PACKAGEMANAGER is now a compiletime feature in its own right, and can be enabled separately from WEBCLIENT (although won't be able to download without WEBCLIENT).

SAVEDGAMES is now a new compiletime feature. Deathmatch/dedicated servers can freely disable it.
menuqc now makes sure that any fields it needs are actually present.
developer 1 should now report glsl line numbers a bit more reliably.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5284 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2018-08-04 07:05:20 +00:00
parent 61c0b9f83b
commit 0b2be8f4ba
21 changed files with 330 additions and 164 deletions

View File

@ -3,8 +3,7 @@
//FIXME: block downloads of exe/dll/so/etc if not an https url (even if inside zips). also block such files from package lists over http.
#include "quakedef.h"
#ifdef WEBCLIENT
#define PACKAGEMANAGER
#ifdef PACKAGEMANAGER
#if !defined(NOBUILTINMENUS) && !defined(SERVERONLY)
#define DOWNLOADMENU
#endif
@ -134,7 +133,6 @@ typedef struct package_s {
struct package_s *alternative; //alternative (hidden) forms of this package.
unsigned int trymirrors;
char *mirror[8]; //FIXME: move to two types of dep...
char gamedir[16];
enum fs_relative fsroot;
@ -172,7 +170,10 @@ typedef struct package_s {
char name[1];
} *deps;
#ifdef WEBCLIENT
struct dl_download *download;
unsigned int trymirrors;
#endif
int flags;
int priority;
@ -190,8 +191,12 @@ static int domanifestinstall; //SECURITY_MANIFEST_*
#ifdef PLUGINS
static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded.
#endif
#ifdef WEBCLIENT
static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions)
static qboolean pkg_updating; //when flagged, further changes are blocked until completion.
#else
static const qboolean pkg_updating = false;
#endif
//FIXME: these are allocated for the life of the exe. changing basedir should purge the list.
static int numdownloadablelists = 0;
@ -1152,11 +1157,13 @@ void PM_Shutdown(void)
{
numdownloadablelists--;
#ifdef WEBCLIENT
if (downloadablelist[numdownloadablelists].curdl)
{
DL_Close(downloadablelist[numdownloadablelists].curdl);
downloadablelist[numdownloadablelists].curdl = NULL;
}
#endif
downloadablelist[numdownloadablelists].received = 0;
Z_Free(downloadablelist[numdownloadablelists].url);
downloadablelist[numdownloadablelists].url = NULL;
@ -1232,6 +1239,7 @@ static void PM_UnmarkPackage(package_t *package)
return; //looks like its already deselected.
package->flags &= ~(DPF_MARKED);
#ifdef WEBCLIENT
//Is this safe?
package->trymirrors = 0; //if its enqueued, cancel that quickly...
if (package->download)
@ -1239,6 +1247,7 @@ static void PM_UnmarkPackage(package_t *package)
DL_Close(package->download);
package->download = NULL;
}
#endif
//remove stuff that depends on us
for (o = availablepackages; o; o = o->next)
@ -1530,7 +1539,7 @@ static void PM_PrintChanges(void)
}
static void PM_ApplyChanges(void);
#ifdef WEBCLIENT
static void PM_ListDownloaded(struct dl_download *dl)
{
int i;
@ -1619,6 +1628,7 @@ static void PM_ListDownloaded(struct dl_download *dl)
}
}
}
#endif
//retry 1==
static void PM_UpdatePackageList(qboolean autoupdate, int retry)
{
@ -1633,6 +1643,10 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
if (*pm_downloads_url.string)
PM_AddSubList(pm_downloads_url.string, "", true, true);
#ifndef WEBCLIENT
for (i = 0; i < numdownloadablelists; i++)
downloadablelist[i].received = true;
#else
doautoupdate |= autoupdate;
//kick off the initial tier of list-downloads.
@ -1673,6 +1687,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
PM_PrintChanges();
}
}
#endif
}
@ -1860,6 +1875,7 @@ static void PM_WriteInstalledPackages(void)
}
}
#ifdef WEBCLIENT
//callback from PM_Download_Got, extracts each file from an archive
static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)
{ //this is gonna suck. threading would help, but gah.
@ -2114,6 +2130,7 @@ static char *PM_GetTempName(package_t *p)
Z_Free(ts);
return Z_StrDup(destname);
}
#endif
/*static void PM_AddDownloadedPackage(const char *filename)
{
@ -2143,8 +2160,9 @@ static char *PM_GetTempName(package_t *p)
int PM_IsApplying(qboolean listsonly)
{
package_t *p;
int count = 0;
#ifdef WEBCLIENT
package_t *p;
int i;
if (!listsonly)
{
@ -2159,12 +2177,14 @@ int PM_IsApplying(qboolean listsonly)
if (downloadablelist[i].curdl)
count++;
}
#endif
return count;
}
//looks for the next package that needs downloading, and grabs it
static void PM_StartADownload(void)
{
#ifdef WEBCLIENT
vfsfile_t *tmpfile;
char *temp;
package_t *p;
@ -2284,6 +2304,7 @@ static void PM_StartADownload(void)
//clear the updating flag once there's no more activity needed
pkg_updating = downloading;
#endif
}
//'just' starts doing all the things needed to remove/install selected packages
static void PM_ApplyChanges(void)
@ -2291,17 +2312,22 @@ static void PM_ApplyChanges(void)
package_t *p, **link;
char temp[MAX_OSPATH];
#ifdef WEBCLIENT
if (pkg_updating)
return;
pkg_updating = true;
#endif
//delete any that don't exist
for (link = &availablepackages; *link ; )
{
p = *link;
#ifdef WEBCLIENT
if (p->download)
; //erk, dude, don't do two!
else if ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_ENABLED)))
else
#endif
if ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_ENABLED)))
{ //if we don't want it but we have it anyway. don't bother to follow this logic when reinstalling
qboolean reloadpacks = false;
struct packagedep_s *dep;
@ -2416,12 +2442,14 @@ static void PM_ApplyChanges(void)
link = &(*link)->next;
}
#ifdef WEBCLIENT
//and flag any new/updated ones for a download
for (p = availablepackages; p ; p=p->next)
{
if ((p->flags&DPF_MARKED) && !(p->flags&DPF_ENABLED) && !p->download)
p->trymirrors = ~0u;
}
#endif
PM_StartADownload(); //and try to do those downloads.
}
@ -2496,14 +2524,18 @@ static qboolean PM_DeclinedPackages(char *out, size_t outsize)
}
static void PM_PromptApplyChanges_Callback(void *ctx, int opt)
{
#ifdef WEBCLIENT
pkg_updating = false;
#endif
if (opt == 0)
PM_ApplyChanges();
}
static void PM_PromptApplyChanges(void);
static void PM_PromptApplyDecline_Callback(void *ctx, int opt)
{
#ifdef WEBCLIENT
pkg_updating = false;
#endif
if (opt == 1)
{
PM_DeclinedPackages(NULL, 0);
@ -2514,6 +2546,7 @@ static void PM_PromptApplyChanges(void)
{
unsigned int changes;
char text[8192];
#ifdef WEBCLIENT
//lock it down, so noone can make any changes while this prompt is still displayed
if (pkg_updating)
{
@ -2521,6 +2554,7 @@ static void PM_PromptApplyChanges(void)
return;
}
pkg_updating = true;
#endif
strcpy(text, "Really decline the following\nrecommendedpackages?\n\n");
if (PM_DeclinedPackages(text+strlen(text), sizeof(text)-strlen(text)))
@ -2530,7 +2564,11 @@ static void PM_PromptApplyChanges(void)
strcpy(text, "Apply the following changes?\n\n");
changes = PM_ChangeList(text+strlen(text), sizeof(text)-strlen(text));
if (!changes)
{
#ifdef WEBCLIENT
pkg_updating = false;//no changes...
#endif
}
else
M_Menu_Prompt(PM_PromptApplyChanges_Callback, NULL, text, "Apply", NULL, "Cancel");
}
@ -2903,11 +2941,13 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
if (p->alternative && (p->flags & DPF_HIDDEN))
p = p->alternative;
#ifdef WEBCLIENT
if (p->download)
Draw_FunString (x+4, y, va("%i", (int)p->download->qdownload.percent));
else if (p->trymirrors)
Draw_FunString (x+4, y, "PND");
else
else
#endif
{
switch((p->flags & (DPF_ENABLED | DPF_MARKED)))
{
@ -3055,14 +3095,17 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig
}
}
}
#ifdef WEBCLIENT
else
p->trymirrors = 0;
#endif
return true;
}
return false;
}
#ifdef WEBCLIENT
static void MD_AutoUpdate_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
{
char *settings[] =
@ -3094,6 +3137,17 @@ static qboolean MD_AutoUpdate_Key (struct menucustom_s *c, struct menu_s *m, int
return false;
}
static qboolean MD_MarkUpdatesButton (union menuoption_s *mo,struct menu_s *m,int key)
{
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
{
PM_MarkUpdates();
return true;
}
return false;
}
#endif
qboolean MD_PopMenu (union menuoption_s *mo,struct menu_s *m,int key)
{
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
@ -3114,15 +3168,6 @@ static qboolean MD_ApplyDownloads (union menuoption_s *mo,struct menu_s *m,int k
return false;
}
static qboolean MD_MarkUpdatesButton (union menuoption_s *mo,struct menu_s *m,int key)
{
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
{
PM_MarkUpdates();
return true;
}
return false;
}
static qboolean MD_RevertUpdates (union menuoption_s *mo,struct menu_s *m,int key)
{
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
@ -3154,20 +3199,22 @@ static void MD_AddItemsToDownloadMenu(menu_t *m)
y+=8;
if (!prefixlen)
{
#ifdef WEBCLIENT
MC_AddCommand(m, 0, 170, y, "Mark Updates", MD_MarkUpdatesButton);
y+=8;
#endif
MC_AddCommand(m, 0, 170, y, "Revert Updates", MD_RevertUpdates);
y+=8;
}
if (!prefixlen)
{
#ifdef WEBCLIENT
c = MC_AddCustom(m, 0, y, p, 0);
c->draw = MD_AutoUpdate_Draw;
c->key = MD_AutoUpdate_Key;
c->common.width = 320;
c->common.height = 8;
y += 8;
#endif
}
y+=4; //small gap

View File

@ -187,9 +187,6 @@ qboolean M_Options_InvertMouse (menucheck_t *option, struct menu_s *menu, chk_se
void M_Menu_Options_f (void)
{
extern cvar_t crosshair, r_projection;
#ifndef CLIENTONLY
extern cvar_t sv_autosave;
#endif
int y;
static const char *projections[] = {
@ -211,7 +208,8 @@ void M_Menu_Options_f (void)
NULL
};
#ifndef CLIENTONLY
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
extern cvar_t sv_autosave;
static const char *autosaveopts[] = {
"Off",
"30 secs",
@ -234,8 +232,8 @@ void M_Menu_Options_f (void)
menubulk_t bulk[] = {
MB_CONSOLECMD("Customize controls", "menu_keys\n", "Modify keyboard and mouse inputs."),
#ifdef WEBCLIENT
MB_CONSOLECMD("Updates and packages", "menu_download\n", "Modify keyboard and mouse inputs."),
#ifdef PACKAGEMANAGER
MB_CONSOLECMD("Updates and Packages", "menu_download\n", "Configure additional content and plugins."),
#endif
MB_CONSOLECMD("Go to console", "toggleconsole\nplay misc/menu2.wav\n", "Open up the engine console."),
MB_CONSOLECMD("Reset to defaults", "cvarreset *\nexec default.cfg\nplay misc/menu2.wav\n", "Reloads the default configuration."),
@ -250,7 +248,7 @@ void M_Menu_Options_f (void)
MB_CHECKBOXCVAR("Lookspring", lookspring, 0),
MB_CHECKBOXCVAR("Lookstrafe", lookstrafe, 0),
MB_CHECKBOXCVAR("Windowed Mouse", _windowed_mouse, 0),
#ifndef CLIENTONLY
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
MB_COMBOCVAR("Auto Save", sv_autosave, autosaveopts, autosavevals, NULL),
#endif
MB_SPACING(4),

View File

@ -4,7 +4,7 @@
#include "winquake.h"
#include "shader.h"
#ifndef NOBUILTINMENUS
#ifndef CLIENTONLY
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
//=============================================================================
/* LOAD/SAVE MENU */
@ -329,10 +329,10 @@ void M_Menu_SinglePlayer_f (void)
MC_AddConsoleCommand (menu, 64, 170, 40, "Easy", va("closemenu; skill 0;deathmatch 0; coop %i;newgame\n", cl_splitscreen.ival>0));
MC_AddConsoleCommand (menu, 64, 170, 48, "Medium", va("closemenu; skill 1;deathmatch 0; coop %i;newgame\n", cl_splitscreen.ival>0));
MC_AddConsoleCommand (menu, 64, 170, 56, "Hard", va("closemenu; skill 2;deathmatch 0; coop %i;newgame\n", cl_splitscreen.ival>0));
#ifdef SAVEDGAMES
MC_AddConsoleCommand (menu, 64, 170, 72, "Load Game", "menu_load\n");
MC_AddConsoleCommand (menu, 64, 170, 80, "Save Game", "menu_save\n");
#endif
menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false);
return;
#endif
@ -455,8 +455,10 @@ void M_Menu_SinglePlayer_f (void)
menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "New Game", "menu_single class demo1\n");
}
#ifdef SAVEDGAMES
MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Save Game", "menu_save\n");
MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Load Game", "menu_load\n");
#endif
}
menu->cursoritem = (menuoption_t *)MC_AddCursor(menu, &resel, 56, menu->selecteditem?menu->selecteditem->common.posy:0);
@ -474,8 +476,10 @@ void M_Menu_SinglePlayer_f (void)
menu->selecteditem = (menuoption_t*)
MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", "closemenu;disconnect;maxclients 1;samelevel \"\";deathmatch \"\";set_calc coop ($cl_splitscreen>0);startmap_sp\n");
#ifdef SAVEDGAMES
MC_AddConsoleCommandQBigFont (menu, 72, 52, "Load Game", "menu_load\n");
MC_AddConsoleCommandQBigFont (menu, 72, 72, "Save Game", "menu_save\n");
#endif
menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);
return;
@ -525,12 +529,14 @@ void M_Menu_SinglePlayer_f (void)
menu->selecteditem = (menuoption_t *)b;
b->common.width = width;
b->common.height = 20;
#ifdef SAVEDGAMES
b = MC_AddConsoleCommand (menu, 72, 304, 52, "", "menu_load\n");
b->common.width = width;
b->common.height = 20;
b = MC_AddConsoleCommand (menu, 72, 304, 72, "", "menu_save\n");
b->common.width = width;
b->common.height = 20;
#endif
#if MAX_SPLITS > 1
b = (menubutton_t*)MC_AddCvarCombo(menu, 72, 72+width/2, 92, "", &cl_splitscreen, opts, vals);

