Fix saved games a little. Should no longer cause issues with hexen2,ad,etc.

Fix black lightmaps in h2mp.
Fix missing tinyfont in hexen2.
Added the drop part of drag+drop to x11.
Hopefully fixed x11 clipboard issues.
Hidden some annoying developer prints under developer 2.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5298 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2018-08-23 06:03:31 +00:00
parent 3fb88f2ec2
commit 0980455e10
31 changed files with 866 additions and 272 deletions

View File

@ -509,15 +509,17 @@ SET_TARGET_PROPERTIES(qi PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN")
SET_TARGET_PROPERTIES(qi PROPERTIES PREFIX "fteplug_")
#Bullet Physics library plugin
FIND_PACKAGE(Bullet REQUIRED)
ADD_LIBRARY(bullet MODULE
plugins/qvm_api.c
plugins/plugin.c
plugins/bullet/bulletplug.cpp
)
TARGET_INCLUDE_DIRECTORIES(bullet PUBLIC ${BULLET_INCLUDE_DIRS})
SET_TARGET_PROPERTIES(bullet PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN")
SET_TARGET_PROPERTIES(bullet PROPERTIES PREFIX "fteplug_")
FIND_PACKAGE(Bullet)
IF (${BULLET_FOUND})
ADD_LIBRARY(bullet MODULE
plugins/qvm_api.c
plugins/plugin.c
plugins/bullet/bulletplug.cpp
)
TARGET_INCLUDE_DIRECTORIES(bullet PUBLIC ${BULLET_INCLUDE_DIRS})
SET_TARGET_PROPERTIES(bullet PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN")
SET_TARGET_PROPERTIES(bullet PROPERTIES PREFIX "fteplug_")
ENDIF()
#Bullet Physics library plugin
#FIND_PACKAGE(ode REQUIRED)
@ -569,4 +571,3 @@ ENDIF()
#ffmpeg plugin
#cef plugin
#bullet plugin

View File

@ -1249,6 +1249,12 @@ void VARGS CL_SendClientCommand(qboolean reliable, char *format, ...)
CL_AllowIndependantSendCmd(oldallow);
}
//sometimes a server will quickly restart twice.
//connected clients will then receive TWO 'new' commands - both with the same servercount value.
//the connection process then tries to proceed with two sets of commands until it fails catastrophically.
//by attempting to strip out dupe commands we can usually avoid the issue
//note that FTE servers track progress properly, so this is not an issue for us, but in the interests of compat with mvdsv...
//however, FTE servers can send a little faster, so warnings about this can be awkward.
int CL_RemoveClientCommands(char *command)
{
clcmdbuf_t *next, *first;

View File

@ -3302,7 +3302,7 @@ static void CLQW_ParseServerData (void)
}
else
{
if (CL_RemoveClientCommands("soundlist"))
if (CL_RemoveClientCommands("soundlist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
Con_DPrintf("Multiple soundlists\n");
// ask for the sound list next
// CL_SendClientCommand ("soundlist %i 0", cl.servercount);
@ -4068,7 +4068,7 @@ static void CL_ParseSoundlist (qboolean lots)
}
else
{
if (CL_RemoveClientCommands("modellist"))
if (CL_RemoveClientCommands("modellist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
Con_DPrintf("Multiple modellists\n");
// CL_SendClientCommand ("modellist %i 0", cl.servercount);
CL_SendClientCommand (true, modellist_name, cl.servercount, 0);
@ -5247,9 +5247,9 @@ static void CL_ParseSetInfo (void)
player = &cl.players[slot];
if (offset)
Con_DPrintf("SETINFO %s: %s+=%s\n", player->name, key, val);
Con_DLPrintf(2,"SETINFO %s: %s+=%s\n", player->name, key, val);
else
Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, val);
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
InfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final);
player->userinfovalid = true;

View File

@ -1964,6 +1964,11 @@ void M_Menu_Main_f (void)
static menuresel_t resel;
int y;
#ifndef SERVERONLY
if (isDedicated || !Renderer_Started())
return;
#endif
#ifdef CSQC_DAT
if (CSQC_ConsoleCommand(-1, va("%s %s", Cmd_Argv(0), Cmd_Args())))
return;
@ -2223,10 +2228,18 @@ int MC_AddBulk(struct menu_s *menu, menuresel_t *resel, menubulk_t *bulk, int xs
{ //lots of fancy code just to figure out the correct width of the string. yay. :(
int px, py;
conchar_t buffer[2048], *end;
end = COM_ParseFunString(CON_WHITEMASK, bulk->text, buffer, sizeof(buffer), false);
Font_BeginString(font_default, 0, 0, &px, &py);
px = Font_LineWidth(buffer, end);
Font_EndString(NULL);
if (font_default)
{
end = COM_ParseFunString(CON_WHITEMASK, bulk->text, buffer, sizeof(buffer), false);
Font_BeginString(font_default, 0, 0, &px, &py);
px = Font_LineWidth(buffer, end);
Font_EndString(NULL);
}
else
{
Con_DPrintf("MC_AddBulk: default font not initialised yet\n");
px = strlen(bulk->text)*8;
}
x -= ((float)px * vid.width) / vid.rotpixelwidth;
}

View File

@ -8,8 +8,6 @@
//=============================================================================
/* LOAD/SAVE MENU */
#define FTESAVEGAME_VERSION 25000
typedef struct {
int issave;
int cursorpos;
@ -61,7 +59,7 @@ static void M_ScanSave(unsigned int slot, const char *name, qboolean savable)
{
VFS_GETS(f, line, sizeof(line));
version = atoi(line);
if (version != 5 && version != 6 && (version < FTESAVEGAME_VERSION || version >= FTESAVEGAME_VERSION+GT_MAX))
if (version != SAVEGAME_VERSION_NQ && version != SAVEGAME_VERSION_QW && version != SAVEGAME_VERSION_FTE_LEG && (version < SAVEGAME_VERSION_FTE_HUB || version >= SAVEGAME_VERSION_FTE_HUB+GT_MAX))
{
Q_strncpyz (m_saves[slot].desc, "Incompatible version", sizeof(m_saves[slot].desc));
VFS_CLOSE (f);

View File

@ -2246,12 +2246,14 @@ parsefluid:
if (ptype->scale)
{
ptype->looks.type = PT_SPARKFAN;
Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name);
if (ptype->countextra || ptype->count || ptype->countrand)
Con_DPrintf("%s.%s: effect lacks a texture. assuming type sparkfan.\n", ptype->config, ptype->name);
}
else
{
ptype->looks.type = PT_SPARK;
Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name);
if (ptype->countextra || ptype->count || ptype->countrand)
Con_DPrintf("%s.%s: effect lacks a texture. assuming type spark.\n", ptype->config, ptype->name);
}
}
else if (ptype->looks.type == PT_SPARK)

View File

@ -7183,19 +7183,28 @@ pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc)
{
if (!num)
{
if (crc == 22390)
; //fte's full csqc stuff
else if (crc == 5927)
; //simple csqc. but only if
#ifndef csqc_isdarkplaces
else if (crc == 52195)
switch(crc)
{
case PROGHEADER_CRC_CSQC:
break; //fte's full csqc stuff
case PROGHEADER_CRC_QW:
case PROGHEADER_CRC_NQ:
case PROGHEADER_CRC_PREREL:
case PROGHEADER_CRC_TENEBRAE:
case PROGHEADER_CRC_H2:
case PROGHEADER_CRC_H2MP:
case PROGHEADER_CRC_H2DEMO:
break; //simple csqc. but only if it has the right entry points.
#ifndef csqc_isdarkplaces
case PROGHEADER_CRC_CSQC_DP:
csqc_isdarkplaces = true;
Con_DPrintf(CON_WARNING "Running darkplaces csprogs.dat version\n");
}
break;
#endif
else
default:
Con_Printf(CON_WARNING "Running unknown csprogs.dat version\n");
break;
}
}
return true;
}
@ -7372,7 +7381,7 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks
if (csqc_nogameaccess && !PR_FindFunction (csqcprogs, "CSQC_DrawHud", PR_ANY))
{ //simple csqc module is not csqc. abort now.
CSQC_Shutdown();
Con_DPrintf("Loaded progs.dat has no CSQC_DrawHud\n");
Con_DPrintf("progs.dat is not suitable for SimpleCSQC - no CSQC_DrawHud\n");
return false;
}
else if (csqc_nogameaccess)

View File

@ -1337,10 +1337,7 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, qbyte *
{
t = (-1-ambient)*255;
for (i=0 ; i<size*3 ; i++)
{
blocklights[i] = t;
}
for (maps = 0 ; maps < MAXQ1LIGHTMAPS ; maps++)
{
surf->cached_light[maps] = -1-ambient;
@ -1350,25 +1347,19 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, qbyte *
else if (r_fullbright.value>0) //not qw
{
for (i=0 ; i<size*3 ; i++)
{
blocklights[i] = r_fullbright.value*255*256;
}
}
else if (!currentmodel->lightdata)
{
/*fullbright if map is not lit. but not overbright*/
for (i=0 ; i<size*3 ; i++)
{
blocklights[i] = 128*256;
}
}
else if (!surf->samples)
{
/*no samples, but map is otherwise lit = pure black*/
for (i=0 ; i<size*3 ; i++)
{
blocklights[i] = 0;
}
surf->cached_light[0] = 0;
surf->cached_colour[0] = 0;
}
@ -1472,8 +1463,26 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, qbyte *
else
{
// set to full bright if no light data
if (r_fullbright.ival || !currentmodel->lightdata)
if (ambient < 0)
{
t = (-1-ambient)*255;
for (i=0 ; i<size ; i++)
blocklights[i] = t;
for (maps = 0 ; maps < MAXQ1LIGHTMAPS ; maps++)
{
surf->cached_light[maps] = -1-ambient;
surf->cached_colour[maps] = 0xff;
}
}
else if (r_fullbright.value > 0)
{ //r_fullbright is meant to be a scaler.
for (i=0 ; i<size ; i++)
blocklights[i] = r_fullbright.value*255*256;
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
}
else if (!currentmodel->lightdata)
{ //no scalers here.
for (i=0 ; i<size ; i++)
blocklights[i] = 255*256;
surf->cached_light[0] = d_lightstylevalue[0];

View File

@ -565,7 +565,7 @@ int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match,
dir = opendir(truepath);
if (!dir)
{
Con_DPrintf("Failed to open dir %s\n", truepath);
Con_DLPrintf((errno==ENOENT)?2:1, "Failed to open dir %s\n", truepath);
return true;
}
do
@ -664,7 +664,7 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
lib = lt_dlopenext (name);
if (!lib)
{
Con_DPrintf("%s: %s\n", name, lt_dlerror());
Con_DLPrintf(2, "%s: %s\n", name, lt_dlerror());
return NULL;
}
@ -711,7 +711,7 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
lib = dlopen (va("%s.so", name), RTLD_LAZY);
if (!lib)
{
Con_DPrintf("%s\n", dlerror());
Con_DLPrintf(2,"%s\n", dlerror());
return NULL;
}

View File

@ -1220,8 +1220,14 @@ STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION = 255, // DP
//savegame vars
#define SAVEGAME_COMMENT_LENGTH 39
#define SAVEGAME_VERSION 667
#define SAVEGAME_COMMENT_LENGTH 39
#define SAVEGAME_VERSION_NQ 5
#define SAVEGAME_VERSION_QW 6 //actually zQuake, but the functional difference is that its qw instead of nq.
#define SAVEGAME_VERSION_FTE_LEG 667 //found in .sav files. this is for legacy-like saved games with multiple players.
#define SAVEGAME_VERSION_FTE_HUB 25000 //found in .fsv files. includes svs.gametype, so bumps should be large.
#define CACHEGAME_VERSION_OLD 513
#define CACHEGAME_VERSION_VERBOSE 514
#define CACHEGAME_VERSION_BINARY 515
#define PM_DEFAULTSTEPHEIGHT 18

View File

@ -3478,12 +3478,12 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
case TF_H2_T4A4:
frames[0].defaultshader =
"{\n"
"cull disable\n"
"{\n"
"map $diffuse\n"
"blendfunc gl_one_minus_src_alpha gl_src_alpha\n"
"alphagen entity\n"
"rgbgen lightingDiffuse\n"
"cull disable\n"
"depthwrite\n"
"}\n"
"}\n";

View File

@ -3069,7 +3069,7 @@ 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 com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\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 mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\n"
#define HEX2CFG "set 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 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 com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\n"
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
@ -4332,10 +4332,11 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base
if (Sys_SteamHasFile(basepath, basepathlen, "quake 2", "baseq2/pak0.pak"))
return true;
}
else if (!strcmp(gamename, "hexen2") || !strcmp(gamename, "h2mp"))
else if (!strcmp(gamename, "hexen2") || !strcmp(gamename, "h2mp") || !strcmp(gamename, "portals"))
{
if (Sys_SteamHasFile(basepath, basepathlen, "hexen 2", "data/pak0.pak"))
return true;
gamename = "hexen2";
}
s = va("/usr/share/games/%s/", gamename);

View File

@ -88,6 +88,21 @@ void PR_Route_Visualise (void);
void PR_Route_Init (void);
#endif
//known progs versions...
enum
{
PROGHEADER_CRC_QW = 54730,
PROGHEADER_CRC_NQ = 5927,
PROGHEADER_CRC_PREREL = 26940, //prerelease
PROGHEADER_CRC_TENEBRAE = 32401, //tenebrae
PROGHEADER_CRC_H2 = 38488, //basic hexen2
PROGHEADER_CRC_H2MP = 26905, //hexen2 mission pack uses slightly different defs... *sigh*...
PROGHEADER_CRC_H2DEMO = 14046, //I'm guessing this is from the original release or something
PROGHEADER_CRC_CSQC = 22390,
PROGHEADER_CRC_CSQC_DP = 52195,
PROGHEADER_CRC_MENUQC = 10020
};
//pr_cmds.c builtins that need to be moved to a common.
void VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) LIKEPRINTF(2);
void QCBUILTIN PF_print (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);

View File

@ -97,6 +97,8 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain
hVM=NULL;
*fname = 0;
Con_DPrintf("Attempting to load native library: %s\n", name);
if (binroot)
{
if (!hVM && FS_NativePath(dllname_arch, FS_BINARYPATH, fname, sizeof(fname)))
@ -115,13 +117,13 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain
{
if (!hVM && FS_NativePath(va("%s_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname)))
{
Con_DPrintf("Loading native: %s\n", fname);
Con_DLPrintf(2, "Loading native: %s\n", fname);
hVM = Sys_LoadLibrary(fname, funcs);
}
if (!hVM && FS_NativePath(va("%s_%s"ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname)))
{
Con_DPrintf("Loading native: %s\n", fname);
Con_DLPrintf(2, "Loading native: %s\n", fname);
hVM = Sys_LoadLibrary(fname, funcs);
}
}
@ -135,14 +137,14 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain
if (!hVM)
{
snprintf (fname, sizeof(fname), "%s/%s", gpath, dllname_arch);
Con_DPrintf("Loading native: %s\n", fname);
Con_DLPrintf(2, "Loading native: %s\n", fname);
hVM = Sys_LoadLibrary(fname, funcs);
}
if (!hVM)
{
snprintf (fname, sizeof(fname), "%s/%s", gpath, dllname_anycpu);
Con_DPrintf("Loading native: %s\n", fname);
Con_DLPrintf(2, "Loading native: %s\n", fname);
hVM = Sys_LoadLibrary(fname, funcs);
}
}

View File

@ -104,8 +104,7 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority
thread = NULL;
}
pthread_attr_destroy(&attr);
#ifdef __USE_GNU
#if defined(DEBUG) && defined(__USE_GNU) && __GLIBC_PREREQ(2,12)
pthread_setname_np(*thread, name);
#endif
@ -133,7 +132,7 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority
}
pthread_attr_destroy(&attr);
#ifdef __USE_GNU
#if defined(DEBUG) && defined(__USE_GNU) && __GLIBC_PREREQ(2,12)
pthread_setname_np(*thread, name);
#endif

View File

@ -1950,7 +1950,7 @@ struct font_s *Font_LoadFont(const char *fontfilename, float vheight)
size_t lumpsize;
qbyte lumptype;
unsigned char *w = W_GetLumpName(fontfilename+4, &lumpsize, &lumptype);
if (!w || lumpsize != 5)
if (!w || lumpsize != 32*128 || lumptype != 'D')
{
Z_Free(f);
return NULL;

View File

@ -150,6 +150,7 @@ static struct
Cursor (*pXCreatePixmapCursor)(Display *display, Pixmap source, Pixmap mask, XColor *foreground_color, XColor *background_color, unsigned int x, unsigned int y);
Window (*pXCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes);
int (*pXDefineCursor)(Display *display, Window w, Cursor cursor);
int (*pXDeleteProperty)(Display *display, Window w, Atom property);
int (*pXDestroyWindow)(Display *display, Window w);
int (*pXFillRectangle)(Display *display, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height);
int (*pXFlush)(Display *display);
@ -213,6 +214,13 @@ static struct
qboolean dounicode;
XIC unicodecontext;
XIM inputmethod;
struct
{
Window source; //the source window to send dndFinished to.
Atom type; //the type of the data. usually text/uri-list.
Atom myprop; //the property on our window that we're copying the data to.
} dnd;
} x11;
static int X11_ErrorHandler(Display *dpy, XErrorEvent *e)
@ -237,6 +245,7 @@ static qboolean x11_initlib(void)
{(void**)&x11.pXCreatePixmapCursor, "XCreatePixmapCursor"},
{(void**)&x11.pXCreateWindow, "XCreateWindow"},
{(void**)&x11.pXDefineCursor, "XDefineCursor"},
{(void**)&x11.pXDeleteProperty, "XDeleteProperty"},
{(void**)&x11.pXDestroyWindow, "XDestroyWindow"},
{(void**)&x11.pXFillRectangle, "XFillRectangle"},
{(void**)&x11.pXFlush, "XFlush"},
@ -2353,26 +2362,150 @@ static void GetEvent(void)
Con_DPrintf("Got unknown x11wm message %s\n", protname);
x11.pXFree(protname);
}
#if 1
else if (!strcmp(name, "XdndEnter") && event.xclient.format == 32)
{
//check for text/uri-list
int i;
for (i = 2; i < 2+3; i++)
{
if (event.xclient.data.l[i])
{
char *t = x11.pXGetAtomName(vid_dpy, event.xclient.data.l[i]);
if (!strcmp(t, "text/uri-list")) //file list
x11.dnd.type = event.xclient.data.l[i];
x11.pXFree(t);
}
}
}
else if (!strcmp(name, "XdndPosition") && event.xclient.format == 32)
{
//Send XdndStatus
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = event.xclient.data.l[0];
xev.xclient.message_type = x11.pXInternAtom(vid_dpy, "XdndStatus", False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = vid_window; //so source can ignore it if stale
xev.xclient.data.l[1] = 1;
xev.xclient.data.l[2] = 0; //(x<<16)|y (should be in root coords)
xev.xclient.data.l[3] = 0; //(w<<16)|h
xev.xclient.data.l[4] = x11.pXInternAtom (vid_dpy, "XdndActionCopy", False);
x11.pXSendEvent(vid_dpy, xev.xclient.window, False, 0, &xev);
}
else if (!strcmp(name, "XdndLeave") && event.xclient.format == 32)
{
if (x11.dnd.source == event.xclient.data.l[0])
{
x11.dnd.source = None;
x11.dnd.type = None;
}
}
else if (!strcmp(name, "XdndDrop") && event.xclient.format == 32)
{
Atom xa_XdndSelection = x11.pXInternAtom(vid_dpy, "XdndSelection", False);
Time t = CurrentTime;//event.xclient.data.l[2];
x11.dnd.myprop = x11.pXInternAtom(vid_dpy, "_FTE_dnd", False);
if (x11.pXGetSelectionOwner(vid_dpy, xa_XdndSelection) == event.xclient.data.l[0])
{
x11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop);
x11.pXConvertSelection(vid_dpy, xa_XdndSelection, x11.dnd.type, x11.dnd.myprop, vid_window, t);
}
}
#endif
else
Con_DPrintf("Got unknown x11 message %s\n", name);
x11.pXFree(name);
}
break;
#if 1
case SelectionRequest: //needed for copy-to-clipboard
case SelectionNotify:
//for drag-n-drop
if (event.xselection.selection == x11.pXInternAtom(vid_dpy, "XdndSelection", False) && x11.dnd.myprop != None)
{
Atom xa_string = x11.pXInternAtom(vid_dpy, "UTF8_STRING", false);
qboolean okay = false;
unsigned char *data;
Atom type;
int fmt;
unsigned long nitems;
unsigned long bytesleft;
if (x11.pXGetWindowProperty(vid_dpy, vid_window, x11.dnd.myprop, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data) == Success && data)
{
if (type == x11.dnd.type)
{
char *start, *end;
for (start = data; *start; )
{
for (end = start; *end && *end != '\r'; end++)
;
if (end != start)
Host_RunFile(start, end-start, NULL);
start = end;
while (*start == '\r' || *start == '\n')
start++;
}
okay = true;
x11.pXFree(data);
}
}
x11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop); //might be large, so don't force it to hang around.
//Send XdndFinished now
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = x11.dnd.source;
xev.xclient.message_type = x11.pXInternAtom(vid_dpy, "XdndFinished", False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = vid_window; //so source can ignore it if stale
xev.xclient.data.l[1] = (okay?1:0);
xev.xclient.data.l[2] = x11.pXInternAtom (vid_dpy, "XdndActionCopy", False);
x11.pXSendEvent(vid_dpy, xev.xclient.window, False, 0, &xev);
}
break;
case SelectionRequest: //needed for when another program tries pasting.
{
Atom xa_u8string = x11.pXInternAtom(vid_dpy, "UTF8_STRING", false); //explicitly UTF-8
Atom xa_l1string = x11.pXInternAtom(vid_dpy, "STRING", false); //explicitly 8859-1
Atom xa_text = x11.pXInternAtom(vid_dpy, "TEXT", false); //selection owner decides encoding (and we pick UTF-8)
Atom xa_targets = x11.pXInternAtom(vid_dpy, "TARGETS", false);
Atom xa_supportedtargets[] = {xa_u8string, xa_l1string, xa_text, xa_targets/*, xa_multiple, xa_timestamp*/};
memset(&rep, 0, sizeof(rep));
if (event.xselectionrequest.property == None)
event.xselectionrequest.property = x11.pXInternAtom(vid_dpy, "foobar2000", false);
if (event.xselectionrequest.property != None && event.xselectionrequest.target == xa_string)
{
if (event.xselectionrequest.property != None && event.xselectionrequest.target == xa_targets)
{ //TARGETS results in a list of accepted target types (atoms)
x11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 32, PropModeReplace, (void*)xa_supportedtargets, countof(xa_supportedtargets));
rep.xselection.property = event.xselectionrequest.property;
}
else if (event.xselectionrequest.property != None && (event.xselectionrequest.target == xa_u8string || event.xselectionrequest.target == xa_text))
{ //UTF8_STRING or TEXT (which we choose to use utf-8 as our charset)
x11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, (void*)clipboard_buffer, strlen(clipboard_buffer));
rep.xselection.property = event.xselectionrequest.property;
}
else if (event.xselectionrequest.property != None && event.xselectionrequest.target == xa_l1string)
{ //STRING == latin1. convert as needed.
char latin1[SYS_CLIPBOARD_SIZE];
char *in = clipboard_buffer;
int c = 0;
int err;
while (*in && c < sizeof(latin1))
{
int uc =utf8_decode(&err, in, &in);
if ((uc >= 0xe000 && uc <= 0xe100) && (uc&0x7f) >= 32)
uc = uc&0x7f; //don't do c0/c1 glyphs. otherwise treat as ascii.
else if (uc > 255 || err)
uc = '?'; //unsupported char
latin1[c++] = uc;
}
x11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, (void*)latin1, c);
rep.xselection.property = event.xselectionrequest.property;
}
else
{
{ //unsupported target. we need to let them know that we don't know what they're asking for.
rep.xselection.property = None;
}
rep.xselection.type = SelectionNotify;
@ -2518,7 +2651,7 @@ static void *X11VID_CreateCursorRGBA(const qbyte *rgbacursor, size_t w, size_t h
for (y = 0; y < h; y++)
for (x = 0; x < w; x++, rgbacursor+=4)
*dest++ = (rgbacursor[3]<<24)|(rgbacursor[0]<<16)|(rgbacursor[1]<<8)|(rgbacursor[0]<<0); //0xARGB
*dest++ = (rgbacursor[3]<<24)|(rgbacursor[0]<<16)|(rgbacursor[1]<<8)|(rgbacursor[2]<<0); //0xARGB
cursor = Z_Malloc(sizeof(*cursor));
*cursor = xcursor.ImageLoadCursor(vid_dpy, img);
@ -2994,6 +3127,10 @@ Window X_CreateWindow(qboolean override, XVisualInfo *visinfo, int x, int y, uns
/*make it visible*/
x11.pXMapWindow(vid_dpy, wnd);
//advertise support as a drag+drop target
prots[0] = 5; //version 5 is the most recent.
x11.pXChangeProperty(vid_dpy, wnd, x11.pXInternAtom(vid_dpy, "XdndAware", False), XA_ATOM, 32, PropModeReplace, (void*)prots, 1);
return wnd;
}
@ -3656,6 +3793,7 @@ char *Sys_GetClipboard(void)
{
if(vid_dpy)
{
//FIXME: we should query it using TARGETS first to see if UTF8_STRING etc is actually valid.
Atom xa_clipboard = x11.pXInternAtom(vid_dpy, "PRIMARY", false);
Atom xa_string = x11.pXInternAtom(vid_dpy, "UTF8_STRING", false);
Window clipboardowner = x11.pXGetSelectionOwner(vid_dpy, xa_clipboard);
@ -3666,8 +3804,15 @@ char *Sys_GetClipboard(void)
unsigned long nitems, bytesleft;
unsigned char *data;
x11.pXConvertSelection(vid_dpy, xa_clipboard, xa_string, None, vid_window, CurrentTime);
//FIXME: we should rewrite the clipboard pasting to invoke a callback once its available.
x11.pXFlush(vid_dpy);
x11.pXGetWindowProperty(vid_dpy, vid_window, xa_string, 0, 0, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data);
x11.pXSync(vid_dpy, False);
Sys_Sleep(0.3);
x11.pXSync(vid_dpy, False);
//and now we can actually read the data.
x11.pXGetWindowProperty(vid_dpy, vid_window, xa_string, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data);
return data;
}
@ -3691,6 +3836,10 @@ void Sys_SaveClipboard(char *text)
{
Atom xa_clipboard = x11.pXInternAtom(vid_dpy, "PRIMARY", false);
x11.pXSetSelectionOwner(vid_dpy, xa_clipboard, vid_window, CurrentTime);
//Set both clipboards. Because x11 is kinda annoying.
xa_clipboard = x11.pXInternAtom(vid_dpy, "CLIPBOARD", false);
x11.pXSetSelectionOwner(vid_dpy, xa_clipboard, vid_window, CurrentTime);
}
}
#endif

View File

@ -77,7 +77,7 @@ void R_SetSky(const char *sky)
tex.reflectcube = R_LoadHiResTexture(sky, "env:gfx/env", IF_LOADNOW|IF_CUBEMAP|IF_CLAMP);
if (tex.reflectcube->width)
{
forcedsky = R_RegisterShader(va("skybox_%s", sky), 0, "{\nsort sky\nprogram defaultskybox\n{\nmap \"$cube:$reflectcube\"\ntcgen skybox\n}\nsurfaceparms nodlight\nsurfaceparms sky\n}");
forcedsky = R_RegisterShader(va("skybox_%s", sky), 0, "{\nsort sky\nprogram defaultskybox\n{\nmap \"$cube:$reflectcube\"\ntcgen skybox\n}\nsurfaceparm nodlight\nsurfaceparm sky\n}");
R_BuildDefaultTexnums(&tex, forcedsky);
return;
}
@ -85,7 +85,7 @@ void R_SetSky(const char *sky)
//crappy old path that I still need to fix up a bit
//unlike cubemaps, this works on gl1.1/gles1, and also works with the different faces as different sizes.
forcedsky = R_RegisterShader(shadername, 0, va("{\nsort sky\nskyparms \"%s\" 512 -\nsurfaceparms nodlight\n}", sky));
forcedsky = R_RegisterShader(shadername, 0, va("{\nsort sky\nskyparms \"%s\" 512 -\nsurfaceparm nodlight\n}", sky));
//check that we actually got some textures.
//we accept the skybox if even 1 face is valid.
//we ignore the replacement only request if all are invalid.

View File

@ -10,6 +10,8 @@ struct edict_s;
#define NOENDIAN
#endif
#define qcc_iswhite(c) ((c) == ' ' || (c) == '\r' || (c) == '\n' || (c) == '\t' || (c) == '\v')
pbool ED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, int fldtype, char *s);
/*
@ -1887,7 +1889,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b
int header_crc;
//if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like
int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend))
int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline))
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
const char *datastart;
@ -1924,6 +1926,51 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
while(1)
{
datastart = file;
if (extendedterm)
{
//skip simple leading whitespace
while (qcc_iswhite(*file))
file++;
if (file[0] == '/' && file[1] == '*')
{ //looks like we have a hidden extension.
file+=2;
for(;;)
{
//skip to end of line
if (!*file)
break; //unexpected EOF
else if (file[0] == '*' && file[1] == '/')
{ //end of comment
file+=2;
break;
}
else if (*file != '\n')
{
file++;
continue;
}
file++; //skip past the \n
while (*file == ' ' || *file == '\t')
file++; //skip leading indentation
if (file[0] == '*' && file[1] == '/')
{ //end of comment
file+=2;
break;
}
else if (*file == '/')
continue; //embedded comment. ignore the line. not going to do nested comments, because those are not normally valid anyway, just C++-style inside C-style.
else if (extendedterm(ppf, ctx, &file))
; //found a term we recognised
else
; //unknown line, but this is a comment so whatever
}
continue;
}
}
file = QCC_COM_Parse(file);
if (file == NULL)
break; //finished reading file
@ -1976,8 +2023,8 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
externs->entspawn((struct edict_s *) ed, true);
file = ED_ParseEdict(progfuncs, file, ed);
if (callback)
callback(ppf, (struct edict_s *)ed, ctx, datastart, file);
if (entspawned)
entspawned(ppf, (struct edict_s *)ed, ctx, datastart, file);
}
else if (!strcmp(qcc_token, "progs"))
{
@ -2247,8 +2294,11 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
externs->entspawn((struct edict_s *) ed, true);
file = ED_ParseEdict(progfuncs, file, ed);
callback(ppf, (struct edict_s *)ed, ctx, datastart, file);
if (entspawned)
entspawned(ppf, (struct edict_s *)ed, ctx, datastart, file);
}
else if (extendedterm && extendedterm(ppf, ctx, &datastart))
file = datastart;
else
Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0);
}

View File

@ -289,7 +289,7 @@ int PDECL Comp_Continue(pubprogfuncs_t *progfuncs);
pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, char *key);
char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, char *key);
char *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, size_t *size, size_t maxsize, int mode);
int PDECL PR_LoadEnts(pubprogfuncs_t *progfuncs, const char *file, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend));
int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline));
char *PDECL PR_SaveEnt (pubprogfuncs_t *progfuncs, char *buf, size_t *size, size_t maxsize, struct edict_s *ed);
struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *progfuncs, const char *buf, size_t *size, struct edict_s *ed);
void PDECL PR_StackTrace (pubprogfuncs_t *progfuncs, int showlocals);

View File

@ -122,7 +122,10 @@ struct pubprogfuncs_s
void (PDECL *ED_Print) (pubprogfuncs_t *prinst, struct edict_s *ed);
char *(PDECL *save_ents) (pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, int mode); //dump the entire progs info into one big self allocated string
int (PDECL *load_ents) (pubprogfuncs_t *prinst, const char *s, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend)); //restore the entire progs state (or just add some more ents) (returns edicts ize)
int (PDECL *load_ents) (pubprogfuncs_t *prinst, const char *s, void *ctx,
void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend),
pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline)
); //restore the entire progs state (or just add some more ents) (returns edicts ize)
char *(PDECL *saveent) (pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, struct edict_s *ed); //will save just one entities vars
struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, const char *buf, size_t *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed)
@ -280,7 +283,7 @@ typedef union eval_s
#define ED_Free(pf, ed) (*pf->EntFree) (pf, ed)
#define ED_Clear(pf, ed) (*pf->EntClear) (pf, ed)
#define PR_LoadEnts(pf, s, ctx, cb) (*pf->load_ents) (pf, s, ctx, cb)
#define PR_LoadEnts(pf, s, ctx, entcb, extcb) (*pf->load_ents) (pf, s, ctx, entcb, extcb)
#define PR_SaveEnts(pf, buf, size, maxsize, mode) (*pf->save_ents) (pf, buf, size, maxsize, mode)
#if 0//def _DEBUG

View File

@ -1307,7 +1307,7 @@ static void PR_ApplyCompilation_f (void)
PR_RegisterFields();
sv.world.edict_size=PR_InitEnts(svprogfuncs, sv.world.max_edicts);
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, s, NULL, NULL);
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, s, NULL, NULL, NULL);
PR_LoadGlabalStruct(false);
@ -1555,6 +1555,35 @@ static void PR_FallbackSpawn_Misc_Model(pubprogfuncs_t *progfuncs, edict_t *self
G_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self);
PF_makestatic(progfuncs, pr_globals);
}
static void PR_FallbackSpawn_Func_Detail(pubprogfuncs_t *progfuncs, edict_t *self)
{
void *pr_globals;
eval_t *val;
if (sv.world.worldmodel && sv.world.worldmodel->type==mod_brush && sv.world.worldmodel->fromgame == fg_quake3)
{ //on q3bsp, these are expected to be handled directly by q3map2, but it doesn't always strip it.
ED_Free(progfuncs, self);
return;
}
if (!self->v->model && (val = progfuncs->GetEdictFieldValue(progfuncs, self, "mdl", ev_string, NULL)))
self->v->model = val->string;
if (!*PR_GetString(progfuncs, self->v->model)) //must have a model, because otherwise various things will assume its not valid at all.
progfuncs->SetStringField(progfuncs, self, &self->v->model, "*null", true);
self->v->solid = SOLID_BSP;
self->v->movetype = MOVETYPE_PUSH;
//make sure the model is precached, to avoid errors.
pr_globals = PR_globals(progfuncs, PR_CURRENT);
G_INT(OFS_PARM0) = self->v->model;
PF_precache_model(progfuncs, pr_globals);
pr_globals = PR_globals(progfuncs, PR_CURRENT);
G_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self);
G_INT(OFS_PARM1) = self->v->model;
PF_setmodel(progfuncs, pr_globals);
}
struct spawnents_s
{
int killonspawnflags;
@ -1672,6 +1701,13 @@ static void PDECL PR_DoSpawnInitialEntity(pubprogfuncs_t *progfuncs, struct edic
}
else if (!strcmp(eclassname, "misc_model"))
PR_FallbackSpawn_Misc_Model(progfuncs, ed);
//func_detail+func_group are for compat with ericw-tools, etc.
else if (!strcmp(eclassname, "func_detail_illusionary"))
PR_FallbackSpawn_Misc_Model(progfuncs, ed);
else if (!strcmp(eclassname, "func_detail") || !strcmp(eclassname, "func_detail_wall") || !strcmp(eclassname, "func_detail_fence"))
PR_FallbackSpawn_Func_Detail(progfuncs, ed);
else if (!strcmp(eclassname, "func_group"))
PR_FallbackSpawn_Func_Detail(progfuncs, ed);
else
{
//only warn on the first occurence of the classname, don't spam.
@ -1757,7 +1793,7 @@ void PR_SpawnInitialEntities(const char *file)
ctx.fulldata = PR_FindGlobal(svprogfuncs, "__fullspawndata", PR_ANY, NULL);
if (svprogfuncs)
sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, &ctx, PR_DoSpawnInitialEntity);
sv.world.edict_size = PR_LoadEnts(svprogfuncs, file, &ctx, PR_DoSpawnInitialEntity, NULL);
else
sv.world.edict_size = 0;
}
@ -4243,7 +4279,7 @@ void QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars
#ifdef NQPROT
//DPP7's network protocol depends upon the ordering of these from an external file. unreliable, but if we're meant to be compatible then we need to at least pretend.
if (!sv.strings.particle_precache[1] && sv_listen_dp.ival)
if (!sv.strings.particle_precache[1] && (sv_listen_dp.ival || !strncmp(s, "effectinfo.", 11)))
COM_Effectinfo_Enumerate(SV_ParticlePrecache_Add);
#endif
@ -11764,10 +11800,12 @@ void PR_DumpPlatform_f(void)
*/
{"CSQC_Init", "void(float apilevel, string enginename, float engineversion)", CS, "Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon."},
{"CSQC_WorldLoaded", "void()", CS, "Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp."},
{"CSQC_WorldLoaded", "void()", CS, "Called after the server's model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp (via getentitytoken)."},
{"CSQC_Shutdown", "void()", CS, "Specifies that the csqc is going down. Save your persistant settings here."},
{"CSQC_UpdateView", "void(float vwidth, float vheight, float notmenu)", CS, "Called every single video frame. The CSQC is responsible for rendering the entire screen."},
{"CSQC_UpdateViewLoading", "void(float vwidth, float vheight, float notmenu)", CS, "Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it."},
{"CSQC_DrawHud", "void(vector viewsize, float scoresshown)", CS, "Part of simple csqc, called after drawing the 3d view whenever CSQC_UpdateView is not defined."},
{"CSQC_DrawScores", "void(vector viewsize, float scoresshown)", CS, "Part of simple csqc, called after CSQC_DrawHud whenever CSQC_UpdateView is not defined, and when there are no menus/console active."},
{"CSQC_Parse_StuffCmd", "void(string msg)", CS, "Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely."},
{"CSQC_Parse_CenterPrint", "float(string msg)", CS, "Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint."},
{"CSQC_Parse_Damage", "float(float save, float take, vector inflictororg)", CS, "Called as a result of player.dmg_save or player.dmg_take being set on the server.\nReturn true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing.\nYou can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time."},

View File

@ -517,7 +517,9 @@ static edict_t *QDECL Q1QVMPF_EntAlloc(pubprogfuncs_t *pf, pbool object, size_t
return (struct edict_s *)e;
}
static int QDECL Q1QVMPF_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend))
static int QDECL Q1QVMPF_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx,
void (PDECL *ent_callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend),
pbool (PDECL *ext_callback)(pubprogfuncs_t *pf, void *ctx, const char **str))
{
//the qvm calls the spawn functions itself.
//no saved-games.

View File

@ -2,6 +2,7 @@
#include "pr_common.h"
#if !defined(CLIENTONLY) && defined(SAVEDGAMES)
#define CACHEGAME_VERSION_DEFAULT CACHEGAME_VERSION_VERBOSE
extern cvar_t skill;
extern cvar_t deathmatch;
@ -77,8 +78,94 @@ void SV_SavegameComment (char *text, size_t textsize)
}
#ifndef QUAKETC
pbool SV_Legacy_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr)
{
char token[8192];
com_tokentype_t tt;
const char *l = *ptr;
size_t idx;
if (l[0] == 's' && l[1] == 'v' && l[2] == '.')
l += 3; //DPism
do
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);
} while(tt == TTP_LINEENDING);
if (tt != TTP_RAWTOKEN)return false;
if (!strcmp(token, "lightstyle") || !strcmp(token, "lightstyles"))
{ //lightstyle N "STYLESTRING" 1 1 1
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
idx = atoi(token);
if (idx >= countof(sv.strings.lightstyles))
return false; //unsupported index.
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
if (sv.strings.lightstyles[idx])
Z_Free((char*)sv.strings.lightstyles[idx]);
sv.strings.lightstyles[idx] = Z_StrDup(token);
sv.lightstylecolours[idx][0] = sv.lightstylecolours[idx][1] = sv.lightstylecolours[idx][2] = 1.0;
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
sv.lightstylecolours[idx][0] = atof(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
sv.lightstylecolours[idx][1] = atof(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
sv.lightstylecolours[idx][2] = atof(token);
}
else if (!strcmp(token, "model_precache") || !strcmp(token, "model"))
{ //model_precache N "MODELNAME"
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
idx = atoi(token);
if (!idx || idx >= countof(sv.strings.model_precache))
return false; //unsupported index.
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
sv.strings.model_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);
}
else if (!strcmp(token, "sound_precache") || !strcmp(token, "sound"))
{ //sound_precache N "MODELNAME"
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
idx = atoi(token);
if (!idx || idx >= countof(sv.strings.sound_precache))
return false; //unsupported index.
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
sv.strings.sound_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);
}
else if (!strcmp(token, "particle_precache"))
{ //particle_precache N "MODELNAME"
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
idx = atoi(token);
if (!idx || idx >= countof(sv.strings.particle_precache))
return false; //unsupported index.
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
sv.strings.particle_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);
}
else if (!strcmp(token, "buffer"))
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
//buffer = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
//count = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
return false;
}
else if (!strcmp(token, "bufstr"))
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
//buffer = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;
//idx = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;
return false;
}
else
return false;
*ptr = l;
return true;
}
//expects the version to have already been parsed
static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
static qboolean SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
{
//FIXME: Multiplayer save probably won't work with spectators.
char mapname[MAX_QPATH];
@ -101,11 +188,11 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
char *modelnames[MAX_PRECACHE_MODELS];
char *soundnames[MAX_PRECACHE_SOUNDS];
if (version != 667 && version != 5 && version != 6) //5 for NQ, 6 for ZQ/FQ
if (version != SAVEGAME_VERSION_FTE_LEG && version != SAVEGAME_VERSION_NQ && version != SAVEGAME_VERSION_QW)
{
VFS_CLOSE (f);
Con_TPrintf ("Unable to load savegame of version %i\n", version);
return;
return false;
}
VFS_GETS(f, str, sizeof(str)); //discard comment.
Con_Printf("loading legacy game from %s...\n", filename);
@ -131,7 +218,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
}
SV_SendMessagesToAll();
if (version == 5 || version == 6)
if (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)
{
slots = 1;
SV_UpdateMaxPlayers(1);
@ -163,7 +250,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
{
VFS_CLOSE(f);
Con_Printf ("Corrupted save game");
return;
return false;
}
SV_UpdateMaxPlayers(slots);
for (clnum = 0; clnum < sv.allocated_client_slots; clnum++) //work out which players we had when we saved, and hope they accepted the reconnect.
@ -196,7 +283,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
}
}
}
if (version == 5 || version == 6)
if (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)
{
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (Cvar_FindVar("skill"), atof(str));
@ -204,7 +291,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
Cvar_SetValue (Cvar_FindVar("coop"), 0);
Cvar_SetValue (Cvar_FindVar("teamplay"), 0);
if (version == 5)
if (version == SAVEGAME_VERSION_NQ)
{
progstype = PROG_NQ;
Cvar_Set (&pr_ssqc_progs, "progs.dat"); //NQ's progs.
@ -240,7 +327,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
{
VFS_CLOSE (f);
Con_TPrintf ("Couldn't load map\n");
return;
return false;
}
sv.allocated_client_slots = slots;
@ -285,7 +372,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
// load the edicts out of the savegame file
// the rest of the file is sent directly to the progs engine.
if (version == 5 || version == 6)
if (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)
;//Q_InitProgs(); //reinitialize progs entirly.
else
{
@ -322,7 +409,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
strcpy(file, "loadgame");
clnum=VFS_READ(f, file+8, filelen);
file[filelen+8]='\0';
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL);
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_Legacy_ExtendedSaveData);
BZ_Free(file);
PR_LoadGlabalStruct(false);
@ -332,7 +419,7 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
VFS_CLOSE(f);
//FIXME: DP saved games have some / *\nkey values\nkey values\n* / thing in them to save precaches and stuff
//FIXME: QSS+DP saved games have some / *\nkey values\nkey values\n* / thing in them to save precaches and stuff
World_ClearWorld(&sv.world, true);
@ -362,16 +449,17 @@ static void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
cl->playerclass = 0;
#endif
}
return true;
}
static void SV_LegacySavegame (const char *savename)
static qboolean SV_LegacySavegame (const char *savename)
{
size_t len;
char *s = NULL;
client_t *cl;
int clnum;
int version = SAVEGAME_VERSION;
int version = SAVEGAME_VERSION_FTE_LEG;
char native[MAX_OSPATH];
char name[MAX_QPATH];
@ -382,27 +470,26 @@ static void SV_LegacySavegame (const char *savename)
if (sv.state != ss_active)
{
Con_TPrintf("Can't apply: Server isn't running or is still loading\n");
return;
return false;
}
if (sv.allocated_client_slots != 1 || svs.clients->state != cs_spawned)
{
//we don't care about fte-format legacy.
Con_TPrintf("Unable to use legacy savegame format to save multiplayer games\n");
SV_Savegame_f();
return;
return false;
}
sprintf (name, "%s", savename);
COM_RequireExtension (name, ".sav", sizeof(name)); //do NOT allow .pak etc
if (!FS_NativePath(name, FS_GAMEONLY, native, sizeof(native)))
return;
return false;
Con_TPrintf (U8("Saving game to %s...\n"), native);
f = FS_OpenVFS(name, "wbp", FS_GAMEONLY);
if (!f)
{
Con_TPrintf ("ERROR: couldn't open %s.\n", name);
return;
return false;
}
//if there are 1 of 1 players connected
@ -412,9 +499,9 @@ static void SV_LegacySavegame (const char *savename)
if (s)
{
if (progstype == PROG_QW)
version = 6;
version = SAVEGAME_VERSION_QW;
else
version = 5;
version = SAVEGAME_VERSION_NQ;
}
}
@ -423,7 +510,7 @@ static void SV_LegacySavegame (const char *savename)
SV_SavegameComment (comment, sizeof(comment));
VFS_PRINTF(f, "%s\n", comment);
if (version != SAVEGAME_VERSION)
if (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)
{
//only 16 spawn parms.
for (i=0; i < 16; i++)
@ -467,31 +554,46 @@ static void SV_LegacySavegame (const char *savename)
s = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1);
VFS_PUTS(f, s);
VFS_PUTS(f, "\n");
/*
// DarkPlaces extended savegame
sv.lightstyles %i %s
sv.model_precache %i %s
sv.sound_precache %i %s
sv.buffer %i %i "string"
sv.bufstr %i %i "%s"
#if 1
/* Extended save info
** This should also be compatible with both DP and QSS.
** WARNING: this does NOT protect against models/sounds being precached in different/random orders (statics/baselines/ambients will be wrong).
** the only protection we get is from late precaches.
** theoretically the loader could make it work by rewriting the various tables, but that would not necessarily be reliable.
*/
VFS_PUTS(f, "/*\n");
VFS_PUTS(f, "// FTE extended savegame\n");
for (i=0 ; i < countof(sv.strings.lightstyles); i++)
{ //yes, repeat styles 0-63 again, for some reason, but only list ones that are not empty.
if (sv.strings.lightstyles[i])
VFS_PRINTF(f, "sv.lightstyles %i %s\n", i, sv.strings.lightstyles[i]);
}
for (i=1 ; i < countof(sv.strings.model_precache); i++)
{
if (sv.strings.model_precache[i])
VFS_PRINTF(f, "sv.model_precache %i %s\n", i, sv.strings.model_precache[i]);
}
for (i=1 ; i < countof(sv.strings.sound_precache); i++)
{
if (sv.strings.sound_precache[i])
VFS_PRINTF(f, "sv.lightstyles %i %s\n", i, sv.strings.sound_precache[i]);
}
// sv.buffer %i %i "string"
// sv.bufstr %i %i "%s"
VFS_PUTS(f, "*/\n");
#endif
svprogfuncs->parms->memfree(s);
VFS_CLOSE(f);
FS_FlushFSHashWritten(name);
Q_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));
return true;
}
#endif
#define CACHEGAME_VERSION_OLD 513
#define CACHEGAME_VERSION_VERBOSE 514
#define CACHEGAME_VERSION_BINARY 515
void SV_FlushLevelCache(void)
{
levelcache_t *cache;
@ -701,35 +803,83 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
VFS_GETS(f, str, sizeof(str));
version = atoi(str);
if (version != CACHEGAME_VERSION_OLD)
if (version != CACHEGAME_VERSION_OLD && version != CACHEGAME_VERSION_VERBOSE)
{
VFS_CLOSE (f);
Con_TPrintf ("Savegame is version %i, not %i\n", version, CACHEGAME_VERSION_OLD);
Con_TPrintf ("Savegame is version %i, not %i\n", version, CACHEGAME_VERSION_DEFAULT);
return false;
}
VFS_GETS(f, str, sizeof(str)); //comment
SV_SendMessagesToAll();
VFS_GETS(f, str, sizeof(str));
pt = atof(str);
if (version == CACHEGAME_VERSION_OLD)
{
VFS_GETS(f, str, sizeof(str));
pt = atof(str);
// this silliness is so we can load 1.06 save files, which have float skill values
VFS_GETS(f, str, sizeof(str));
current_skill = (int)(atof(str) + 0.1);
Cvar_Set (&skill, va("%i", current_skill));
// this silliness is so we can load 1.06 save files, which have float skill values
VFS_GETS(f, str, sizeof(str));
current_skill = (int)(atof(str) + 0.1);
Cvar_Set (&skill, va("%i", current_skill));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&deathmatch, atof(str));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&coop, atof(str));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&teamplay, atof(str));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&deathmatch, atof(str));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&coop, atof(str));
VFS_GETS(f, str, sizeof(str));
Cvar_SetValue (&teamplay, atof(str));
VFS_GETS(f, mapname, sizeof(mapname));
VFS_GETS(f, str, sizeof(str));
time = atof(str);
VFS_GETS(f, mapname, sizeof(mapname));
VFS_GETS(f, str, sizeof(str));
time = atof(str);
}
else
{
time = 0;
pt = PROG_UNKNOWN;
while (VFS_GETS(f, str, sizeof(str)))
{
char *s = str;
cvar_t *var;
s = COM_Parse(s);
if (!strcmp(com_token, "map"))
{ //map "foo": terminates the preamble.
COM_ParseOut(s, mapname, sizeof(mapname));
break;
}
else if (!strcmp(com_token, "cvar"))
{
s = COM_Parse(s);
var = Cvar_FindVar(com_token);
s = COM_Parse(s);
if (var)
Cvar_Set(var, com_token);
}
else if (!strcmp(com_token, "time"))
{
s = COM_Parse(s);
time = atof(com_token);
}
else if (!strcmp(com_token, "vmmode"))
{
s = COM_Parse(s);
if (!strcmp(com_token, "NONE")) pt = PROG_NONE;
else if (!strcmp(com_token, "QW")) pt = PROG_QW;
else if (!strcmp(com_token, "NQ")) pt = PROG_NQ;
else if (!strcmp(com_token, "H2")) pt = PROG_H2;
else if (!strcmp(com_token, "PREREL")) pt = PROG_PREREL;
else if (!strcmp(com_token, "TENEBRAE")) pt = PROG_TENEBRAE;
else if (!strcmp(com_token, "UNKNOWN")) pt = PROG_UNKNOWN;
else pt = PROG_UNKNOWN;
}
else
Con_TPrintf ("Unknown savegame directive %s\n", com_token);
}
}
//NOTE: This sets up the default baselines+statics+ambients.
//FIXME: if any model names changed, then we're screwed.
SV_SpawnServer (mapname, startspot, false, false);
sv.time = time;
if (svs.gametype != gametype)
@ -744,20 +894,6 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
return false;
}
// sv.paused = true; // pause until all clients connect
// sv.loadgame = true;
// load the light styles
VFS_GETS(f, str, sizeof(str));
numstyles = atoi(str);
if (numstyles > MAX_LIGHTSTYLES)
{
VFS_CLOSE (f);
Con_Printf ("load failed - invalid number of lightstyles\n");
return false;
}
// load the edicts out of the savegame file
// the rest of the file is sent directly to the progs engine.
@ -770,25 +906,39 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
PR_InitEnts(svprogfuncs, sv.world.max_edicts);
}
for (i = 0; i<MAX_LIGHTSTYLES ; i++)
{
if (sv.strings.lightstyles[i])
BZ_Free((void*)sv.strings.lightstyles[i]);
sv.strings.lightstyles[i] = NULL;
}
for (i=0 ; i<numstyles ; i++)
if (version == CACHEGAME_VERSION_OLD)
{
// load the light styles
VFS_GETS(f, str, sizeof(str));
sv.strings.lightstyles[i] = Z_StrDup(str);
}
for ( ; i<MAX_LIGHTSTYLES ; i++)
{
sv.strings.lightstyles[i] = Z_StrDup("");
}
numstyles = atoi(str);
if (numstyles > MAX_LIGHTSTYLES)
{
VFS_CLOSE (f);
Con_Printf ("load failed - invalid number of lightstyles\n");
return false;
}
for (i = 0; i<MAX_LIGHTSTYLES ; i++)
{
if (sv.strings.lightstyles[i])
BZ_Free((void*)sv.strings.lightstyles[i]);
sv.strings.lightstyles[i] = NULL;
}
modelpos = VFS_TELL(f);
LoadModelsAndSounds(f);
for (i=0 ; i<numstyles ; i++)
{
VFS_GETS(f, str, sizeof(str));
sv.strings.lightstyles[i] = Z_StrDup(str);
}
for ( ; i<MAX_LIGHTSTYLES ; i++)
{
sv.strings.lightstyles[i] = Z_StrDup("");
}
modelpos = VFS_TELL(f);
LoadModelsAndSounds(f);
}
else
modelpos = 0;
filepos = VFS_TELL(f);
filelen = VFS_GETLEN(f);
@ -797,7 +947,7 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
memset(file, 0, filelen+1);
VFS_READ(f, file, filelen);
file[filelen]='\0';
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL);
sv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, NULL, SV_Legacy_ExtendedSaveData);
BZ_Free(file);
progstype = pt;
@ -807,8 +957,11 @@ qboolean SV_LoadLevelCache(const char *savename, const char *level, const char *
pr_global_struct->time = sv.time = sv.world.physicstime = time;
sv.starttime = Sys_DoubleTime() - sv.time;
VFS_SEEK(f, modelpos);
LoadModelsAndSounds(f);
if (modelpos != 0)
{
VFS_SEEK(f, modelpos);
LoadModelsAndSounds(f);
}
VFS_CLOSE(f);
@ -918,7 +1071,7 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
int i;
char comment[SAVEGAME_COMMENT_LENGTH+1];
levelcache_t *cache;
int version = CACHEGAME_VERSION_OLD;
int version = CACHEGAME_VERSION_DEFAULT;
if (!sv.state)
return;
@ -1078,44 +1231,13 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
case PROG_TENEBRAE: mode = "TENEBRAE"; break;
case PROG_UNKNOWN: mode = "UNKNOWN"; break;
}
VFS_PRINTF (f, "vmmode %s\n", COM_QuotedString(mode, buf, sizeof(buf), false));
VFS_PRINTF (f, "skill %s\n", COM_QuotedString(skill.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "deathmatch %s\n", COM_QuotedString(deathmatch.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "coop %s\n", COM_QuotedString(coop.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "teamplay %s\n", COM_QuotedString(teamplay.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "map %s\n", COM_QuotedString(svs.name, buf, sizeof(buf), false));
VFS_PRINTF (f, "time %f\n", sv.time);
for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
if (sv.strings.lightstyles[i])
VFS_PRINTF (f, "lstyle %i %s %f %f %f\n", i, COM_QuotedString(sv.strings.lightstyles[i], buf, sizeof(buf), false), sv.lightstylecolours[i][0], sv.lightstylecolours[i][1], sv.lightstylecolours[i][2]);
for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
if (sv.strings.model_precache[i] && *sv.strings.model_precache[i])
VFS_PRINTF (f, "model %i %s\n", i, COM_QuotedString(sv.strings.model_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
if (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])
VFS_PRINTF (f, "sound %i %s\n", i, COM_QuotedString(sv.strings.sound_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_SSPARTICLESPRE ; i++)
if (sv.strings.particle_precache[i] && *sv.strings.particle_precache[i])
VFS_PRINTF (f, "particles %i %s\n", i, sv.strings.particle_precache[i]);
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
VFS_PRINTF (f, "vwep %i %s\n", i, COM_QuotedString(sv.strings.vw_model_precache[i], buf, sizeof(buf), false));
PR_Common_SaveGame(f, svprogfuncs, version >= CACHEGAME_VERSION_BINARY);
//FIXME: string buffers
//FIXME: hash tables
//FIXME: skeletal objects?
//FIXME: static entities
//FIXME: midi track
//FIXME: custom temp-ents?
//FIXME: pending uri_gets? (if only just to report fails on load)
//FIXME: routing calls?
//FIXME: sql queries?
//FIXME: frik files?
//FIXME: qc threads?
VFS_PRINTF (f, "entities\n");
VFS_PRINTF (f, "vmmode %s\n", COM_QuotedString(mode, buf, sizeof(buf), false));
VFS_PRINTF (f, "cvar skill %s\n", COM_QuotedString(skill.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "cvar deathmatch %s\n", COM_QuotedString(deathmatch.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "cvar coop %s\n", COM_QuotedString(coop.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "cvar teamplay %s\n", COM_QuotedString(teamplay.string, buf, sizeof(buf), false));
VFS_PRINTF (f, "time %f\n", sv.time);
VFS_PRINTF (f, "map %s\n", COM_QuotedString(svs.name, buf, sizeof(buf), false));
}
else
{
@ -1165,6 +1287,43 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
svprogfuncs->parms->memfree(s);
}
if (version >= CACHEGAME_VERSION_VERBOSE)
{
char buf[8192];
for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
if (sv.strings.lightstyles[i])
VFS_PRINTF (f, "lightstyle %i %s %f %f %f\n", i, COM_QuotedString(sv.strings.lightstyles[i], buf, sizeof(buf), false), sv.lightstylecolours[i][0], sv.lightstylecolours[i][1], sv.lightstylecolours[i][2]);
for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
if (sv.strings.model_precache[i] && *sv.strings.model_precache[i])
VFS_PRINTF (f, "model %i %s\n", i, COM_QuotedString(sv.strings.model_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
if (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])
VFS_PRINTF (f, "sound %i %s\n", i, COM_QuotedString(sv.strings.sound_precache[i], buf, sizeof(buf), false));
for (i=1 ; i<MAX_SSPARTICLESPRE ; i++)
if (sv.strings.particle_precache[i] && *sv.strings.particle_precache[i])
VFS_PRINTF (f, "particle %i %s\n", i, COM_QuotedString(sv.strings.particle_precache[i], buf, sizeof(buf), false));
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
if (sv.strings.vw_model_precache[i])
VFS_PRINTF (f, "vwep %i %s\n", i, COM_QuotedString(sv.strings.vw_model_precache[i], buf, sizeof(buf), false));
PR_Common_SaveGame(f, svprogfuncs, version >= CACHEGAME_VERSION_BINARY);
//FIXME: string buffers
//FIXME: hash tables
//FIXME: skeletal objects?
//FIXME: static entities
//FIXME: midi track
//FIXME: custom temp-ents?
//FIXME: pending uri_gets? (if only just to report fails on load)
//FIXME: routing calls?
//FIXME: sql queries?
//FIXME: frik files?
//FIXME: qc threads?
// portalblobsize = CM_WritePortalState(sv.world.worldmodel, &portalblob);
// VFS_WRITE(f, portalblob, portalblobsize);
}
VFS_CLOSE (f);
@ -1180,8 +1339,6 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
FS_FlushFSHashWritten(name);
}
#define FTESAVEGAME_VERSION 25000
//mapchange is true for Q2's map-change autosaves.
void SV_Savegame (const char *savename, qboolean mapchange)
{
@ -1209,14 +1366,6 @@ void SV_Savegame (const char *savename, qboolean mapchange)
levelcache_t *cache;
char *savefilename;
#ifndef QUAKETC
if (!sv_savefmt.ival && !mapchange)
{
SV_LegacySavegame(savename);
return;
}
#endif
if (!sv.state || sv.state == ss_clustermode)
{
Con_Printf("Server is not active - unable to save\n");
@ -1228,6 +1377,22 @@ void SV_Savegame (const char *savename, qboolean mapchange)
return;
}
#ifndef QUAKETC
{
int savefmt = sv_savefmt.ival;
if (!*sv_savefmt.string && (svs.gametype != GT_PROGS || progstype == PROG_H2 || svs.levcache))
savefmt = 1; //hexen2+q2/etc must not use the legacy format by default. can't use it when using any kind of hub system either (harder to detect upfront, which might give confused saved game naming but will at least work).
else
savefmt = sv_savefmt.ival;
if (!savefmt && !mapchange)
{
if (SV_LegacySavegame(savename))
return;
Con_Printf("Unable to use legacy saved game format\n");
}
}
#endif
switch(svs.gametype)
{
default:
@ -1265,7 +1430,7 @@ void SV_Savegame (const char *savename, qboolean mapchange)
return;
}
SV_SavegameComment(comment, sizeof(comment));
VFS_PRINTF (f, "%d\n", FTESAVEGAME_VERSION+svs.gametype);
VFS_PRINTF (f, "%d\n", SAVEGAME_VERSION_FTE_HUB+svs.gametype);
VFS_PRINTF (f, "%s\n", comment);
VFS_PRINTF(f, "%i\n", sv.allocated_client_slots);
@ -1285,9 +1450,31 @@ void SV_Savegame (const char *savename, qboolean mapchange)
VFS_PRINTF(f, "%s\n", cl->name);
if (*cl->name)
for (len = 0; len < NUM_SPAWN_PARMS; len++)
VFS_PRINTF(f, "%i (%f)\n", *(int*)&cl->spawn_parms[len], cl->spawn_parms[len]); //write ints as not everyone passes a float in the parms.
//write floats too so you can use it to debug.
{
if (1)
{
char tmp[65536];
VFS_PRINTF(f, "{\n");
for (len = 0; len < NUM_SPAWN_PARMS; len++)
VFS_PRINTF(f, "\tparm%i 0x%x //%.9g\n", len, *(int*)&cl->spawn_parms[len], cl->spawn_parms[len]); //write hex as not everyone passes a float in the parms.
VFS_PRINTF(f, "\tparm_string %s\n", COM_QuotedString(cl->spawn_parmstring?cl->spawn_parmstring:"", tmp, sizeof(tmp), false));
/*if (cl->spawninfo)
{
VFS_PRINTF(f, "\tspawninfo %s\n", COM_QuotedString(cl->spawninfo, tmp, sizeof(tmp), false));
VFS_PRINTF(f, "\tspawninfotime %9g\n", cl->spawninfotime);
}*/
VFS_PRINTF(f, "}\n"); //write ints as not everyone passes a float in the parms.
}
else
{
for (len = 0; len < NUM_SPAWN_PARMS; len++)
VFS_PRINTF(f, "%i (%f)\n", *(int*)&cl->spawn_parms[len], cl->spawn_parms[len]); //write ints as not everyone passes a float in the parms.
//write floats too so you can use it to debug.
//FIXME: spawn_parmstring
//FIXME: spawninfo[time] (for hexen2)
//FIXME: startspot...
}
}
}
InfoBuf_WriteToFile(f, &svs.info, NULL, 0);
@ -1414,6 +1601,8 @@ void SV_Savegame (const char *savename, qboolean mapchange)
//fixme
FS_FlushFSHashFull();
}
Q_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));
}
@ -1464,10 +1653,13 @@ void SV_Savegame_f (void)
}
#ifndef QUAKETC
if (!Q_strcasecmp(Cmd_Argv(0), "savegame_legacy"))
SV_LegacySavegame(savename);
else
{
if (SV_LegacySavegame(savename))
return;
Con_Printf("Unable to use legacy save format\n");
}
#endif
SV_Savegame(savename, false);
SV_Savegame(savename, false);
}
else
Con_Printf("%s: invalid number of arguments\n", Cmd_Argv(0));
@ -1527,7 +1719,8 @@ void SV_AutoSave(void)
#endif
}
void SV_Loadgame_f (void)
//Attempts to load a named saved game.
qboolean SV_Loadgame (const char *unsafe_savename)
{
levelcache_t *cache;
unsigned char str[MAX_LOCALINFO_STRING+1], *trim;
@ -1555,16 +1748,7 @@ void SV_Loadgame_f (void)
};
int bd,best;
#ifndef SERVERONLY
if (!Renderer_Started() && !isDedicated)
{
Cbuf_AddText(va("wait;%s %s\n", Cmd_Argv(0), Cmd_Args()), Cmd_ExecLevel);
return;
}
#endif
Q_strncpyz(savename, Cmd_Argv(1), sizeof(savename));
Q_strncpyz(savename, unsafe_savename, sizeof(savename));
if (!*savename || strstr(savename, ".."))
strcpy(savename, "quick");
@ -1583,7 +1767,7 @@ void SV_Loadgame_f (void)
if (!f)
{
Con_TPrintf ("ERROR: couldn't open %s.\n", filename);
return;
return false;
}
#if defined(MENU_DAT) && !defined(SERVERONLY)
@ -1592,18 +1776,23 @@ void SV_Loadgame_f (void)
VFS_GETS(f, str, sizeof(str)-1);
version = atoi(str);
if (version < FTESAVEGAME_VERSION || version >= FTESAVEGAME_VERSION+GT_MAX)
if (version < SAVEGAME_VERSION_FTE_HUB || version >= SAVEGAME_VERSION_FTE_HUB+GT_MAX)
{
#ifdef QUAKETC
VFS_CLOSE (f);
Con_TPrintf ("Unable to load savegame of version %i\n", version);
return false;
#else
SV_Loadgame_Legacy(filename, f, version);
if (SV_Loadgame_Legacy(filename, f, version))
{
Q_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));
return true;
}
return false;
#endif
return;
}
gametype = version - FTESAVEGAME_VERSION;
gametype = version - SAVEGAME_VERSION_FTE_HUB;
VFS_GETS(f, str, sizeof(str)-1);
#ifndef SERVERONLY
if (!cls.state)
@ -1676,6 +1865,32 @@ void SV_Loadgame_f (void)
for (len = 0; len < NUM_SPAWN_PARMS; len++)
{
VFS_GETS(f, str, sizeof(str)-1);
if (*str == '{')
{
while(VFS_GETS(f, str, sizeof(str)-1))
{
if (*str == '}')
break;
trim = COM_Parse(str);
if (!strcmp(com_token, "parm_string"))
{
COM_Parse(str);
cl->spawn_parmstring = Z_StrDup(com_token);
}
else if (!strncmp(com_token, "parm", 4) && (unsigned)atoi(com_token+4) < NUM_SPAWN_PARMS)
{
COM_Parse(str);
len = atoi(com_token+4);
if (!strncmp(com_token, "0x", 2))
*(int*)&cl->spawn_parms[len] = strtoul(com_token, NULL, 16);
else
cl->spawn_parms[len] = strtod(com_token, NULL);
}
else
Con_Printf("Unknown player data: %s\n", com_token);
}
break;
}
for (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)
*trim='\0';
for (trim = str; *trim <= ' ' && *trim; trim++)
@ -1802,5 +2017,21 @@ void SV_Loadgame_f (void)
sv.spawned_client_slots += loadzombies;
sv.autosave_time = sv.time + sv_autosave.value*60;
Q_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));
return true;
}
void SV_Loadgame_f (void)
{
#ifndef SERVERONLY
if (!Renderer_Started() && !isDedicated)
{
Cbuf_AddText(va("wait;%s %s\n", Cmd_Argv(0), Cmd_Args()), Cmd_ExecLevel);
return;
}
#endif
SV_Loadgame(Cmd_Argv(1));
}
#endif

