Add 'schemes' value to fmf files, for games to list (multiple) uri schemes with which to easily start the engine and connect to a specified server. Also add cl_verify_urischeme, a bit like ezquake has.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6021 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
f0aea54e5d
commit
cf31dcccad
12 changed files with 488 additions and 147 deletions
|
@ -123,6 +123,7 @@ cvar_t cl_demospeed = CVARF("cl_demospeed", "1", 0);
|
|||
cvar_t cl_demoreel = CVARFD("cl_demoreel", "0", CVAR_SAVE, "When enabled, the engine will begin playing a demo loop on startup.");
|
||||
|
||||
cvar_t cl_loopbackprotocol = CVARD("cl_loopbackprotocol", "qw", "Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods.");
|
||||
static cvar_t cl_verify_urischeme = CVARAFD("cl_verify_urischeme", "0", "cl_verify_qwprotocol"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, "0: Do nothing.\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\n2: Always re-register on every startup, without prompting. Sledgehammer style.");
|
||||
|
||||
|
||||
cvar_t cl_threadedphysics = CVARD("cl_threadedphysics", "0", "When set, client input frames are generated and sent on a worker thread");
|
||||
|
@ -5006,6 +5007,8 @@ void CL_Init (void)
|
|||
#ifndef SERVERONLY
|
||||
Cvar_Register (&cl_loopbackprotocol, cl_controlgroup);
|
||||
#endif
|
||||
Cvar_Register (&cl_verify_urischeme, cl_controlgroup);
|
||||
|
||||
Cvar_Register (&cl_countpendingpl, cl_controlgroup);
|
||||
Cvar_Register (&cl_threadedphysics, cl_controlgroup);
|
||||
hud_tracking_show = Cvar_Get("hud_tracking_show", "1", 0, "statusbar");
|
||||
|
@ -6003,18 +6006,62 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
|||
else
|
||||
#endif
|
||||
{
|
||||
if (nlen >= 5 && !strncmp(fname, "qw://", 5))
|
||||
const char *schemeend = strstr(fname, "://");
|
||||
|
||||
if (schemeend)
|
||||
{ //this is also implemented by ezquake, so be careful here...
|
||||
//"qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
|
||||
//examples:
|
||||
// "quake2://broker:port"
|
||||
// "quake2:rtc://broker:port/game"
|
||||
// "qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
|
||||
//we'll chop off any non-auth prefix, its just so we can handle multiple protocols via a single uri scheme.
|
||||
char *t, *cmd;
|
||||
const char *url;
|
||||
char buffer[8192];
|
||||
t = Z_Malloc(nlen+1);
|
||||
memcpy(t, fname, nlen);
|
||||
t[nlen] = 0;
|
||||
url = t+5;
|
||||
const char *schemestart = strchr(fname, ':');
|
||||
int schemelen, urilen;
|
||||
|
||||
for (cmd = t+5; *cmd; cmd++)
|
||||
//if its one of our explicit protocols then use the url as-is
|
||||
const char *netschemes[] = {"udp", "udp4", "udp6", "ipx", "tcp", "tcp4", "tcp6", "spx", "ws", "wss", "tls", "dtls", "ice", "rtc", "ices", "rtcs", "irc", "udg", "unix"};
|
||||
int i;
|
||||
size_t slen;
|
||||
|
||||
if (!schemestart || schemestart==schemeend)
|
||||
schemestart = fname;
|
||||
else
|
||||
schemestart++;
|
||||
schemelen = schemeend-schemestart;
|
||||
urilen = nlen-(schemestart-fname);
|
||||
|
||||
for (i = 0; i < countof(netschemes); i++)
|
||||
{
|
||||
slen = strlen(netschemes[i]);
|
||||
if (schemelen == slen && !strncmp(schemestart, netschemes[i], slen))
|
||||
{
|
||||
char quoted[8192];
|
||||
char *t = Z_Malloc(urilen+1);
|
||||
memcpy(t, schemestart, urilen);
|
||||
t[urilen] = 0;
|
||||
|
||||
Cbuf_AddText(va("connect %s\n", COM_QuotedString(t, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);
|
||||
|
||||
if(file)
|
||||
VFS_CLOSE(file);
|
||||
Z_Free(t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
schemelen++;
|
||||
if (!strncmp(schemestart+schemelen, "//", schemelen))
|
||||
schemelen+=2;
|
||||
|
||||
t = Z_Malloc(urilen+1);
|
||||
memcpy(t, schemestart, urilen);
|
||||
t[urilen] = 0;
|
||||
url = t+schemelen;
|
||||
|
||||
for (cmd = t+schemelen; *cmd; cmd++)
|
||||
{
|
||||
if (*cmd == '/')
|
||||
{
|
||||
|
@ -6043,25 +6090,6 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
|
|||
Z_Free(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
const char *netschemes[] = {"udp://", "udp4//", "udp6//", "ipx://", "tcp://", "tcp4//", "tcp6//", "spx://", "ws://", "wss://", "tls://", "dtls://", "ice://", "rtc://", "ices://", "rtcs://", "irc://", "udg://", "unix://"};
|
||||
int i;
|
||||
size_t slen;
|
||||
for (i = 0; i < countof(netschemes); i++)
|
||||
{
|
||||
slen = strlen(netschemes[i]);
|
||||
if (nlen >= slen && !strncmp(fname, netschemes[i], slen))
|
||||
{
|
||||
char quoted[8192];
|
||||
char *t = Z_Malloc(nlen+1);
|
||||
memcpy(t, fname, nlen);
|
||||
t[nlen] = 0;
|
||||
Cbuf_AddText(va("connect %s\n", COM_QuotedString(t, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);
|
||||
Z_Free(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f = Z_Malloc(sizeof(*f) + nlen);
|
||||
|
@ -6762,7 +6790,11 @@ void CL_ExecInitialConfigs(char *resetcommand)
|
|||
Ruleset_Scan();
|
||||
}
|
||||
|
||||
|
||||
static void Host_URIPrompt(void *ctx, promptbutton_t btn)
|
||||
{
|
||||
if (btn == PROMPT_YES)
|
||||
Cbuf_AddText ("\nsys_register_file_associations\n", RESTRICT_LOCAL);
|
||||
}
|
||||
|
||||
void Host_FinishLoading(void)
|
||||
{
|
||||
|
@ -6846,6 +6878,22 @@ void Host_FinishLoading(void)
|
|||
}
|
||||
else //3 flags for a renderer restart
|
||||
Renderer_Start();
|
||||
|
||||
|
||||
if (fs_manifest->schemes && Cmd_IsCommand("sys_register_file_associations"))
|
||||
{
|
||||
if (cl_verify_urischeme.ival >= 2)
|
||||
Cbuf_AddText ("\nsys_register_file_associations\n", RESTRICT_LOCAL);
|
||||
else if (cl_verify_urischeme.ival)
|
||||
{
|
||||
char *scheme = Sys_URIScheme_NeedsRegistering();
|
||||
if (scheme)
|
||||
{
|
||||
Menu_Prompt(Host_URIPrompt, NULL, va("The URI scheme %s:// is not configured.\nRegister now?", scheme), "Register", NULL, "No");
|
||||
Z_Free(scheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1018,6 +1018,10 @@ qboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)
|
|||
void Sys_SendKeyEvents(void)
|
||||
{
|
||||
}
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{ //android does its mime/etc registrations via android xml junk. dynamically registering stuff isn't supported, so pretend that its already registered to avoid annoying prompts.
|
||||
return NULL;
|
||||
}
|
||||
void Sys_Init(void)
|
||||
{
|
||||
Cvar_Register(&sys_keepscreenon, "android stuff");
|
||||
|
|
|
@ -289,11 +289,85 @@ void Sys_Quit (void)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{
|
||||
qboolean found;
|
||||
qofs_t insize;
|
||||
char *in;
|
||||
char confbase[MAX_OSPATH];
|
||||
|
||||
char scheme[64];
|
||||
const char *schemes = fs_manifest->schemes;
|
||||
|
||||
schemes=COM_ParseOut(schemes, scheme, sizeof(scheme));
|
||||
if (!schemes)
|
||||
return NULL;
|
||||
|
||||
{ //user
|
||||
const char *config = getenv("XDG_CONFIG_HOME");
|
||||
const char *home = getenv("HOME");
|
||||
if (config && *config)
|
||||
Q_strncpyz(confbase, config, sizeof(confbase));
|
||||
else
|
||||
{
|
||||
if (home && *home)
|
||||
Q_snprintfz(confbase, sizeof(confbase), "%s/.config", home);
|
||||
else
|
||||
return NULL; //can't register anyway, just pretend its registered.
|
||||
}
|
||||
}
|
||||
|
||||
//check if the scheme is registered or not.
|
||||
in = FS_MallocFile(va("%s/mimeapps.list", confbase), FS_SYSTEM, &insize);
|
||||
if (in)
|
||||
{
|
||||
do
|
||||
{
|
||||
qboolean inadded = false;
|
||||
char *l = in;
|
||||
const char *schemeline = va("x-scheme-handler/%s=fte-%s.desktop;", scheme, fs_manifest->installation);
|
||||
size_t schemelinelen = strlen(schemeline);
|
||||
found = false;
|
||||
while(*l)
|
||||
{
|
||||
char *le;
|
||||
while(*l == ' ' || *l == '\n')
|
||||
l++;
|
||||
le = strchr(l, '\n');
|
||||
if (le)
|
||||
le = le+1;
|
||||
else
|
||||
le = l + strlen(l);
|
||||
if (!strncmp(l, "[Added Associations]", 20))
|
||||
inadded = true;
|
||||
else if (!strncmp(l, "[", 1))
|
||||
inadded = false;
|
||||
else if (inadded && !strncmp(l, schemeline, schemelinelen))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
l = le;
|
||||
}
|
||||
} while(found && (schemes=COM_ParseOut(schemes, scheme, sizeof(scheme))));
|
||||
Z_Free(in);
|
||||
}
|
||||
else
|
||||
found = false;
|
||||
|
||||
if (found)
|
||||
return NULL;
|
||||
return Z_StrDup(scheme);
|
||||
}
|
||||
static void Sys_Register_File_Associations_f(void)
|
||||
{
|
||||
const char *s;
|
||||
char xdgbase[MAX_OSPATH];
|
||||
char confbase[MAX_OSPATH];
|
||||
|
||||
char scheme[MAX_OSPATH];
|
||||
const char *schemes = fs_manifest->schemes;
|
||||
|
||||
if (1)
|
||||
{ //user
|
||||
const char *data = getenv("XDG_DATA_HOME");
|
||||
|
@ -357,41 +431,66 @@ static void Sys_Register_File_Associations_f(void)
|
|||
|
||||
//we need to create some .desktop file first, so stuff knows how to start us up.
|
||||
{
|
||||
vfsfile_t *f;
|
||||
char iconsyspath[MAX_OSPATH];
|
||||
char *exe = realpath(host_parms.argv[0], NULL);
|
||||
char *basedir = realpath(com_gamepath, NULL);
|
||||
const char *iconname = fs_manifest->installation;
|
||||
const char *desktopfile =
|
||||
"[Desktop Entry]\n"
|
||||
"Type=Application\n"
|
||||
"Encoding=UTF-8\n"
|
||||
"Name=%s\n"
|
||||
"Comment=Awesome First Person Shooter\n" //again should be a manicfest item
|
||||
"Exec=\"%s\" %%u\n" //FIXME: FS_GetManifestArgs! etc!
|
||||
"Path=%s\n"
|
||||
"Icon=%s\n"
|
||||
"Terminal=false\n"
|
||||
"Categories=Game;\n"
|
||||
"MimeType=" "application/x-quakeworlddemo;" "x-scheme-handler/qw;\n"
|
||||
;
|
||||
|
||||
if (!exe)
|
||||
{
|
||||
int i;
|
||||
if (strchr(host_parms.argv[0], '/') && (i=readlink("/proc/self/exe", iconsyspath, sizeof(iconsyspath)-1))>0)
|
||||
{ //if they used a relative path to invoke the binary, replace it with an absolute one.
|
||||
iconsyspath[i] = 0;
|
||||
exe = strdup(iconsyspath);
|
||||
}
|
||||
else //no absolute path. assume it was loaded from the (default) path.
|
||||
exe = strdup(host_parms.argv[0]);
|
||||
}
|
||||
|
||||
if (!strcmp(iconname, "afterquake") || !strcmp(iconname, "nq")) //hacks so that we don't need to create icons.
|
||||
iconname = "quake";
|
||||
|
||||
if (FS_NativePath("icon.png", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath)))
|
||||
iconname = iconsyspath;
|
||||
|
||||
desktopfile = va(desktopfile,
|
||||
s = va("%s/applications/fte-%s.desktop", xdgbase, fs_manifest->installation);
|
||||
FS_CreatePath(s, FS_SYSTEM);
|
||||
f = FS_OpenVFS(s, "wb", FS_SYSTEM);
|
||||
if (f)
|
||||
{
|
||||
VFS_PRINTF(f,
|
||||
"[Desktop Entry]\n"
|
||||
"Type=Application\n"
|
||||
"Encoding=UTF-8\n"
|
||||
"Name=%s\n"
|
||||
"Comment=Awesome First Person Shooter\n" //again should be a manicfest item
|
||||
"Exec=\"%s\" %%u %s\n"
|
||||
"Path=%s\n"
|
||||
"Icon=%s\n"
|
||||
"Terminal=false\n"
|
||||
"Categories=Game;\n"
|
||||
"MimeType=" "application/x-quakeworlddemo;",
|
||||
fs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation,
|
||||
exe, basedir, iconname);
|
||||
exe, FS_GetManifestArgs(), basedir, iconname);
|
||||
|
||||
for (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)
|
||||
VFS_PRINTF(f, "x-scheme-handler/%s;", scheme);
|
||||
VFS_PRINTF(f, "\n");
|
||||
|
||||
VFS_CLOSE(f);
|
||||
}
|
||||
|
||||
free(exe);
|
||||
free(basedir);
|
||||
FS_WriteFile(va("%s/applications/fte-%s.desktop", xdgbase, fs_manifest->installation), desktopfile, strlen(desktopfile), FS_SYSTEM);
|
||||
|
||||
//FIXME: read icon.png and write it to ~/.local/share/icons/hicolor/WxH/apps/foo.png
|
||||
}
|
||||
|
||||
//we need to set some default applications.
|
||||
//write out a new file and rename the new over the top of the old
|
||||
for (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)
|
||||
{
|
||||
char *foundassoc = NULL;
|
||||
vfsfile_t *out = FS_OpenVFS(va("%s/.mimeapps.list.new", confbase), "wb", FS_SYSTEM);
|
||||
|
@ -403,6 +502,8 @@ static void Sys_Register_File_Associations_f(void)
|
|||
{
|
||||
qboolean inadded = false;
|
||||
char *l = in;
|
||||
const char *schemeline = va("x-scheme-handler/%s=", scheme);
|
||||
size_t schemelinelen = strlen(schemeline);
|
||||
while(*l)
|
||||
{
|
||||
char *le;
|
||||
|
@ -421,10 +522,10 @@ static void Sys_Register_File_Associations_f(void)
|
|||
}
|
||||
else if (!strncmp(l, "[", 1))
|
||||
inadded = false;
|
||||
else if (inadded && !strncmp(l, "x-scheme-handler/qw=", 20))
|
||||
else if (inadded && !strncmp(l, schemeline, schemelinelen))
|
||||
{
|
||||
foundassoc = l;
|
||||
insize -= strlen(le);
|
||||
insize -= le-l;
|
||||
memmove(l, le, strlen(le)); //remove the line
|
||||
}
|
||||
l = le;
|
||||
|
@ -432,7 +533,7 @@ static void Sys_Register_File_Associations_f(void)
|
|||
if (foundassoc)
|
||||
{ //if we found it, or somewhere to insert it, then insert it.
|
||||
VFS_WRITE(out, in, foundassoc-in);
|
||||
VFS_PRINTF(out, "x-scheme-handler/qw=fte-%s.desktop;\n", fs_manifest->installation);
|
||||
VFS_PRINTF(out, "x-scheme-handler/%s=fte-%s.desktop;\n", scheme, fs_manifest->installation);
|
||||
VFS_WRITE(out, foundassoc, insize - (foundassoc-in));
|
||||
}
|
||||
else
|
||||
|
@ -442,7 +543,7 @@ static void Sys_Register_File_Associations_f(void)
|
|||
if (!foundassoc)
|
||||
{ //if file not found, or no appropriate section, just concat it on the end.
|
||||
VFS_PRINTF(out, "[Added Associations]\n");
|
||||
VFS_PRINTF(out, "x-scheme-handler/qw=fte-%s.desktop;\n", fs_manifest->installation);
|
||||
VFS_PRINTF(out, "x-scheme-handler/%s=fte-%s.desktop;\n", scheme, fs_manifest->installation);
|
||||
}
|
||||
VFS_FLUSH(out);
|
||||
VFS_CLOSE(out);
|
||||
|
|
|
@ -371,6 +371,10 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{ //no support, report something that'll disable annoying prompts.
|
||||
return NULL;
|
||||
}
|
||||
void Sys_Init()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -725,6 +725,11 @@ int Sys_FileTime (char *path)
|
|||
return -1;
|
||||
}
|
||||
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{ //no support.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Sys_Init(void)
|
||||
{
|
||||
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
|
||||
|
|
|
@ -1511,10 +1511,13 @@ void Sys_MakeCodeWriteable (void *startaddr, unsigned long length)
|
|||
}
|
||||
#endif
|
||||
|
||||
void Sys_DoFileAssociations(int elevated);
|
||||
void Sys_DoFileAssociations(int elevated, const char *scheme);
|
||||
void Sys_Register_File_Associations_f(void)
|
||||
{
|
||||
Sys_DoFileAssociations(0);
|
||||
if (!Q_strcasecmp(Cmd_Argv(1), "quiet"))
|
||||
Sys_DoFileAssociations(2, fs_manifest->schemes); //current user only.
|
||||
else
|
||||
Sys_DoFileAssociations(0, fs_manifest->schemes); //user+machine(with elevation on failure)
|
||||
}
|
||||
|
||||
static void QDECL Sys_Priority_Changed(cvar_t *var, char *oldval)
|
||||
|
@ -1559,7 +1562,7 @@ void Sys_Init (void)
|
|||
#ifndef SERVERONLY
|
||||
Cvar_Register(&sys_disableWinKeys, "System vars");
|
||||
Cvar_Register(&sys_disableTaskSwitch, "System vars");
|
||||
Cmd_AddCommandD("sys_register_file_associations", Sys_Register_File_Associations_f, "Register FTE as the system handler for .bsp .mvd .qwd .dem files. Also register the qw:// URL protocol. This command will probably trigger a UAC prompt in Windows Vista and up. Deny it for current-user-only asociations (will also prevent listing in windows' 'default programs' ui due to microsoft bugs/limitations).");
|
||||
Cmd_AddCommandD("sys_register_file_associations", Sys_Register_File_Associations_f, "Register FTE as the system handler for .bsp .mvd .qwd .dem files. Also register the URL protocol. This command will probably trigger a UAC prompt in Windows Vista and up. Deny it for current-user-only asociations (will also prevent listing in windows' 'default programs' ui due to microsoft bugs/limitations).");
|
||||
|
||||
#ifdef QUAKESPYAPI
|
||||
#ifndef CLIENTONLY
|
||||
|
@ -2884,6 +2887,47 @@ void Win7_TaskListInit(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
//using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info
|
||||
//no, I don't really have a clue why it needs to be so long.
|
||||
//#include <svrapi.h>
|
||||
#ifndef ACCESS_READ
|
||||
#define ACCESS_READ 0x1
|
||||
#define ACCESS_WRITE 0x2
|
||||
#endif
|
||||
static BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired)
|
||||
{
|
||||
HANDLE hToken;
|
||||
PRIVILEGE_SET PrivilegeSet;
|
||||
DWORD dwPrivSetSize;
|
||||
DWORD dwAccessGranted;
|
||||
BOOL fAccessGranted = FALSE;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
SECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION);
|
||||
PSECURITY_DESCRIPTOR psdSD = NULL;
|
||||
DWORD dwNeeded;
|
||||
GetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded);
|
||||
psdSD = malloc(dwNeeded);
|
||||
GetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded);
|
||||
ImpersonateSelf(SecurityImpersonation);
|
||||
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
|
||||
memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));
|
||||
GenericMapping.GenericRead = ACCESS_READ;
|
||||
GenericMapping.GenericWrite = ACCESS_WRITE;
|
||||
GenericMapping.GenericExecute = 0;
|
||||
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
||||
MapGenericMask(&dwAccessDesired, &GenericMapping);
|
||||
dwPrivSetSize = sizeof(PRIVILEGE_SET);
|
||||
AccessCheck(psdSD, hToken, dwAccessDesired, &GenericMapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessGranted, &fAccessGranted);
|
||||
free(psdSD);
|
||||
return fAccessGranted;
|
||||
}
|
||||
static BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired)
|
||||
{
|
||||
wchar_t wpath[MAX_OSPATH];
|
||||
return microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SVNREVISION
|
||||
#if 0 //1 to debug engine update in msvc.
|
||||
#define SVNREVISION 1
|
||||
|
@ -3085,10 +3129,68 @@ typedef struct qIApplicationAssociationRegistrationUI
|
|||
} *lpVtbl;
|
||||
} qIApplicationAssociationRegistrationUI;
|
||||
|
||||
void Sys_DoFileAssociations(int elevated)
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{ //just disables the prompts.
|
||||
HKEY root;
|
||||
char buffer[2048];
|
||||
char scheme[64];
|
||||
const char *s, *schemes = fs_manifest->schemes;
|
||||
char *exec, *me;
|
||||
size_t i;
|
||||
wchar_t enginebinaryw[MAX_OSPATH];
|
||||
char enginebinary[MAX_OSPATH*4];
|
||||
|
||||
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||
{
|
||||
root = HKEY_CURRENT_USER;
|
||||
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s", scheme), "", buffer, sizeof(buffer)))
|
||||
{
|
||||
root = HKEY_LOCAL_MACHINE;
|
||||
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s", scheme), "", buffer, sizeof(buffer)))
|
||||
break;
|
||||
}
|
||||
|
||||
//the scheme exists at least...
|
||||
if (!MyRegGetStringValue(root, va("Software\\Classes\\%s\\shell\\open\\command", scheme), "", buffer, sizeof(buffer)))
|
||||
break; //erk, missing.
|
||||
COM_Parse(buffer);
|
||||
if (!microsoft_accessU(com_token, ACCESS_READ))
|
||||
break; //can't read it? doesn't exist?
|
||||
exec = COM_SkipPath(com_token);
|
||||
for (i = 0; exec[i]; i++)
|
||||
if (exec[i] == '_' || exec[i] == '.' || (exec[i] >= '0' && exec[i] <= '9'))
|
||||
{ //anything that looks like a revision number
|
||||
exec[i] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
GetModuleFileNameW(NULL, enginebinaryw, countof(enginebinaryw)-1);
|
||||
narrowen(enginebinary, sizeof(enginebinary), enginebinaryw);
|
||||
me = COM_SkipPath(enginebinary);
|
||||
for (i = 0; me[i]; i++)
|
||||
if (me[i] == '_' || me[i] == '.' || (me[i] >= '0' && me[i] <= '9'))
|
||||
{ //anything that looks like a revision number
|
||||
me[i] = 0;
|
||||
break;
|
||||
}
|
||||
if (Q_strcasecmp(exec, me))
|
||||
break; //looks like its set to something else.
|
||||
}
|
||||
|
||||
if (s)
|
||||
return Z_StrDup(scheme);
|
||||
return NULL;
|
||||
}
|
||||
void Sys_DoFileAssociations(int elevated, const char *schemes)
|
||||
{
|
||||
//elevated:
|
||||
// 0: console command
|
||||
// 1: running as an elevated/admin process
|
||||
// 2: register as current user only (do not show associations prompt).
|
||||
char command[1024];
|
||||
qboolean ok = true;
|
||||
char scheme[64];
|
||||
const char *s;
|
||||
qboolean ok = true;
|
||||
HKEY root;
|
||||
|
||||
//I'd do everything in current_user if I could, but windows sucks too much for that.
|
||||
|
@ -3101,12 +3203,13 @@ void Sys_DoFileAssociations(int elevated)
|
|||
//on xp, we use ONLY current user. no 'registered applications' means no 'registered applications bug', which means no need to use hklm at all.
|
||||
//in vista/7, we have to create stuff in local_machine. in which case we might as well put ALL associations in there. the ui stuff will allow user-specific settings, so this is not an issue other than the fact that it triggers uac.
|
||||
//in 8, we cannot programatically force ownership of our associations, so we might as well just use the ui method even for vista+7 instead of the ruder version.
|
||||
//in win10, the 'ui' stuff is just a quick popup to tell the user to configure defaults themselves. hopefully we can fall back on the regular associations for when the user didn't override anyting.
|
||||
if (qwinvermaj < 6)
|
||||
elevated = 2;
|
||||
|
||||
root = elevated == 2?HKEY_CURRENT_USER:HKEY_LOCAL_MACHINE;
|
||||
root = (elevated>=2)?HKEY_CURRENT_USER:HKEY_LOCAL_MACHINE;
|
||||
|
||||
#define ASSOC_VERSION 2
|
||||
// #define ASSOC_VERSION 2
|
||||
#define ASSOCV "1"
|
||||
|
||||
//register the basic demo class
|
||||
|
@ -3116,6 +3219,13 @@ void Sys_DoFileAssociations(int elevated)
|
|||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_DemoFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||
if (ok)
|
||||
{ //and now the extensions themselves...
|
||||
MyRegSetValue(root, "Software\\Classes\\.qtv", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||
MyRegSetValue(root, "Software\\Classes\\.mvd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||
MyRegSetValue(root, "Software\\Classes\\.qwd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||
MyRegSetValue(root, "Software\\Classes\\.dem", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile."ASSOCV));
|
||||
}
|
||||
|
||||
//register the basic map class. yeah, the command is the same as for demos. but the description is different!
|
||||
Q_snprintfz(command, sizeof(command), "Quake Map");
|
||||
|
@ -3124,8 +3234,13 @@ void Sys_DoFileAssociations(int elevated)
|
|||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_BSPFile."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||
if (ok)
|
||||
{ //and now the extensions themselves...
|
||||
MyRegSetValue(root, "Software\\Classes\\.bsp", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile."ASSOCV));
|
||||
MyRegSetValue(root, "Software\\Classes\\.map", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile."ASSOCV));
|
||||
}
|
||||
|
||||
//register the basic protocol class
|
||||
//register the basic uri scheme class
|
||||
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "", REG_SZ, command, strlen(command));
|
||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"", "URL Protocol", REG_SZ, "", strlen(""));
|
||||
|
@ -3133,8 +3248,22 @@ void Sys_DoFileAssociations(int elevated)
|
|||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||
ok = ok & MyRegSetValue(root, "Software\\Classes\\"DISTRIBUTION"_Server."ASSOCV"\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||
if (ok)
|
||||
{ //and now the schemes themselves... (doesn't really use the same scheme stuff)
|
||||
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||
{
|
||||
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
||||
MyRegSetValue(root, va("Software\\Classes\\%s", scheme), "", REG_SZ, command, strlen(command));
|
||||
MyRegSetValue(root, va("Software\\Classes\\%s", scheme), "URL Protocol", REG_SZ, "", strlen(""));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\",0", com_argv[0]);
|
||||
MyRegSetValue(root, va("Software\\Classes\\%s\\DefaultIcon", scheme), "", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||
MyRegSetValue(root, va("Software\\Classes\\%s\\shell\\open\\command", scheme), "", REG_SZ, command, strlen(command));
|
||||
}
|
||||
}
|
||||
|
||||
//try to get ourselves listed in windows' 'default programs' ui.
|
||||
|
||||
//now try to get ourselves listed in windows' 'default programs' ui.
|
||||
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME);
|
||||
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities", "ApplicationName", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "%s", FULLENGINENAME" is an awesome hybrid game engine able to run multiple Quake-compatible/derived games.");
|
||||
|
@ -3154,53 +3283,34 @@ void Sys_DoFileAssociations(int elevated)
|
|||
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\FileAssociations", ".fmf", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
||||
// ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\MIMEAssociations", "application/x-ftemanifest", REG_SZ, DISTRIBUTION"_ManifestFile", strlen(DISTRIBUTION"_ManifestFile"));
|
||||
|
||||
Q_snprintfz(command, sizeof(command), DISTRIBUTION"_Server.1");
|
||||
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME"\\Capabilities\\UrlAssociations", "qw", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), DISTRIBUTION"_Server."ASSOCV);
|
||||
for (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )
|
||||
{
|
||||
ok = ok & MyRegSetValue(root, "Software\\"FULLENGINENAME "\\Capabilities\\UrlAssociations", scheme, REG_SZ, command, strlen(command));
|
||||
}
|
||||
|
||||
Q_snprintfz(command, sizeof(command), "Software\\"FULLENGINENAME"\\Capabilities");
|
||||
ok = ok & MyRegSetValue(root, "Software\\RegisteredApplications", FULLENGINENAME, REG_SZ, command, strlen(command));
|
||||
|
||||
//also try to add it to current user.
|
||||
if (root==HKEY_LOCAL_MACHINE)
|
||||
Sys_DoFileAssociations(2, schemes);
|
||||
|
||||
//let the shell know that file associations changed (otherwise we might have to wait for a reboot)
|
||||
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||||
|
||||
if (!ok && elevated < 2)
|
||||
if (!ok && root==HKEY_LOCAL_MACHINE)
|
||||
{
|
||||
HINSTANCE ch = ShellExecute(mainwindow, "runas", com_argv[0], va("-register_types %i", elevated+1), NULL, SW_SHOWNORMAL);
|
||||
if ((qintptr_t)ch <= 32)
|
||||
Sys_DoFileAssociations(2);
|
||||
ShellExecute(mainwindow, "runas", com_argv[0], va("-register_types \"%s\"", schemes), NULL, SW_SHOWNORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
if (ok && root==HKEY_LOCAL_MACHINE)
|
||||
{
|
||||
// char buf[1];
|
||||
//attempt to display the vista+ prompt (only way possible in win8, apparently)
|
||||
//note that in win10 this will supposedly just show a notification popup with the user required to configure it manually via control panel.
|
||||
qIApplicationAssociationRegistrationUI *aarui = NULL;
|
||||
|
||||
//needs to be done anyway to ensure that its listed, and so that we get the association if nothing else has it.
|
||||
//however, the popup for when you start new programs is very annoying, so lets try to avoid that. our file associations are somewhat explicit anyway.
|
||||
//note that you'll probably still get the clumsy prompt if you try to run fte as a different user. really depends if you gave it local machine write access.
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.qtv", "", buf, sizeof(buf)))
|
||||
MyRegSetValue(root, "Software\\Classes\\.qtv", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.mvd", "", buf, sizeof(buf)))
|
||||
MyRegSetValue(root, "Software\\Classes\\.mvd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.qwd", "", buf, sizeof(buf)))
|
||||
MyRegSetValue(root, "Software\\Classes\\.qwd", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.dem", "", buf, sizeof(buf)))
|
||||
MyRegSetValue(root, "Software\\Classes\\.dem", "", REG_SZ, DISTRIBUTION"_DemoFile."ASSOCV, strlen(DISTRIBUTION"_DemoFile.1"));
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(root, "Software\\Classes\\.bsp", "", buf, sizeof(buf)))
|
||||
MyRegSetValue(root, "Software\\Classes\\.bsp", "", REG_SZ, DISTRIBUTION"_BSPFile."ASSOCV, strlen(DISTRIBUTION"_BSPFile.1"));
|
||||
//legacy url associations are a bit more explicit
|
||||
// if (!aarui || elevated==2 || !MyRegGetStringValue(HKEY_CURRENT_USER, "Software\\Classes\\qw", "", buf, sizeof(buf)))
|
||||
{
|
||||
Q_snprintfz(command, sizeof(command), "QuakeWorld Server");
|
||||
MyRegSetValue(root, "Software\\Classes\\qw", "", REG_SZ, command, strlen(command));
|
||||
MyRegSetValue(root, "Software\\Classes\\qw", "URL Protocol", REG_SZ, "", strlen(""));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\",0", com_argv[0]);
|
||||
MyRegSetValue(root, "Software\\Classes\\qw\\DefaultIcon", "", REG_SZ, command, strlen(command));
|
||||
Q_snprintfz(command, sizeof(command), "\"%s\" \"%%1\"", com_argv[0]);
|
||||
MyRegSetValue(root, "Software\\Classes\\qw\\shell\\open\\command", "", REG_SZ, command, strlen(command));
|
||||
}
|
||||
|
||||
CoInitialize(NULL);
|
||||
if (FAILED(CoCreateInstance(&qCLSID_ApplicationAssociationRegistrationUI, 0, CLSCTX_INPROC_SERVER, &qIID_IApplicationAssociationRegistrationUI, (LPVOID*)&aarui)))
|
||||
aarui = NULL;
|
||||
|
@ -3308,46 +3418,6 @@ int MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType)
|
|||
}
|
||||
|
||||
#ifdef WEBCLIENT
|
||||
//using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info
|
||||
//no, I don't really have a clue why it needs to be so long.
|
||||
//#include <svrapi.h>
|
||||
#ifndef ACCESS_READ
|
||||
#define ACCESS_READ 0x1
|
||||
#define ACCESS_WRITE 0x2
|
||||
#endif
|
||||
static BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired)
|
||||
{
|
||||
HANDLE hToken;
|
||||
PRIVILEGE_SET PrivilegeSet;
|
||||
DWORD dwPrivSetSize;
|
||||
DWORD dwAccessGranted;
|
||||
BOOL fAccessGranted = FALSE;
|
||||
GENERIC_MAPPING GenericMapping;
|
||||
SECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION);
|
||||
PSECURITY_DESCRIPTOR psdSD = NULL;
|
||||
DWORD dwNeeded;
|
||||
GetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded);
|
||||
psdSD = malloc(dwNeeded);
|
||||
GetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded);
|
||||
ImpersonateSelf(SecurityImpersonation);
|
||||
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
|
||||
memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));
|
||||
GenericMapping.GenericRead = ACCESS_READ;
|
||||
GenericMapping.GenericWrite = ACCESS_WRITE;
|
||||
GenericMapping.GenericExecute = 0;
|
||||
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
||||
MapGenericMask(&dwAccessDesired, &GenericMapping);
|
||||
dwPrivSetSize = sizeof(PRIVILEGE_SET);
|
||||
AccessCheck(psdSD, hToken, dwAccessDesired, &GenericMapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessGranted, &fAccessGranted);
|
||||
free(psdSD);
|
||||
return fAccessGranted;
|
||||
}
|
||||
static BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired)
|
||||
{
|
||||
wchar_t wpath[MAX_OSPATH];
|
||||
return microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired);
|
||||
}
|
||||
|
||||
#ifndef GWLP_WNDPROC
|
||||
#define GWLP_WNDPROC GWL_WNDPROC
|
||||
#define SetWindowLongPtr SetWindowLong
|
||||
|
@ -4112,9 +4182,10 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
|||
isPlugin = 0;
|
||||
}
|
||||
|
||||
if (COM_CheckParm("-register_types"))
|
||||
c = COM_CheckParm("-register_types");
|
||||
if (c)
|
||||
{
|
||||
Sys_DoFileAssociations(1);
|
||||
Sys_DoFileAssociations(1, (c+1 < com_argc)?com_argv[c+1]:NULL);
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -7746,7 +7746,7 @@ void Info_SetValueForKey (char *s, const char *key, const char *value, int maxsi
|
|||
Info_SetValueForStarKey (s, key, value, maxsize);
|
||||
}
|
||||
|
||||
static void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))
|
||||
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))
|
||||
{
|
||||
char key[1024];
|
||||
char value[1024];
|
||||
|
|
|
@ -764,6 +764,8 @@ typedef struct
|
|||
char *rtcbroker; //the broker to use for webrtc connections.
|
||||
char *basedir; //this is where we expect to find the data.
|
||||
char *iconname; //path we can find the icon (relative to the fmf's location)
|
||||
|
||||
char *schemes; //protocol scheme used to connect to a server running this game, use com_parse.
|
||||
struct
|
||||
{
|
||||
enum
|
||||
|
@ -854,9 +856,9 @@ void Info_RemovePrefixedKeys (char *start, char prefix);
|
|||
void Info_RemoveKey (char *s, const char *key);
|
||||
char *Info_KeyForNumber (const char *s, int num);
|
||||
void Info_Print (const char *s, const char *lineprefix);
|
||||
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
|
||||
/*
|
||||
void Info_RemoveNonStarKeys (char *start);
|
||||
void Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
|
||||
void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags);
|
||||
*/
|
||||
|
||||
|
|
|
@ -219,6 +219,7 @@ void FS_Manifest_Free(ftemanifest_t *man)
|
|||
Z_Free(man->downloadsurl);
|
||||
Z_Free(man->installupd);
|
||||
#endif
|
||||
Z_Free(man->schemes);
|
||||
Z_Free(man->protocolname);
|
||||
Z_Free(man->eula);
|
||||
Z_Free(man->defaultexec);
|
||||
|
@ -259,6 +260,8 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
|
|||
if (oldm->installupd)
|
||||
newm->installupd = Z_StrDup(oldm->installupd);
|
||||
#endif
|
||||
if (oldm->schemes)
|
||||
newm->schemes = Z_StrDup(oldm->schemes);
|
||||
if (oldm->protocolname)
|
||||
newm->protocolname = Z_StrDup(oldm->protocolname);
|
||||
if (oldm->eula)
|
||||
|
@ -319,6 +322,8 @@ static void FS_Manifest_Print(ftemanifest_t *man)
|
|||
if (man->installupd)
|
||||
Con_Printf("install %s\n", COM_QuotedString(man->installupd, buffer, sizeof(buffer), false));
|
||||
#endif
|
||||
if (man->schemes)
|
||||
Con_Printf("schemes %s\n", COM_QuotedString(man->schemes, buffer, sizeof(buffer), false));
|
||||
if (man->protocolname)
|
||||
Con_Printf("protocolname %s\n", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false));
|
||||
if (man->defaultexec)
|
||||
|
@ -660,6 +665,14 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
|
|||
man->installupd = Z_StrDup(Cmd_Argv(1));
|
||||
}
|
||||
#endif
|
||||
else if (!Q_strcasecmp(cmd, "schemes"))
|
||||
{
|
||||
int i;
|
||||
Z_Free(man->schemes);
|
||||
man->schemes = Z_StrDup(Cmd_Argv(1));
|
||||
for (i = 2; i < Cmd_Argc(); i++)
|
||||
Z_StrCat(&man->schemes, va(" %s", Cmd_Argv(i)));
|
||||
}
|
||||
else if (!Q_strcasecmp(cmd, "protocolname"))
|
||||
{
|
||||
Z_Free(man->protocolname);
|
||||
|
@ -3807,9 +3820,9 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
|
|||
/*quake requires a few settings for compatibility*/
|
||||
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8\n"
|
||||
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
|
||||
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset r_meshpitch -1\nr_sprite_backfacing 1\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
|
||||
#define QCFG "//schemes quake qw\n" "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset r_meshpitch -1\nr_sprite_backfacing 1\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
|
||||
/*NetQuake reconfiguration, to make certain people feel more at home...*/
|
||||
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\ncfg_save_auto 1\n" QCFG "set sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
|
||||
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\n" QCFG "cfg_save_auto 1\nset sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
|
||||
#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n"
|
||||
#define FITZCFG NQCFG "fps_preset builtin_spasm\ncl_sbar 2\nset gl_load24bit 1\n"
|
||||
#define TENEBRAECFG NQCFG "fps_preset builtin_tenebrae\n"
|
||||
|
@ -3823,11 +3836,11 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
|
|||
/*some modern non-compat settings*/
|
||||
#define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n"
|
||||
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
|
||||
#define HEX2CFG "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
|
||||
#define HEX2CFG "//schemes hexen2\n" "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
|
||||
/*yay q2!*/
|
||||
#define Q2CFG "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
|
||||
#define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
|
||||
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
|
||||
#define Q3CFG "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
|
||||
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 8\nset com_parseutf8 0\ngl_overbright 2\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_nogamedirnativecode 0\nsv_port "STRINGIFY(PORT_Q3SERVER)"\n"
|
||||
//#define RMQCFG "sv_bigcoords 1\n"
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
|
@ -5900,6 +5913,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
|
|||
}
|
||||
}
|
||||
|
||||
if (!man->schemes)
|
||||
{
|
||||
Cmd_TokenizeString(va("schemes \"%s\"", gamemode_info[i].argname+1), false, false);
|
||||
FS_Manifest_ParseTokens(man);
|
||||
}
|
||||
|
||||
#ifdef PACKAGEMANAGER
|
||||
if (!man->downloadsurl && gamemode_info[i].downloadsurl)
|
||||
{
|
||||
|
@ -5932,7 +5951,17 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
|
|||
}
|
||||
if (!man->defaultexec && gamemode_info[i].customexec)
|
||||
{
|
||||
man->defaultexec = Z_StrDup(gamemode_info[i].customexec);
|
||||
const char *e = gamemode_info[i].customexec;
|
||||
while (e[0] == '/' && e[1] == '/')
|
||||
{
|
||||
e+=2;
|
||||
while(*e)
|
||||
{
|
||||
if (*e++ == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
man->defaultexec = Z_StrDup(e);
|
||||
}
|
||||
|
||||
builtingame = true;
|
||||
|
|
|
@ -49,6 +49,7 @@ void VARGS Sys_Printf (char *fmt, ...) LIKEPRINTF(1);
|
|||
void Sys_Warn (char *fmt, ...) LIKEPRINTF(1);
|
||||
//like Sys_Printf. dunno why there needs to be two of em.
|
||||
|
||||
char *Sys_URIScheme_NeedsRegistering(void); //returns the name of one of the current manifests uri schemes that isn't registered (but should be registerable).
|
||||
void Sys_Quit (void);
|
||||
void Sys_RecentServer(char *command, char *target, char *title, char *desc);
|
||||
qboolean Sys_RunInstaller(void);
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef struct svm_server_s {
|
|||
bucket_t bucket; //for faster address lookups.
|
||||
struct svm_game_s *game;
|
||||
struct svm_server_s *next;
|
||||
char rules[1024];
|
||||
} svm_server_t;
|
||||
|
||||
typedef struct svm_game_s {
|
||||
|
@ -66,6 +67,7 @@ typedef struct svm_game_s {
|
|||
size_t numservers;
|
||||
qboolean persistent;
|
||||
char *aliases; //list of terminated names, terminated with a double-null
|
||||
char *scheme;
|
||||
char name[1]; //eg: Quake
|
||||
} svm_game_t;
|
||||
|
||||
|
@ -604,6 +606,31 @@ vfsfile_t *SVM_Generate_Gamelist(const char **mimetype, const char *query)
|
|||
*mimetype = "text/html";
|
||||
return f;
|
||||
}
|
||||
struct rulelist_s
|
||||
{
|
||||
unsigned int lines;
|
||||
unsigned int blobofs;
|
||||
char *line[64];
|
||||
char blob[8192];
|
||||
};
|
||||
static void SVM_GatherServerRule(void *ctx, const char *key, const char *val)
|
||||
{
|
||||
struct rulelist_s *rules = ctx;
|
||||
char niceval[256];
|
||||
if (rules->lines == countof(rules->line))
|
||||
return; //overflow
|
||||
QuakeCharsToHTML(niceval, sizeof(niceval), val, false);
|
||||
if (!Q_snprintfz(rules->blob+rules->blobofs, sizeof(rules->blob)-rules->blobofs, "<tr><td>%s</td><td>%s</td></tr>\n", key, niceval))
|
||||
{
|
||||
rules->line[rules->lines++] = rules->blob+rules->blobofs;
|
||||
rules->blobofs += strlen(rules->blob+rules->blobofs)+1;
|
||||
}
|
||||
}
|
||||
static int QDECL SVM_SortServerRule(const void *r1, const void *r2)
|
||||
{
|
||||
return Q_strcasecmp(*(char*const*const)r1, *(char*const*const)r2);
|
||||
}
|
||||
|
||||
vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr, const char *query)
|
||||
{
|
||||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||
|
@ -611,13 +638,12 @@ vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr
|
|||
char hostname[1024];
|
||||
svm_server_t *server;
|
||||
netadr_t adr[64];
|
||||
int count;
|
||||
size_t count, u;
|
||||
const char *url;
|
||||
|
||||
VFS_PRINTF(f, "%s", master_css);
|
||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
||||
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
//FIXME: block dns lookups here?
|
||||
count = NET_StringToAdr2(serveraddr, 0, adr, countof(adr), NULL);
|
||||
while(count-->0)
|
||||
|
@ -625,13 +651,45 @@ vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr
|
|||
server = SVM_GetServer(&adr[count]);
|
||||
if (server)
|
||||
{
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), (server->needpass&1)?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
|
||||
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
if (server->game->scheme && !server->brokerid)
|
||||
url = va("<a href=\"%s://%s\">%s</a>", server->game->scheme, url, url);
|
||||
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", url, (server->needpass&1)?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "<br/>\n");
|
||||
|
||||
if (*server->rules)
|
||||
{
|
||||
struct rulelist_s rules;
|
||||
rules.lines = rules.blobofs = 0;
|
||||
Info_Enumerate(server->rules, &rules, SVM_GatherServerRule);
|
||||
qsort(rules.line, rules.lines, sizeof(rules.line[0]), SVM_SortServerRule);
|
||||
|
||||
//VFS_PRINTF(f, "<table border=0>\n");
|
||||
// VFS_PRINTF(f, "<td></td><td>");
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "</th><th>Rule</th><th>Value</th></tr>\n");
|
||||
for (u = 0; u < rules.lines; u++)
|
||||
VFS_PUTS(f, rules.line[u]);
|
||||
VFS_PRINTF(f, "</table>");
|
||||
// VFS_PRINTF(f, "</td>");
|
||||
//VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "<br/>\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]));
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
}
|
||||
}
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
|
||||
*mimetype = "text/html";
|
||||
return f;
|
||||
|
@ -642,7 +700,7 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
|||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||
char tmpbuf[256];
|
||||
char hostname[1024];
|
||||
const char *url;
|
||||
const char *url, *infourl;
|
||||
svm_game_t *game;
|
||||
svm_server_t *server;
|
||||
unsigned clients=0,bots=0,specs=0;
|
||||
|
@ -684,11 +742,14 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
|||
{
|
||||
url = tmpbuf;
|
||||
Q_snprintfz(tmpbuf, sizeof(tmpbuf), "rtc://%s/%s", masteraddr, server->brokerid);
|
||||
infourl = tmpbuf;
|
||||
}
|
||||
else
|
||||
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
{
|
||||
infourl = url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
}
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s%s%s</td><td>%s</td><td>%s</td><td>%u", url, (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"/server/%s\">%s</a></td><td>%s%s%s</td><td>%s</td><td>%s</td><td>%u", infourl, url, (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
||||
if (server->bots)
|
||||
VFS_PRINTF(f, "+%ub", server->bots);
|
||||
VFS_PRINTF(f, "/%u", server->maxclients);
|
||||
|
@ -1192,6 +1253,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
if (!strcmp(chal, ourchallenge))
|
||||
{
|
||||
|
||||
bots = atoi(Info_ValueForKey(s, "bots"));
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
clients = max(0, clients-bots);
|
||||
|
@ -1204,6 +1266,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
||||
if (srv)
|
||||
{
|
||||
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||
if (developer.ival)
|
||||
Info_Print(s, "\t");
|
||||
if (game)
|
||||
|
@ -1380,6 +1443,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
srv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);
|
||||
if (srv)
|
||||
{
|
||||
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||
if (developer.ival)
|
||||
Info_Print(s, "\t");
|
||||
srv->protover = 3;//atoi(Info_ValueForKey(s, "protocol"));
|
||||
|
@ -1606,6 +1670,13 @@ static qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man)
|
|||
g = COM_Parse(g);
|
||||
game = SVM_FindGame(com_token, 2);
|
||||
#endif
|
||||
if (!game)
|
||||
return false;
|
||||
if (man->schemes && !game->scheme)
|
||||
{
|
||||
COM_Parse(man->schemes);
|
||||
game->scheme = Z_StrDup(com_token);
|
||||
}
|
||||
while (*g)
|
||||
{
|
||||
g = COM_Parse(g);
|
||||
|
|
|
@ -155,6 +155,11 @@ void Sys_BrowserRedirect_f(void)
|
|||
emscriptenfte_window_location(Cmd_Argv(1));
|
||||
}
|
||||
|
||||
char *Sys_URIScheme_NeedsRegistering(void)
|
||||
{ //just disables the prompts that we can't honour anyway.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Sys_Init(void)
|
||||
{
|
||||
extern cvar_t vid_width, vid_height, vid_fullscreen;
|
||||
|
|
Loading…
Reference in a new issue