View File

@ -1222,7 +1222,7 @@ void M_Init_Internal (void)
return;
internalmenusregistered = true;
#ifndef CLIENTONLY
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
Cmd_AddCommand ("menu_save", M_Menu_Save_f);
Cmd_AddCommand ("menu_load", M_Menu_Load_f);
Cmd_AddCommand ("menu_loadgame", M_Menu_Load_f); //q2...
@ -1371,7 +1371,7 @@ void M_Init (void)
Cmd_AddCommand ("menu_servers", M_Menu_ServerList2_f);
#endif
//downloads menu needs sandboxing, so cannot be provided by qc.
#ifdef WEBCLIENT
#ifdef PACKAGEMANAGER
Cmd_AddCommand ("menu_download", Menu_DownloadStuff_f);
#endif
//demo menu is allowed to see outside of the quakedir. you can't replicate that in qc's sandbox.

View File

@ -1817,6 +1817,8 @@ static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s
model_t *mod = Mod_ForName(modelname, MLV_WARN);
if (modelval)
modelval->string = G_INT(OFS_PARM1); //lets hope garbage collection is enough.
else
Con_Printf("PF_m_setmodel: no model field!\n");
if (mod)
while(mod->loadstate == MLS_LOADING)
@ -1834,7 +1836,10 @@ static void QCBUILTIN PF_m_setcustomskin(pubprogfuncs_t *prinst, struct globalva
const char *skindata = PF_VarString(prinst, 2, pr_globals);
eval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, "skinobject", ev_string, &menuc_eval.skinobject);
if (!val)
{
Con_Printf("PF_m_setcustomskin: no skinobject field!\n");
return;
}
if (val->_float > 0)
{
@ -1858,6 +1863,8 @@ static void QCBUILTIN PF_m_setorigin(pubprogfuncs_t *prinst, struct globalvars_s
eval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, "origin", ev_vector, &menuc_eval.origin);
if (val)
VectorCopy(org, val->_vector);
else
Con_Printf("PF_m_setorigin: no origin field!\n");
}
static void QCBUILTIN PF_m_clearscene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -2645,6 +2652,12 @@ qboolean MP_Init (void)
PR_ProgsAdded(menu_world.progs, mprogs, "menu.dat");
//ensure that there's space for these fields in.
//other fields will always be referenced/defined by the qc, or 0.
PR_RegisterFieldVar(menu_world.progs, ev_string, "model", -1, -1);
PR_RegisterFieldVar(menu_world.progs, ev_vector, "origin", -1, -1);
PR_RegisterFieldVar(menu_world.progs, ev_float, "skinobject", -1, -1);
menuentsize = PR_InitEnts(menu_world.progs, 8192);