View File

@ -140,6 +140,8 @@ typedef struct
char mapname[256]; // text description of the map
char modelname[MAX_QPATH]; // maps/<name>.bsp, for model_precache[0]
char loadgame_on_restart[MAX_QPATH]; //saved game to load on map_restart
world_t world;
union {
@ -866,12 +868,12 @@ typedef struct bannedips_s {
typedef enum {
GT_PROGS, //q1, qw, h2 are similar enough that we consider it only one game mode. (We don't support the h2 protocol)
GT_Q1QVM,
#ifdef VM_LUA
GT_LUA, //for the luls
#endif
GT_HALFLIFE,
GT_QUAKE2, //q2 servers run from a q2 game dll
GT_QUAKE3, //q3 servers run off the q3 qvm api
#ifdef VM_LUA
GT_LUA, //for the luls
#endif
GT_MAX
} gametype_e;
@ -879,6 +881,7 @@ typedef struct levelcache_s {
struct levelcache_s *next;
char *mapname;
gametype_e gametype;
unsigned char savedplayers[(MAX_CLIENTS+7)>>3]; //bitmask to say which players are actually stored in the cache. so that restarts can restore.
} levelcache_t;
#ifdef TCPCONNECT
@ -1577,6 +1580,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
void SV_Savegame_f (void);
void SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);
void SV_Loadgame_f (void);
qboolean SV_Loadgame (const char *unsafe_savename);
void SV_AutoSave(void);
void SV_FlushLevelCache(void);
extern cvar_t sv_autosave;

View File

@ -450,12 +450,29 @@ command from the console or progs.
quirks:
a leading '*' means new unit, meaning all old map state is flushed regardless of startspot
a '+' means 'set nextmap cvar to the following value and otherwise ignore, for q2 compat. only applies if there's also a '.' and the specified bsp doesn't exist, for q1 compat.
just a '.' is taken to mean 'restart'. parms are not changed from their current values, startspot is also unchanged.
just a '.' is taken to mean 'restart'. parms are not changed from their current values, startspot is also unchanged. Loads the last saved game instead when applicable.
'map' will change map, for most games. strips parms+serverflags+cache. note that NQ kicks everyone (NQ expects you to use changelevel for that).
variations:
'map' will change map, for most games. strips parms+serverflags+cache. note that vanilla NQ kicks everyone (NQ expects you to use changelevel for that).
'changelevel' will not flush the level cache, for h2 compat (won't save current level state in such a situation, as nq would prefer not)
'gamemap' will save the game to 'save0' after loading, for q2 compat
'spmap' is for q3 and sets 'gametype' to '2', otherwise identical to 'map'. all other map commands will reset it to '0' if its '2' at the time.
'map_restart' restarts the current map. Name is needed for q3 compat.
'restart' is an alias for 'map_restart'. Exists for NQ compat, but as an alias for QW mods that tried to use it for mod-specific things.
hexen2 fixme:
'restart restore' restarts the map, reloading from a saved game if applicable.
'restart' forgets the current map (potentially breaking the game). we don't care much for that behaviour (could make it a 'restart unit' I guess).
quake2:
'gamemap [*]foo.dm2[$spot][+nextserver]'
* == new unit
$ == start spot
+ == value for nextserver cvar (used for cinematics).
'map' is always a new unit.
quake:
+ is used in certain map names. * cannot be, but $ potentially could be.
======================
*/
void SV_Map_f (void)
@ -523,7 +540,9 @@ void SV_Map_f (void)
sv.mapchangelocked = false;
if (strcmp(level, ".")) //restart current
if (!strcmp(level, "."))
;//restart current
else
{
snprintf (expanded, sizeof(expanded), "maps/%s.bsp", level); // this function and the if statement below, is a quake bugfix which stopped a map called "dm6++.bsp" from loading because of the + sign, quake2 map syntax interprets + character as "intro.cin+base1.bsp", to play a cinematic then load a map after
if (!COM_FCheckExists (expanded))
@ -584,6 +603,14 @@ void SV_Map_f (void)
}
}
#ifdef SAVEDGAMES
if (isrestart && *sv.loadgame_on_restart && SV_Loadgame(sv.loadgame_on_restart))
{ //we managed to reload a saved game instead!
//this is required in order to keep hub state consistent (dying mid-map would require saved games to store both current and start of map(not to be confused with initial state, which would be trivial))
return;
}
#endif
// check to make sure the level exists
if (*level == '*')
{
@ -644,7 +671,7 @@ void SV_Map_f (void)
if (!exts[i])
{
// FTE is still a Quake engine so report BSP missing
snprintf (expanded, sizeof(expanded), exts[0], level);
snprintf (expanded, sizeof(expanded), exts[1], level);
Con_TPrintf ("Can't find %s\n", expanded);
#ifndef SERVERONLY
SCR_SetLoadingStage(LS_NONE);
@ -785,11 +812,9 @@ void SV_Map_f (void)
}
SCR_SetLoadingFile("spawnserver");
if (newunit || !startspot || cinematic
#ifdef SAVEDGAMES
|| !SV_LoadLevelCache(NULL, level, startspot, false)
if (newunit || !startspot || cinematic || !SV_LoadLevelCache(NULL, level, startspot, false))
#endif
)
{
if (waschangelevel && !startspot)
startspot = "";
@ -1951,6 +1976,12 @@ static void SV_Status_f (void)
if (!sv.strings.sound_precache[count])
break;
Con_Printf("sounds : %i/%i\n", count, MAX_PRECACHE_SOUNDS);
for (count = 1; count < MAX_SSPARTICLESPRE; count++)
if (!sv.strings.particle_precache[count])
break;
if (count!=1)
Con_Printf("particles : %i/%i\n", count, MAX_SSPARTICLESPRE);
}
Con_Printf("gamedir : %s\n", FS_GetGamedir(true));
if (sv.csqcdebug)
@ -2882,25 +2913,38 @@ void SV_ReallyEvilHack_f(void)
void SV_PrecacheList_f(void)
{
unsigned int i;
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
char *group = Cmd_Argv(1);
if (!*group || !strncmp(group, "vwep", 4))
{
if (sv.strings.vw_model_precache[i])
Con_Printf("vweap %u: %s\n", i, sv.strings.vw_model_precache[i]);
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
{
if (sv.strings.vw_model_precache[i])
Con_Printf("vwep %u: %s\n", i, sv.strings.vw_model_precache[i]);
}
}
for (i = 0; i < MAX_PRECACHE_MODELS; i++)
if (!*group || !strncmp(group, "model", 5))
{
if (sv.strings.model_precache[i])
Con_Printf("model %u: %s\n", i, sv.strings.model_precache[i]);
for (i = 0; i < MAX_PRECACHE_MODELS; i++)
{
if (sv.strings.model_precache[i])
Con_Printf("model %u: %s\n", i, sv.strings.model_precache[i]);
}
}
for (i = 0; i < MAX_PRECACHE_SOUNDS; i++)
if (!*group || !strncmp(group, "sound", 5))
{
if (sv.strings.sound_precache[i])
Con_Printf("sound %u: %s\n", i, sv.strings.sound_precache[i]);
for (i = 0; i < MAX_PRECACHE_SOUNDS; i++)
{
if (sv.strings.sound_precache[i])
Con_Printf("sound %u: %s\n", i, sv.strings.sound_precache[i]);
}
}
for (i = 0; i < MAX_SSPARTICLESPRE; i++)
if (!*group || !strncmp(group, "part", 4))
{
if (sv.strings.particle_precache[i])
Con_Printf("pticl %u: %s\n", i, sv.strings.particle_precache[i]);
for (i = 0; i < MAX_SSPARTICLESPRE; i++)
{
if (sv.strings.particle_precache[i])
Con_Printf("part %u: %s\n", i, sv.strings.particle_precache[i]);
}
}
}

View File

@ -898,8 +898,8 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
#ifndef SERVERONLY
cl.worldmodel = NULL;
r_worldentity.model = NULL;
if (0)
cls.state = ca_connected;
// if (0)
// cls.state = ca_connected;
Surf_PreNewMap();
#ifdef VM_CG
CG_Stop();

View File

@ -5685,7 +5685,7 @@ void SV_ExecInitialConfigs(char *defaultexec)
{
Cbuf_AddText("cvar_purgedefaults\n", RESTRICT_LOCAL); //reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values.
Cbuf_AddText("cvarreset *\n", RESTRICT_LOCAL); //reset all cvars to their current (engine) defaults
Cbuf_AddText("alias restart \"changelevel .\"\n",RESTRICT_LOCAL);
Cbuf_AddText("alias restart \"map_restart\"\n",RESTRICT_LOCAL);
Cbuf_AddText(va("sv_gamedir \"%s\"\n", FS_GetGamedir(true)), RESTRICT_LOCAL);

View File

@ -1032,7 +1032,7 @@ int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match,
dir = opendir(truepath);
if (!dir)
{
Con_DPrintf("Failed to open dir %s\n", truepath);
Con_DLPrintf((errno==ENOENT)?2:1, "Failed to open dir %s\n", truepath);
return true;
}
do

View File

@ -5925,7 +5925,7 @@ typedef struct
{
char *name;
void (*func) (void);
qboolean noqchandling;
int noqchandling;
} ucmd_t;
ucmd_t ucmds[] =
@ -5976,8 +5976,8 @@ ucmd_t ucmds[] =
{"ptrack", SV_PTrack_f}, //ZOID - used with autocam
{"snap", SV_NoSnap_f}, //cheat detection
{"enablecsqc", SV_EnableClientsCSQC, true},
{"disablecsqc", SV_DisableClientsCSQC, true},
{"enablecsqc", SV_EnableClientsCSQC, 2},
{"disablecsqc", SV_DisableClientsCSQC, 2},
{"vote", SV_Vote_f},
@ -6178,15 +6178,26 @@ void SV_ExecuteUserCommand (const char *s, qboolean fromQC)
for ( ; u->name ; u++)
if (!strcmp (Cmd_Argv(0), u->name) )
{
if (u->noqchandling==2)
{ //issue the command then let the QC handle it too
if (!fromQC)
{
if (u->func)
u->func();
PR_KrimzonParseCommand(s);
}
host_client = oldhost;
return;
}
if (!fromQC && !u->noqchandling)
if (PR_KrimzonParseCommand(s)) //KRIMZON_SV_PARSECLIENTCOMMAND has the opertunity to parse out certain commands.
if (PR_KrimzonParseCommand(s)) //KRIMZON_SV_PARSECLIENTCOMMAND has the opportunity to parse out certain commands.
{
host_client = oldhost;
return;
}
// SV_BeginRedirect (RD_CLIENT, host_client->language);
if (u->func)
u->func ();
u->func();
host_client = oldhost;
// SV_EndRedirect ();
return;

View File

@ -79,7 +79,7 @@ void World_Bullet_Init(void)
physics_bullet_enable = pCvar_GetNVFDG("physics_bullet_enable", "1", 0, "", "Bullet");
physics_bullet_maxiterationsperframe = pCvar_GetNVFDG("physics_bullet_maxiterationsperframe", "10", 0, "FIXME: should be 1 when CCD is working properly.", "Bullet");
physics_bullet_framerate = pCvar_GetNVFDG("physics_bullet_framerate", "60", 0, "", "Bullet");
pr_meshpitch = pCvar_GetNVFDG("r_meshpitch", "-1", 0, "", "Bullet");
pr_meshpitch = pCvar_GetNVFDG("r_meshpitch", "-1", 0, "", "Bullet");
}
void World_Bullet_Shutdown(void)
@ -1259,9 +1259,10 @@ static void World_Bullet_Frame_BodyFromEntity(world_t *world, wedict_t *ed)
ed->rbe.body.body = (void*)body;
//motion threshhold should be speed/physicsframerate.
//Threshhold enables CCD when the object moves faster than X
//FIXME: recalculate...
body->setCcdMotionThreshold((geomsize[0]+geomsize[1]+geomsize[2])*(4/3));
//radius should be the body's radius
//radius should be the body's radius, or smaller.
body->setCcdSweptSphereRadius((geomsize[0]+geomsize[1]+geomsize[2])*(0.5/3));
ctx->dworld->addRigidBody(body, ed->xv->dimension_solid, ed->xv->dimension_hit);