View File

@ -1453,7 +1453,7 @@ void V_ClearRefdef(playerview_t *pv)
r_refdef.fovv_x = 0;
r_refdef.fovv_y = 0;
r_refdef.drawsbar = cl.intermissionmode == IM_NONE;
r_refdef.drawsbar = (cl.intermissionmode == IM_NONE);
r_refdef.flags = 0;
r_refdef.areabitsknown = false;

View File

@ -111,6 +111,7 @@
#define SUBSERVERS //Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.
//#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7)
//#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140)
#define SAVEDGAMES //Can save the game.
// Networking options
#define NQPROT //act as an nq client/server, with nq gamecode.
@ -124,6 +125,7 @@
//#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea.
#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections.
#define CL_MASTER //Clientside Server Browser functionality.
#define PACKAGEMANAGER //Allows the user to enable/disable/download(with WEBCLIENT) packages and plugins.
// Audio Drivers
#define AVAIL_OPENAL

View File

@ -113,6 +113,7 @@
//#define SUBSERVERS //Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.
////#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7)
////#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140)
//#define SAVEDGAMES //Can save the game.
// Networking options
//#define NQPROT //act as an nq client/server, with nq gamecode.
@ -126,6 +127,7 @@
//#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea.
//#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections.
//#define CL_MASTER //Clientside Server Browser functionality.
//#define PACKAGEMANAGER //Allows the user to enable/disable/download packages and plugins.
// Audio Drivers
//#define AVAIL_OPENAL

View File

@ -110,6 +110,7 @@
//#define SUBSERVERS //Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.
//#define HLCLIENT 7 //we can run HL gamecode (not protocol compatible, set to 6 or 7)
//#define HLSERVER 140 //we can run HL gamecode (not protocol compatible, set to 138 or 140)
#define SAVEDGAMES //Can save the game.
// Networking options
//#define NQPROT //act as an nq client/server, with nq gamecode.
@ -123,6 +124,7 @@
//#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea.
#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections.
#define CL_MASTER //Clientside Server Browser functionality.
//#define PACKAGEMANAGER //Allows the user to enable/disable/download packages and plugins.
// Audio Drivers
#define AVAIL_OPENAL

View File

@ -163,6 +163,8 @@
#undef HAVE_MEDIA_DECODER //can play cin/roq, more with plugins
#undef HAVE_MEDIA_ENCODER //capture/capturedemo work.
#undef HAVE_SPEECHTOTEXT //windows speech-to-text thing
//#define SAVEDGAMES //Can save the game.
#undef PACKAGEMANAGER //Allows the user to enable/disable/download packages and plugins.
#ifdef COMPILE_OPTS
//things to configure qclib, which annoyingly doesn't include this file itself

View File

@ -2242,6 +2242,11 @@ bspx_header_t *BSPX_Setup(model_t *mod, char *filebase, unsigned int filelen, lu
return h;
}
#ifdef SERVERONLY
void BSPX_LoadEnvmaps(model_t *mod, bspx_header_t *bspx, void *mod_base)
{
}
#else
/*
void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt);
void BSPX_RenderEnvmaps(model_t *mod)
@ -2408,9 +2413,6 @@ void BSPX_LoadEnvmaps(model_t *mod, bspx_header_t *bspx, void *mod_base)
}
}
#if 1//ndef SERVERONLY
struct bspxrw
{
fromgame_t fg;
@ -2632,7 +2634,13 @@ unsigned int Mod_NearestCubeForSurf(msurface_t *surf, denvmap_t *envmap, size_t
for (n = 0; n < nenvmap; n++)
{
VectorSubtract(mid, envmap[n].origin, diff);
#if 1
//axial distance
dist = min(min(fabs(diff[0]), fabs(diff[1])), fabs(diff[2]));
#else
//radial distance (squared)
dist = DotProduct(diff,diff);
#endif
if (bestdist > dist)
{
best = n;

View File

@ -736,10 +736,12 @@ void Mod_Purge(enum mod_purge_e ptype)
}
}
#ifndef SERVERONLY
void Mod_FindCubemaps_f(void);
void Mod_Realign_f(void);
void Mod_BSPX_List_f(void);
void Mod_BSPX_Strip_f(void);
#endif
/*
===============
@ -777,10 +779,12 @@ void Mod_Init (qboolean initial)
Cmd_AddCommand("sv_saveentfile", Mod_SaveEntFile_f);
Cmd_AddCommand("version_modelformats", Mod_PrintFormats_f);
#ifndef SERVERONLY
Cmd_AddCommandD("map_findcubemaps", Mod_FindCubemaps_f, "Scans the entities of a map to find reflection envmap sites and determines the nearest one to each surface.");
Cmd_AddCommandD("map_realign", Mod_Realign_f, "Reads the named bsp and writes it back out with only alignment changes.");
Cmd_AddCommandD("map_bspx_list", Mod_BSPX_List_f, "Lists all lumps (and their sizes) in the specified bsp.");
Cmd_AddCommandD("map_bspx_strip", Mod_BSPX_Strip_f, "Strips a named extension lump from a bsp file.");
#endif
}
if (initial)

View File

@ -1877,26 +1877,74 @@ static const char *glsl_hdrs[] =
NULL
};
qboolean GLSlang_GenerateIncludes(int maxstrings, int *strings, const GLchar *prstrings[], GLint length[], const char *shadersource)
#define GLSLPARTS (64+16)
struct glslparts_s
{
const GLchar *str[GLSLPARTS];
GLint len[GLSLPARTS];
const GLchar *file[GLSLPARTS];
int line[GLSLPARTS];
int strings;
const char *error;
};
static void GLSlang_GenerateInternal(struct glslparts_s *glsl, const char *shadersource)
{
if (glsl->strings == GLSLPARTS)
{
glsl->error = "Too many parts";
return;
}
glsl->str[glsl->strings] = shadersource;
glsl->len[glsl->strings] = strlen(shadersource);
glsl->file[glsl->strings] = NULL;
glsl->line[glsl->strings] = 0;
glsl->strings += 1;
}
static void GLSlang_Generate(struct glslparts_s *glsl, const char *shadersource, GLint length, const char *filename, int linenumber)
{
if (glsl->strings == GLSLPARTS)
{
glsl->error = "Too many parts";
return;
}
glsl->str[glsl->strings] = shadersource;
glsl->len[glsl->strings] = length;
glsl->file[glsl->strings] = filename;
glsl->line[glsl->strings] = linenumber;
glsl->strings += 1;
}
static qboolean GLSlang_GenerateIncludes(struct glslparts_s *glsl, const char *shadersource, const char *filename, int linenumber)
{
int i;
char *incline, *inc;
char incname[256];
while((incline=strstr(shadersource, "#include")))
{
if (*strings == maxstrings)
return false;
/*emit up to the include*/
if (incline - shadersource)
{
prstrings[*strings] = shadersource;
length[*strings] = incline - shadersource;
*strings += 1;
char *e = incline;
while(e > shadersource && (e[-1] == ' ' || e[-1] == '\t'))
e--;
if (e > shadersource && e[-1] == '\n')
GLSlang_Generate(glsl, shadersource, e-shadersource, filename, linenumber);
else
GLSlang_Generate(glsl, shadersource, incline-shadersource, filename, linenumber);
}
incline += 8;
incline = COM_ParseOut (incline, incname, sizeof(incname));
if (!incline)
{
glsl->error = "missing include name";
return false;
}
while (*incline == ' ' || *incline == '\t')
incline++;
if (!strncmp(incname, "cvar/", 5))
{
@ -1904,17 +1952,13 @@ qboolean GLSlang_GenerateIncludes(int maxstrings, int *strings, const GLchar *pr
if (var)
{
var->flags |= CVAR_SHADERSYSTEM;
if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, var->string))
if (!GLSlang_GenerateIncludes(glsl, var->string, NULL, 0))
return false;
}
else
{
/*dump something if the cvar doesn't exist*/
if (*strings == maxstrings)
return false;
prstrings[*strings] = "0";
length[*strings] = strlen("0");
*strings += 1;
GLSlang_Generate(glsl, "0", strlen("0"), filename, linenumber);
}
}
else
@ -1923,38 +1967,39 @@ qboolean GLSlang_GenerateIncludes(int maxstrings, int *strings, const GLchar *pr
{
if (!strcmp(incname, glsl_hdrs[i]))
{
if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, glsl_hdrs[i+1]))
if (!GLSlang_GenerateIncludes(glsl, glsl_hdrs[i+1], glsl_hdrs[i], 1))
return false;
break;
}
}
if (!glsl_hdrs[i])
{
if (FS_LoadFile(incname, (void**)&inc) != (qofs_t)-1)
size_t sz;
inc = COM_LoadTempMoreFile(incname, &sz);
if (inc)
{
if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, inc))
{
FS_FreeFile(inc);
if (!GLSlang_GenerateIncludes(glsl, inc, NULL, 1))
return false;
}
FS_FreeFile(inc);
}
else
{
glsl->error = "include file not found";
return false; //FIXME: add a warning
}
}
}
/*move the pointer past the include*/
shadersource = incline;
while (shadersource < incline)
{
if (*shadersource == '\n')
linenumber++;
shadersource++;
}
}
if (*shadersource)
{
if (*strings == maxstrings)
return false;
/*dump the remaining shader string*/
prstrings[*strings] = shadersource;
length[*strings] = strlen(prstrings[*strings]);
*strings += 1;
}
if (*shadersource)
GLSlang_Generate(glsl, shadersource, strlen(shadersource), filename, linenumber);
return true;
}
@ -1965,11 +2010,12 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
{
GLhandleARB shader;
int i;
const GLchar *prstrings[64+16];
GLint length[sizeof(prstrings)/sizeof(prstrings[0])];
int strings = 0;
struct glslparts_s glsl;
char verline[64];
glsl.strings = 0;
glsl.error = NULL;
if (!shadersource)
return 0;
@ -2013,44 +2059,32 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
Q_snprintfz(verline, sizeof(verline), "#version %u compatibility\n", ver);
else
Q_snprintfz(verline, sizeof(verline), "#version %u\n", ver); //core assumed, where defined
prstrings[strings] = verline;
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, verline);
}
}
while(*precompilerconstants)
{
prstrings[strings] = *precompilerconstants++;
length[strings] = strlen(prstrings[strings]);
strings++;
}
GLSlang_GenerateInternal(&glsl, *precompilerconstants++);
prstrings[strings] = "#define ENGINE_"DISTRIBUTION"\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "#define ENGINE_"DISTRIBUTION"\n");
switch (shadertype)
{
case GL_FRAGMENT_SHADER_ARB:
prstrings[strings] = "#define FRAGMENT_SHADER\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "#define FRAGMENT_SHADER\n");
if (gl_config.gles)
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
"precision highp float;\n"
"#else\n"
"precision mediump float;\n"
"#endif\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
if (ver >= 130)
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
//gl3+ deprecated the some things. these are removed in forwards-compatible / core contexts.
//varying became either in or out, which is important if you have geometry shaders...
"#define varying in\n"
@ -2071,20 +2105,16 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
"out vec4 fte_fragdata3;"
"\n#endif\n" //gles3 requires this
"#define gl_FragColor fte_fragdata0\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
else
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#define fte_fragdata0 gl_FragData[0]\n"
"#define fte_fragdata1 gl_FragData[1]\n"
"#define fte_fragdata2 gl_FragData[2]\n"
"#define fte_fragdata3 gl_FragData[3]\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
if (prog)
@ -2127,82 +2157,62 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
for (i = 0; i < countof(defaultsamplernames); i++)
{
if (prog->defaulttextures & (1u<<i))
{
prstrings[strings] = defaultsamplernames[i];
length[strings] = strlen(prstrings[strings]);
strings++;
}
GLSlang_GenerateInternal(&glsl, defaultsamplernames[i]);
}
#endif
for (i = 0; i < prog->numsamplers && i < countof(numberedsamplernames); i++)
{
prstrings[strings] = numberedsamplernames[i];
length[strings] = strlen(prstrings[strings]);
strings++;
}
GLSlang_GenerateInternal(&glsl, numberedsamplernames[i]);
}
break;
case GL_GEOMETRY_SHADER_ARB:
prstrings[strings] = "#define GEOMETRY_SHADER\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "#define GEOMETRY_SHADER\n");
break;
case GL_TESS_CONTROL_SHADER_ARB:
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#define TESS_CONTROL_SHADER\n"
"#if __VERSION__ < 400\n"
"#extension GL_ARB_tessellation_shader : enable\n"
"#endif\n";
"#endif\n"
//varyings are arrays, so don't bother defining that here.
length[strings] = strlen(prstrings[strings]);
strings++;
);
break;
case GL_TESS_EVALUATION_SHADER_ARB:
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#define TESS_EVALUATION_SHADER\n"
"#if __VERSION__ < 400\n"
"#extension GL_ARB_tessellation_shader : enable\n"
"#endif\n"
"#define varying out\n";
length[strings] = strlen(prstrings[strings]);
strings++;
"#define varying out\n"
);
break;
case GL_VERTEX_SHADER_ARB:
prstrings[strings] = "#define VERTEX_SHADER\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "#define VERTEX_SHADER\n");
#ifdef RTLIGHTS
if (!r_shadow_shadowmapping.ival && ver >= 120)
{
prstrings[strings] = "invariant gl_Position;\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "invariant gl_Position;\n");
}
#endif
if (gl_config.gles)
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
"precision highp float;\n"
"#else\n"
"precision mediump float;\n"
"#endif\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
if (ver >= 130)
{
prstrings[strings] =
"#define attribute in\n"
"#define varying out\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl,
"#define attribute in\n"
"#define varying out\n"
);
}
if (gl_config_nofixedfunc)
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"attribute vec3 v_position1;\n"
"#ifdef FRAMEBLEND\n"
"attribute vec3 v_position2;\n"
@ -2218,13 +2228,11 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
#else
"#define ftetransform() (m_modelviewprojection * vec4(v_position, 1.0))\n"
#endif
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
else
{
prstrings[strings] =
GLSlang_GenerateInternal(&glsl,
"#ifdef FRAMEBLEND\n"
"attribute vec3 v_position2;\n"
"uniform vec2 e_vblend;\n"
@ -2236,24 +2244,51 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
"uniform mat4 m_modelviewprojection;\n"
"#define ftetransform ftransform\n"
"#endif\n"
;
length[strings] = strlen(prstrings[strings]);
strings++;
);
}
break;
default:
prstrings[strings] = "#define UNKNOWN_SHADER\n";
length[strings] = strlen(prstrings[strings]);
strings++;
GLSlang_GenerateInternal(&glsl, "#define UNKNOWN_SHADER\n");
break;
}
GLSlang_GenerateIncludes(sizeof(prstrings)/sizeof(prstrings[0]), &strings, prstrings, length, shadersource);
GLSlang_GenerateIncludes(&glsl, shadersource, name, 1);
shader = qglCreateShaderObjectARB(shadertype);
if (gl_workaround_ati_shadersource.ival)
if (developer.ival)
{
GLcharARB *combined;
int totallen = 1;
for (i = 0; i < glsl.strings; i++)
totallen += glsl.len[i] + 64;
combined = malloc(totallen);
totallen = 0;
combined[totallen] = 0;
for (i = 0; i < glsl.strings; i++)
{
if (ver && !i)
; //#version MUST be the first line, don't prefix it with a #line, it'll just break things.
else if (!totallen || combined[totallen-1] == '\n')
{ //last line was a newline, hurrah. safe to insert without breaking anything
Q_snprintfz(combined+totallen, 64, "#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]);
totallen += strlen(combined+totallen);
}
else if (glsl.len[i] && *glsl.str[i] == '\n')
{ //last line didn't end with a newline, but there is one after. that's okay too, but we need to play it safe.
Q_snprintfz(combined+totallen, 64, "\n#line %i %i //%s\n", glsl.line[i], i, glsl.file[i]);
totallen += strlen(combined+totallen);
}
//now shove stuff there.
memcpy(combined+totallen, glsl.str[i], glsl.len[i]);
totallen += glsl.len[i];
combined[totallen] = 0;
}
qglShaderSourceARB(shader, 1, (const GLcharARB**)&combined, NULL);
free(combined);
}
else if (gl_workaround_ati_shadersource.ival)
{
/*ATI Driver Bug: ATI drivers ignore the 'length' array.
this code does what the drivers fail to do.
@ -2262,22 +2297,22 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int
*/
GLcharARB *combined;
int totallen = 1;
for (i = 0; i < strings; i++)
totallen += length[i];
for (i = 0; i < glsl.strings; i++)
totallen += glsl.len[i];
combined = malloc(totallen);
totallen = 0;
combined[totallen] = 0;
for (i = 0; i < strings; i++)
for (i = 0; i < glsl.strings; i++)
{
memcpy(combined + totallen, prstrings[i], length[i]);
totallen += length[i];
memcpy(combined + totallen, glsl.str[i], glsl.len[i]);
totallen += glsl.len[i];
combined[totallen] = 0;
}
qglShaderSourceARB(shader, 1, (const GLcharARB**)&combined, NULL);
free(combined);
}
else
qglShaderSourceARB(shader, strings, prstrings, length);
qglShaderSourceARB(shader, glsl.strings, glsl.str, glsl.len);
qglCompileShaderARB(shader);
return shader;

View File

@ -13621,7 +13621,7 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *s
}
else if (type->type == ev_field)
{
if (type->aux_type->type == ev_vector && !arraysize)
if (type->aux_type->type == ev_vector && !arraysize && *def->name != ':')
{
//do the vector thing.
QC_snprintfz(newname, sizeof(newname), "%s_x", def->name);

View File

@ -5625,11 +5625,12 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST);
if (!d)
{
d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT);
d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT|GDF_USED);
// for (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++)
// d->symboldata[i]._int = pr.size_fields+i;
// pr.size_fields += i;
d->used = true;
d->referenced = true; //always referenced, so you can inherit safely.
}
if (d->arraysize < basicindex+(arraysize?arraysize:1))
@ -5637,7 +5638,17 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
if (d->symboldata)
QCC_PR_ParseError(ERR_INTERNAL, "array members are kinda limited, sorry. try rearranging them or adding padding for alignment\n"); //FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays.
else
d->arraysize = basicindex+(arraysize?arraysize:1);
{
int newsize = basicindex+(arraysize?arraysize:1);
if (d->type->type == ev_union || d->type->type == ev_struct)
d->arraysize = newsize;
else while(d->arraysize < newsize)
{
QC_snprintfz(membername, sizeof(membername), "::%s[%i]", basictypenames[newparm->type], d->arraysize/d->type->size);
QCC_PR_DummyDef(d->type, membername, d->scope, 0, d, d->arraysize, true, GDF_CONST);
d->arraysize+=d->type->size;
}
}
}
}
QCC_FreeDef(d);

View File

@ -604,6 +604,25 @@ void QCC_PrintStrings (void)
}
}*/
void QCC_SortFields (void)
{
int i, j;
QCC_ddef32_t t;
// good 'ol bubble sort
//(qsort doesn't guarentee ordering)
for (i = 0; i < numfielddefs; i++)
{
for (j = i; j < numfielddefs; j++)
if (fields[i].ofs > fields[j].ofs)
{
t = fields[i];
fields[i] = fields[j];
fields[j] = t;
}
}
}
void QCC_PrintFields (void)
{
extern char *basictypenames[];
@ -1855,6 +1874,7 @@ pbool QCC_WriteData (int crc)
printf("code: %s:%i: %s%s%s %s@%i\n", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?"save ":"nosave ", dd->type&DEF_SHARED?"shared ":"", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs);
#endif
}
QCC_SortFields();
if (dupewarncount > 10 && !verbose)
QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more warnings about unreferenced variables, as you clearly don't care about the first 10.", dupewarncount-10);

View File

@ -1,7 +1,7 @@
#include "quakedef.h"
#include "pr_common.h"
#ifndef CLIENTONLY
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
extern cvar_t skill;
extern cvar_t deathmatch;
@ -9,7 +9,7 @@ extern cvar_t coop;
extern cvar_t teamplay;
extern cvar_t pr_enable_profiling;
cvar_t sv_savefmt = CVARFD("sv_savefmt", "1", CVAR_SAVE, "Specifies the format used for the saved game.\n0=legacy.\n1=fte\n2=binary");
cvar_t sv_savefmt = CVARFD("sv_savefmt", "", CVAR_SAVE, "Specifies the format used for the saved game.\n0=legacy.\n1=fte\n2=binary");
cvar_t sv_autosave = CVARFD("sv_autosave", "5", CVAR_SAVE, "Interval for autosaves, in minutes. Set to 0 to disable autosave.");
void SV_Savegame_f (void);
@ -78,7 +78,7 @@ void SV_SavegameComment (char *text, size_t textsize)
#ifndef QUAKETC
//expects the version to have already been parsed
void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
{
//FIXME: Multiplayer save probably won't work with spectators.
char mapname[MAX_QPATH];
@ -394,7 +394,7 @@ static void SV_LegacySavegame (const char *savename)
}
sprintf (name, "%s", savename);
COM_RequireExtension (name, ".sav", sizeof(name));
COM_RequireExtension (name, ".sav", sizeof(name)); //do NOT allow .pak etc
if (!FS_NativePath(name, FS_GAMEONLY, native, sizeof(native)))
return;
Con_TPrintf (U8("Saving game to %s...\n"), native);
@ -1109,7 +1109,7 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
//FIXME: static entities
//FIXME: midi track
//FIXME: custom temp-ents?
//FIXME: pending uri_gets? (if only just to report fails)
//FIXME: pending uri_gets? (if only just to report fails on load)
//FIXME: routing calls?
//FIXME: sql queries?
//FIXME: frik files?

View File

@ -116,7 +116,9 @@ typedef struct
unsigned int csqcchecksum;
qboolean mapchangelocked;
#ifdef SAVEDGAMES
double autosave_time;
#endif
double time;
double starttime;
int framenum;
@ -1572,7 +1574,6 @@ typedef struct
int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p);
// savegame.c
void SV_LegacySavegame_f(void);
void SV_Savegame_f (void);
void SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);
void SV_Loadgame_f (void);

View File

@ -675,6 +675,7 @@ void SV_Map_f (void)
if (!isrestart)
SV_SaveSpawnparms ();
#ifdef SAVEDGAMES
if (newunit)
SV_FlushLevelCache(); //forget all on new unit
else if (startspot && !isrestart && !newunit)
@ -698,6 +699,7 @@ void SV_Map_f (void)
#endif
SV_SaveLevelCache(NULL, false);
}
#endif
#ifdef Q3SERVER
{
@ -783,7 +785,11 @@ void SV_Map_f (void)
}
SCR_SetLoadingFile("spawnserver");
if (newunit || !startspot || cinematic || !SV_LoadLevelCache(NULL, level, startspot, false))
if (newunit || !startspot || cinematic
#ifdef SAVEDGAMES
|| !SV_LoadLevelCache(NULL, level, startspot, false)
#endif
)
{
if (waschangelevel && !startspot)
startspot = "";
@ -854,12 +860,13 @@ void SV_Map_f (void)
Cvar_Set(nsv, "");
}
#ifdef SAVEDGAMES
if (q2savetos0)
{
if (sv.state != ss_cinematic) //too weird.
SV_Savegame("s0", true);
}
#endif
if (isDedicated)
Mod_Purge(MP_MAPCHANGED);

View File

@ -650,7 +650,9 @@ void SV_UnspawnServer (void) //terminate the running server.
free(svs.clients);
svs.clients = NULL;
svs.allocated_client_slots = 0;
#ifdef SAVEDGAMES
SV_FlushLevelCache();
#endif
NET_CloseServer ();
SV_RunCmdCleanup();
}
@ -1614,7 +1616,9 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
if (!startspot)
{
#ifdef SAVEDGAMES
SV_FlushLevelCache(); //to make sure it's caught
#endif
for (i=0 ; i<sv.allocated_client_slots ; i++)
{
if (svs.clients[i].spawninfo)
@ -1730,7 +1734,9 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
SV_SetMoveVars();
sv.starttime = Sys_DoubleTime() - sv.time;
#ifdef SAVEDGAMES
sv.autosave_time = sv.time + sv_autosave.value*60;
#endif
}
#endif

View File

@ -4899,8 +4899,10 @@ float SV_Frame (void)
{
isidle = false;
#ifdef SAVEDGAMES
if (sv.time > sv.autosave_time)
SV_AutoSave();
#endif
}
}
else
@ -5203,10 +5205,9 @@ void SV_InitLocal (void)
Cmd_AddCommand ("openroute", SV_OpenRoute_f);
#ifndef NOBUILTINMENUS
#ifndef SERVERONLY
#ifdef SAVEDGAMES
#if !defined(NOBUILTINMENUS) && !defined(SERVERONLY)
Cvar_Register(&sv_autosave, cvargroup_servercontrol);
#endif
#endif
Cvar_Register(&sv_savefmt, cvargroup_servercontrol);
#ifndef QUAKETC
@ -5216,6 +5217,7 @@ void SV_InitLocal (void)
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");
Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
#endif
SV_MVDInit();