Added pm_pground cvar for compat with mvdsv/ezquake. Do not use it with NQ mods however, as the QC will interfere with the onground state (QW mods are okay).

Added Z_EXT_PF_ONGROUND + Z_EXT_PF_SOLID for compat (not enabled serverside due to conflicts with pext - just a define away).
Fixed bug with loading screens switching sizes part way through loading.
Added hexen2 rain effect.
Fix hexen2 model texture alphas not working.
Fix potential linux crash from excessively long stdin lines.
Added cl_rollalpha cvar.
Fixed quirk where the player would slide along the base of steep walls/slopes.
Tweaked PM_NudgePosition to be more precise, giving more reliable prediction.
Fixed fread qc builtin.
Tweaked random() builtin to bias slightly away from 0, so that nextthink=random()*foo; will never cause statue-monsters.
Check for GL_WEBGL_depth_texture instead of just GL_OES_depth_texture, to fix compressedTex2d errors in firefox.
Second attempt at blocking invariant keyword with mesa.
Use xrandr for gamma where possible. This prevents reading stale XF86 gamma ramps and restoring those invalid ramps when quitting.
Try to grab mouse pointers slightly faster in x11.
Don't call XIFreeDeviceInfo if XIQueryDevice returned NULL.
Document parm_string and startspot qc globals.
Fix possible infinite loop from physics frames.
QTV: stripped most of the old plugin code (because really, who has a browser that still supports either ActiveX or NPAPI). Fixed up emscripten port references.
QTV: fix bug with protocol extensions not being reported to viewers.
QTV: use binary websockets instead of text.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5419 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2019-03-01 22:39:30 +00:00
parent d398af48b2
commit 6c5de8e8b1
46 changed files with 671 additions and 457 deletions

View file

@ -4680,8 +4680,12 @@ void CLQW_ParsePlayerinfo (void)
flags = (unsigned short)MSG_ReadShort ();
if (cls.fteprotocolextensions & (PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS))
{
if (flags & PF_EXTRA_PFS)
flags |= MSG_ReadByte()<<16;
}
else
flags = (flags & 0x3fff) | ((flags & 0xc000)<<8);
state->flags = flags;
@ -4783,15 +4787,15 @@ void CLQW_ParsePlayerinfo (void)
state->gravitydir[2] = -1;
#ifdef PEXT_SCALE
if (flags & PF_SCALE && cls.fteprotocolextensions & PEXT_SCALE)
if ((flags & PF_SCALE) && (cls.fteprotocolextensions & PEXT_SCALE))
state->scale = (float)MSG_ReadByte()/50;
#endif
#ifdef PEXT_TRANS
if (flags & PF_TRANS && cls.fteprotocolextensions & PEXT_TRANS)
if ((flags & PF_TRANS) && (cls.fteprotocolextensions & PEXT_TRANS))
state->alpha = MSG_ReadByte();
#endif
#ifdef PEXT_FATNESS
if (flags & PF_FATNESS && cls.fteprotocolextensions & PEXT_FATNESS)
if ((flags & PF_FATNESS) && (cls.fteprotocolextensions & PEXT_FATNESS))
state->fatness = (float)MSG_ReadChar();
#endif
#ifdef PEXT_HULLSIZE
@ -4818,8 +4822,12 @@ void CLQW_ParsePlayerinfo (void)
}
//should be passed to player move func.
#endif
if (cls.z_ext & Z_EXT_PF_ONGROUND)
state->onground = !!(flags & PF_ONGROUND);
else
state->onground = false;
if (cls.fteprotocolextensions & PEXT_COLOURMOD && (flags & PF_COLOURMOD))
if ((cls.fteprotocolextensions & PEXT_COLOURMOD) && (flags & PF_COLOURMOD))
{
state->colourmod[0] = MSG_ReadByte();
state->colourmod[1] = MSG_ReadByte();
@ -4832,6 +4840,15 @@ void CLQW_ParsePlayerinfo (void)
state->colourmod[2] = 32;
}
//if we have no solidity info, guess.
if (!(cls.z_ext & Z_EXT_PF_SOLID))
{
if (cl.players[num].spectator || state->flags & PF_DEAD)
state->flags &= ~PF_SOLID;
else
state->flags |= PF_SOLID;
}
if (cls.z_ext & Z_EXT_PM_TYPE)
{
int pm_code;
@ -5753,9 +5770,8 @@ void CL_SetSolidPlayers (void)
{
if (!pplayer->active)
continue; // not present this frame
if (pplayer->flags & PF_DEAD)
continue; // dead players aren't solid
if (!(pplayer->flags & PF_SOLID))
continue;
memset(pent, 0, sizeof(physent_t));
VectorCopy(pplayer->origin, pent->origin);

View file

@ -2259,6 +2259,7 @@ void CL_CheckServerInfo(void)
cl.bunnyspeedcap = Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_bunnyspeedcap"));
movevars.slidefix = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_slidefix")) != 0);
movevars.airstep = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_airstep")) != 0);
movevars.pground = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_pground")) != 0);
movevars.stepdown = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_stepdown")) != 0);
movevars.walljump = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_walljump")));
movevars.ktjump = Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_ktjump"));

View file

@ -406,6 +406,7 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state
pmove.velocity[2] = 0;
}
pmove.onground = from->onground;
pmove.jump_msec = (cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec;
pmove.jump_held = from->jump_held;
pmove.waterjumptime = from->waterjumptime;
@ -778,7 +779,7 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st
memset(plstate, 0, sizeof(*plstate));
plstate->jump_held = jumpheld;
switch(state->u.q1.pmovetype)
switch(state->u.q1.pmovetype & 0x7f)
{
case MOVETYPE_NOCLIP:
if (cls.z_ext & Z_EXT_PM_TYPE_NEW)
@ -816,7 +817,10 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st
plstate->onground = onground;
}
else
{
VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity);
plstate->onground = !!(state->u.q1.pmovetype&128);
}
plstate->pm_type = pmtype;
plstate->viewangles[0] = SHORT2ANGLE(state->u.q1.vangle[0]);
@ -886,7 +890,7 @@ void CL_PredictEntityMovement(entity_state_t *estate, float age)
// cmd.forwardmove = 5000;
// cmd.msec = sin(realtime*6) * 128 + 128;
oldphysent = pmove.numphysent;
pmove.onground = true;
pmove.onground = startstate.onground;
CL_PredictUsercmd(0, estate->number, &startstate, &resultstate, &cmd); //uses player 0's maxspeed/grav...
pmove.numphysent = oldphysent;
@ -1236,6 +1240,7 @@ void CL_PredictMovePNum (int seat)
// Con_DPrintf(" propagate %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime);
CL_PredictUsercmd (seat, pv->viewentity, tostate, &tmp, &of->cmd[seat]);
next = &cl.inframes[(toframe+i) & UPDATE_MASK].playerstate[pv->playernum];
next->onground = tmp.onground;
next->jump_held = tmp.jump_held;
next->jump_msec = tmp.jump_msec;
VectorCopy(tmp.gravitydir, next->gravitydir);

View file

@ -243,6 +243,7 @@ cvar_t scr_loadingrefresh = CVARD("scr_loadingrefresh", "0", "Force redrawing of
cvar_t scr_showloading = CVAR("scr_showloading", "1");
//things to configure the legacy loading screen
cvar_t scr_loadingscreen_picture = CVAR("scr_loadingscreen_picture", "gfx/loading");
cvar_t scr_loadingscreen_aspect = CVARD("scr_loadingscreen_aspect", "0", "Controls the aspect of levelshot images.\n0: Use source image's aspect.\n1: Force 4:3 aspect (ignore image's aspect), for best q3 compat.\n2: Ignore aspect considerations and just smear it over the entire screen.");
cvar_t scr_loadingscreen_scale = CVAR("scr_loadingscreen_scale", "1");
cvar_t scr_loadingscreen_scale_limit = CVAR("scr_loadingscreen_scale_limit", "2");
@ -269,6 +270,7 @@ void CLSCR_Init(void)
Cvar_Register(&scr_loadingscreen_picture, cl_screengroup);
Cvar_Register(&scr_loadingscreen_scale, cl_screengroup);
Cvar_Register(&scr_loadingscreen_scale_limit, cl_screengroup);
Cvar_Register(&scr_loadingscreen_aspect, cl_screengroup);
Cvar_Register(&show_fps, cl_screengroup);
Cvar_Register(&show_fps_x, cl_screengroup);
Cvar_Register(&show_fps_y, cl_screengroup);
@ -1998,13 +2000,8 @@ void SCR_DrawLoading (qboolean opaque)
//int mtype = M_GameType(); //unused variable
y = vid.height/2;
if (*levelshotname)
{
pic = R2D_SafeCachePic (levelshotname);
if (!R_GetShaderSizes(pic, &w, &h, true))
w = h = 1;
R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h);
}
if (R2D_DrawLevelshot())
;
else if (opaque)
{
R2D_ImageColours(0, 0, 0, 1);

View file

@ -59,7 +59,7 @@ char *r_defaultimageextensions =
;
static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt);
static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);
cvar_t r_imageextensions = CVARCD("r_imageextensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load.");
cvar_t r_imageextensions = CVARCD("r_imageextensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which might exist on disk (note that this does not list all supported formats, only the extensions that should be searched for).");
cvar_t r_image_downloadsizelimit = CVARFD("r_image_downloadsizelimit", "131072", CVAR_NOTFROMSERVER, "The maximum allowed file size of images loaded from a web-based url. 0 disables completely, while empty imposes no limit.");
extern cvar_t scr_sshot_compression;
extern cvar_t gl_lerpimages;
@ -9104,7 +9104,7 @@ void Image_Init(void)
Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets);
Cmd_AddCommandD("r_imagelist", Image_List_f, "Prints out a list of the currently-known textures.");
Cmd_AddCommandD("r_imageformats", Image_Formats_f, "Prints out a list of the usable texture formats.");
Cmd_AddCommandD("r_imageformats", Image_Formats_f, "Prints out a list of the usable hardware pixel formats.");
}
//destroys all textures
void Image_Shutdown(void)

View file

@ -109,6 +109,7 @@ void R2D_FadeScreen (void);
void R2D_Font_Changed(void);
void R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque);
void R2D_EditorBackground (void);
qboolean R2D_DrawLevelshot(void);
void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic);
void R2D_Image2dQuad(vec2_t const*points, vec2_t const*texcoords, vec4_t const*rgba, mpic_t *pic);

View file

@ -3599,6 +3599,7 @@ static qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwa
}
#endif
//built-in particle effects are always favoured. This is annoying, but means the cvar value alone is enough to detect cheats.
for (i = 0; partset_list[i].name; i++)
{
if (!stricmp(name, partset_list[i].name))

View file

@ -3578,6 +3578,7 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo
pmove.safeorigin_known = false;
pmove.capsule = false; //FIXME
movevars.coordsize = cls.netchan.message.prim.coordsize;
if (ent->xv->gravity)
movevars.entgravity = ent->xv->gravity;
else if (csqc_playerseat >= 0 && cl.playerview[csqc_playerseat].playernum+1 == ent->xv->entnum)
@ -7428,6 +7429,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
movevars.ktjump = false;//pm_ktjump.value;
movevars.slidefix = true;//(pm_slidefix.value != 0);
movevars.airstep = true;//(pm_airstep.value != 0);
movevars.pground = true;
movevars.stepdown = true;
movevars.walljump = false;//(pm_walljump.value);
movevars.slidyslopes = false;//(pm_slidyslopes.value!=0);

View file

@ -312,7 +312,7 @@ extern cvar_t com_protocolversion;
extern cvar_t com_nogamedirnativecode;
extern cvar_t com_parseutf8;
#ifndef NOLEGACY
extern cvar_t com_parseezquake;
extern cvar_t ezcompat_markup;
#endif
extern cvar_t sys_ticrate;
extern cvar_t sys_nostdout;

View file

@ -1440,6 +1440,46 @@ void R2D_FadeScreen (void)
Sbar_Changed();
}
qboolean R2D_DrawLevelshot(void)
{
extern char levelshotname[];
extern cvar_t scr_loadingscreen_aspect;
if (*levelshotname)
{
shader_t *pic = R2D_SafeCachePic (levelshotname);
int w,h;
if (!R_GetShaderSizes(pic, &w, &h, true))
{
#ifdef Q3CLIENT
pic = R2D_SafeCachePic("menu/art/unkownmap");
if (!R_GetShaderSizes(pic, &w, &h, true))
#endif
w = h = 1;
}
switch(scr_loadingscreen_aspect.ival)
{
case 0: //use the source image's aspect
break;
case 1: //q3 assumed 4:3 resolutions, with power-of-two images. lame, but lets retain the aspect
w = 640;
h = 480;
break;
case 2: //just ignore aspect entirely and stretch the thing in hideous ways
w = vid.width;
h = vid.height;
break;
}
R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h);
//q3's noise.
pic = R2D_SafeCachePic("levelShotDetail");
if (R_GetShaderSizes(pic, &w, &h, true))
R2D_Image(0, 0, vid.width, vid.height, 0, 0, vid.width/256, vid.height/256, pic);
return true;
}
return false;
}
//crosshairs
#define CS_HEIGHT 8
#define CS_WIDTH 8

View file

@ -3386,6 +3386,24 @@ char *particle_set_h2part =
"flurry 32\n"
"}\n"
//eidolon's arena
"r_part ce_rain\n"
"{\n"
"texture \"particles/fteparticlefont.tga\"\n"
"tcoords 1 1 63 63 256 2 64\n"
"type texturedspark\n"
"count 1\n"
"scale 2\n"
"scalefactor 1\n"
"die 1\n"
"alpha 0.2\n"
"alphadelta 0\n"
"rgb 255 255 255\n"
"friction 0\n"
"blend add\n"
"veladd 1\n"
"gravity 0\n"
"}\n"
//this teleport effect is nothing like hexen2's. hopefully it'll be acceptable :s
//the down ring
@ -3422,7 +3440,6 @@ char *particle_set_h2part =
"}\n"
//h2part.ce_rain was not loaded
//h2part.ce_quake was not loaded
//h2part.ce_ghost was not loaded
//h2part.ce_teleporterbody_1 was not loaded

View file

@ -119,8 +119,10 @@ cvar_t r_drawflat = CVARAF ("r_drawflat", "0", "gl_textureless",
CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM);
cvar_t r_lightmap = CVARF ("r_lightmap", "0",
CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM);
cvar_t r_wireframe = CVARFD ("r_wireframe", "0",
CVAR_CHEAT, "Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted.");
cvar_t r_wireframe = CVARAFD ("r_wireframe", "0",
"r_showtris", CVAR_CHEAT, "Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted.");
cvar_t r_outline = CVARD ("gl_outline", "0", "Draw some stylised outlines.");
cvar_t r_outline_width = CVARD ("gl_outline_width", "0", "The width of those outlines.");
cvar_t r_wireframe_smooth = CVAR ("r_wireframe_smooth", "0");
cvar_t r_refract_fbo = CVARD ("r_refract_fbo", "1", "Use an fbo for refraction. If 0, just renders as a portal and uses a copy of the current framebuffer.");
cvar_t r_refractreflect_scale = CVARD ("r_refractreflect_scale", "0.5", "Use a different scale for refraction and reflection texturemaps. Because $reasons.");
@ -896,6 +898,8 @@ void Renderer_Init(void)
Cvar_Register (&r_telestyle, GRAPHICALNICETIES);
Cvar_Register (&r_wireframe, GRAPHICALNICETIES);
Cvar_Register (&r_wireframe_smooth, GRAPHICALNICETIES);
Cvar_Register (&r_outline, GRAPHICALNICETIES);
Cvar_Register (&r_outline_width, GRAPHICALNICETIES);
Cvar_Register (&r_refract_fbo, GRAPHICALNICETIES);
Cvar_Register (&r_refractreflect_scale, GRAPHICALNICETIES);
Cvar_Register (&r_postprocshader, GRAPHICALNICETIES);

View file

@ -868,6 +868,8 @@ char *Sys_ConsoleInput(void)
if (!fgets(text, sizeof(text), stdin))
return NULL;
nl = strchr(text, '\n');
if (!nl) //err? wut?
return NULL;
*nl = 0;
//Con_Printf("console input: %s\n", text);
@ -975,7 +977,10 @@ int main (int c, const char **v)
TL_InitLanguages(parms.binarydir);
noconinput = COM_CheckParm("-noconinput");
if (!isatty(STDIN_FILENO))
noconinput = !isPlugin; //don't read the stdin if its probably screwed (running in qtcreator seems to pipe stdout to stdin in an attempt to screw everything up).
else
noconinput = COM_CheckParm("-noconinput");
#ifndef __DJGPP__
if (!noconinput)
fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);

View file

@ -53,9 +53,10 @@ cvar_t vsec_scaley[SIDEVIEWS] = {CVAR("v2_scaley", "0.25"), CVAR("v3_scaley", "0
cvar_t vsec_yaw[SIDEVIEWS] = {CVAR("v2_yaw", "180"), CVAR("v3_yaw", "90"), CVAR("v4_yaw", "270"), CVAR("v5_yaw", "0")};
#endif
cvar_t cl_rollspeed = CVAR("cl_rollspeed", "200");
cvar_t cl_rollangle = CVARD("cl_rollangle", "2.0", "Controls how much the view should tilt while strafing.");
cvar_t v_deathtilt = CVAR("v_deathtilt", "1");
cvar_t cl_rollspeed = CVARD("cl_rollspeed", "200", "Controls the speed required to reach cl_rollangle's tilt.");
cvar_t cl_rollangle = CVARD("cl_rollangle", "2.0", "Controls the maximum view should tilt while strafing.");
cvar_t cl_rollalpha = CVARD("cl_rollalpha", "20", "Controls the speed at which the view rolls according to sideways movement.");
cvar_t v_deathtilt = CVARD("v_deathtilt", "1", "Specifies whether to tilt the view when dead.");
cvar_t cl_bob = CVARD("cl_bob","0.02", "Controls how much the camera position should bob up down as the player runs around.");
cvar_t cl_bobcycle = CVAR("cl_bobcycle","0.6");
@ -76,7 +77,7 @@ cvar_t v_ipitch_cycle = CVAR("v_ipitch_cycle", "1");
cvar_t v_iyaw_level = CVAR("v_iyaw_level", "0.3");
cvar_t v_iroll_level = CVAR("v_iroll_level", "0.1");
cvar_t v_ipitch_level = CVAR("v_ipitch_level", "0.3");
cvar_t v_idlescale = CVAR("v_idlescale", "0");
cvar_t v_idlescale = CVARD("v_idlescale", "0", "Enable swishing of the view (whether idle or otherwise). Often used for concussion effects.");
cvar_t crosshair = CVARF("crosshair", "1", CVAR_ARCHIVE);
cvar_t crosshaircolor = CVARF("crosshaircolor", "255 255 255", CVAR_ARCHIVE);
@ -84,8 +85,8 @@ cvar_t crosshairsize = CVARF("crosshairsize", "8", CVAR_ARCHIVE);
cvar_t cl_crossx = CVARF("cl_crossx", "0", CVAR_ARCHIVE);
cvar_t cl_crossy = CVARF("cl_crossy", "0", CVAR_ARCHIVE);
cvar_t crosshaircorrect = CVARF("crosshaircorrect", "0", CVAR_SEMICHEAT);
cvar_t crosshairimage = CVAR("crosshairimage", "");
cvar_t crosshaircorrect = CVARFD("crosshaircorrect", "0", CVAR_SEMICHEAT, "Moves the crosshair around to represent the impact point of a weapon positioned below the actual view position.");
cvar_t crosshairimage = CVARD("crosshairimage", "", "Enables the use of an external/custom crosshair image");
cvar_t crosshairalpha = CVAR("crosshairalpha", "1");
cvar_t gl_cshiftpercent = CVAR("gl_cshiftpercent", "100");
@ -1133,12 +1134,7 @@ void V_CalcViewRoll (playerview_t *pv)
side = V_CalcRoll (pv->simangles, pv->simvel);
adjspeed = fabs(cl_rollangle.value);
if (adjspeed<1)
adjspeed=1;
if (adjspeed>45)
adjspeed = 45;
adjspeed*=20;
adjspeed = cl_rollalpha.value * bound(1, fabs(cl_rollangle.value), 45);
if (side > pv->rollangle)
{
pv->rollangle += host_frametime * adjspeed;
@ -2544,6 +2540,7 @@ void V_Init (void)
Cvar_Register (&cl_rollspeed, VIEWVARS);
Cvar_Register (&cl_rollangle, VIEWVARS);
Cvar_Register (&cl_rollalpha, VIEWVARS);
Cvar_Register (&cl_bob, VIEWVARS);
Cvar_Register (&cl_bobcycle, VIEWVARS);
Cvar_Register (&cl_bobup, VIEWVARS);

View file

@ -885,7 +885,7 @@ static void Cmd_Echo_f (void)
#else
t = TP_ParseFunChars(t);
#ifndef NOLEGACY
Con_PrintFlags (t, (com_parseezquake.ival?PFS_EZQUAKEMARKUP:0), 0);
Con_PrintFlags (t, ((ezcompat_markup.ival>=2)?PFS_EZQUAKEMARKUP:0), 0);
#else
Con_PrintFlags (t, 0, 0);
#endif

View file

@ -126,8 +126,8 @@ cvar_t fs_gamename = CVARAFD("com_fullgamename", NULL, "fs_gamename", CVAR_NOSET
cvar_t com_protocolname = CVARAD("com_protocolname", NULL, "com_gamename", "The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers.");
cvar_t com_protocolversion = CVARAD("com_protocolversion", "3", NULL, "The protocol version used for dpmaster queries."); //3 by default, for compat with DP/NQ, even if our QW protocol uses different versions entirely. really it only matters for master servers.
cvar_t com_parseutf8 = CVARD("com_parseutf8", "1", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed.
#if !defined(NOLEGACY) && defined(HAVE_CLIENT)
cvar_t com_parseezquake = CVARD("com_parseezquake", "0", "Treat chevron chars from configs as a per-character flag. You should use this only for compat with nquake's configs.");
#if !defined(NOLEGACY)
cvar_t ezcompat_markup = CVARD("ezcompat_markup", "1", "Attempt compatibility with ezquake's text markup.0: disabled.\n1: Handle markup ampersand markup.\n2: Handle chevron markup (only in echo commands, for config compat, because its just too unreliable otherwise).");
#endif
cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active.");
cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.");
@ -3251,6 +3251,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
conchar_t *oldout = out;
#ifndef NOLEGACY
extern cvar_t dpcompat_console;
extern cvar_t ezcompat_markup;
if (flags & PFS_EZQUAKEMARKUP)
{
@ -3577,7 +3578,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
}
}
#ifndef NOLEGACY
else if (*str == '&' && str[1] == 'c' && !(flags & PFS_NOMARKUP))
else if (*str == '&' && str[1] == 'c' && !(flags & PFS_NOMARKUP) && ezcompat_markup.ival)
{
// ezQuake color codes
@ -3600,7 +3601,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
}
}
}
else if (*str == '&' && str[1] == 'r' && !(flags & PFS_NOMARKUP))
else if (*str == '&' && str[1] == 'r' && !(flags & PFS_NOMARKUP) && ezcompat_markup.ival)
{
//ezquake revert
ext = (COLOR_WHITE << CON_FGSHIFT) | (ext&~(CON_RICHFOREMASK|CON_RICHFORECOLOUR));
@ -3610,7 +3611,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
continue;
}
}
else if (str[0] == '=' && str[1] == '`' && str[2] == 'k' && str[3] == '8' && str[4] == ':' && !keepmarkup)
else if (str[0] == '=' && str[1] == '`' && str[2] == 'k' && str[3] == '8' && str[4] == ':' && !keepmarkup && ezcompat_markup.ival)
{
//ezquake compat: koi8 compat for crazy russian people.
//we parse for compat but don't generate (they'll see utf-8 from us).
@ -5758,8 +5759,8 @@ void COM_Init (void)
Cvar_Register (&gameversion_max, "Gamecode");
Cvar_Register (&com_nogamedirnativecode, "Gamecode");
Cvar_Register (&com_parseutf8, "Internationalisation");
#if !defined(NOLEGACY) && defined(HAVE_CLIENT)
Cvar_Register (&com_parseezquake, NULL);
#if !defined(NOLEGACY)
Cvar_Register (&ezcompat_markup, NULL);
#endif
Cvar_Register (&com_highlightcolor, "Internationalisation");
com_parseutf8.ival = 1;

View file

@ -3147,9 +3147,11 @@ const gamemode_info_t gamemode_info[] = {
//standard quake
{"-quake", "q1", "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},QCFG,{"id1", "qw", "*fte"}, "Quake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//alternative name, because fmf file install names are messy when a single name is used for registry install path.
{"-afterquake", NULL, "FTE-Quake",{"id1/pak0.pak", "id1/quake.rc"},QCFG,{"id1", "qw", "*fte"}, "Quake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-afterquake", NULL, "FTE-Quake",{"id1/pak0.pak", "id1/quake.rc"}, QCFG,{"id1", "qw", "*fte"}, "Quake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//netquake-specific quake that avoids qw/ with its nquake fuckups, and disables nqisms
{"-netquake", "nq", "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG,{"id1"}, "NetQuake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//because we can. quakespasm is hopefully close enough...
{"-fitz", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset spasm\n",{"id1"}, "NetQuake", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//because we can
{"-tenebrae", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},QCFG"fps_preset tenebrae\n",{"id1","qw","*fte"},"Tenebrae", "https://triptohell.info/downloadables.php" /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},

View file

@ -426,8 +426,7 @@ int PM_StepSlideMove (qboolean in_air)
VectorCopy (trace.endpos, pmove.origin);
}
//FIXME gravitydir
if ((in_air || movevars.slidefix) && originalvel[2] < 0)
if ((in_air || movevars.slidefix) && -DotProduct(pmove.gravitydir, original) < 0)
VectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0
PM_SlideMove ();
@ -867,7 +866,7 @@ void PM_AirMove (void)
else
blocked = PM_SlideMove ();
if (blocked & BLOCKED_FLOOR)
if (movevars.pground && (blocked & BLOCKED_FLOOR))
pmove.onground = true;
}
}
@ -949,9 +948,17 @@ void PM_CategorizePosition (void)
{
pmove.onground = false;
}
else
else if (!movevars.pground || pmove.onground)
{
trace = PM_PlayerTracePortals (pmove.origin, point, MASK_PLAYERSOLID, NULL);
if (!trace.startsolid && trace.fraction < 1 && -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL)
{ //if the trace hit a slope, slide down the slope to see if we can find ground below. this should fix the 'base-of-slope-is-slide' bug.
vec3_t bounce;
PM_ClipVelocity (pmove.gravitydir, trace.plane.normal, bounce, 2);
VectorMA(trace.endpos, 1-trace.fraction, bounce, point);
trace = PM_PlayerTracePortals (trace.endpos, point, MASK_PLAYERSOLID, NULL);
}
if (!trace.startsolid && (trace.fraction == 1 || -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL))
pmove.onground = false;
else
@ -1044,7 +1051,7 @@ void PM_CategorizePosition (void)
}
}
if (pmove.onground && pmove.pm_type != PM_FLY && pmove.waterlevel < 2)
if (!movevars.pground && pmove.onground && pmove.pm_type != PM_FLY && pmove.waterlevel < 2)
{
// snap to ground so that we can't jump higher than we're supposed to
if (!trace.startsolid && !trace.allsolid)
@ -1102,7 +1109,7 @@ static void PM_CheckJump (void)
// check for jump bug
// groundplane normal was set in the call to PM_CategorizePosition
if (-DotProduct(pmove.gravitydir, pmove.velocity) < 0 && DotProduct(pmove.velocity, groundplane.normal) < -0.1)
if (!movevars.pground && -DotProduct(pmove.gravitydir, pmove.velocity) < 0 && DotProduct(pmove.velocity, groundplane.normal) < -0.1)
{
// pmove.velocity is pointing into the ground, clip it
PM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1);
@ -1121,7 +1128,6 @@ static void PM_CheckJump (void)
}
pmove.jump_held = true; // don't jump again until released
pmove.jump_msec = pmove.cmd.msec;
}
@ -1215,19 +1221,21 @@ static void PM_NudgePosition (void)
vec3_t base;
int x, y, z;
int i;
static int sign[5] = {0, -1, 1, -2, 2};
static float sign[5] = {0, -1/8.0, 1/8.0, -2/8.0, 2/8.0};
VectorCopy (pmove.origin, base);
for (i=0 ; i<3 ; i++)
base[i] = MSG_FromCoord(MSG_ToCoord(base[i], 2), 2);
if (movevars.coordsize) for (i=0 ; i<3 ; i++)
base[i] = MSG_FromCoord(MSG_ToCoord(base[i], movevars.coordsize), movevars.coordsize); //higher precision or at least with more accurate rounding
else for (i=0 ; i<3 ; i++)
base[i] = ((int) (pmove.origin[i] * 8)) * 0.125; //legacy compat, which biases towards the origin.
// VectorCopy (base, pmove.origin);
//if we're moving, allow that spot without snapping to any grid
// if (pmove.velocity[0] || pmove.velocity[1] || pmove.velocity[2])
if (PM_TestPlayerPosition (pmove.origin, false))
return;
// if (PM_TestPlayerPosition (pmove.origin, false))
// return;
for (z=0 ; z<countof(sign) ; z++)
{
@ -1235,9 +1243,9 @@ static void PM_NudgePosition (void)
{
for (y=0 ; y<countof(sign) ; y++)
{
pmove.origin[0] = base[0] + (sign[x] * 1.0/8);
pmove.origin[1] = base[1] + (sign[y] * 1.0/8);
pmove.origin[2] = base[2] + (sign[z] * 1.0/8);
pmove.origin[0] = base[0] + sign[x];
pmove.origin[1] = base[1] + sign[y];
pmove.origin[2] = base[2] + sign[z];
if (PM_TestPlayerPosition (pmove.origin, false))
return;
}
@ -1251,8 +1259,8 @@ static void PM_NudgePosition (void)
{
for (y=0 ; y<3 ; y++)
{
pmove.origin[0] = base[0] + (sign[x] * 1.0/8);
pmove.origin[1] = base[1] + (sign[y] * 1.0/8);
pmove.origin[0] = base[0] + sign[x];
pmove.origin[1] = base[1] + sign[y];
pmove.origin[2] = base[2] + z;
if (PM_TestPlayerPosition (pmove.origin, false))
return;
@ -1443,7 +1451,7 @@ void PM_PlayerMove (float gamespeed)
// this is to make sure landing sound is not played twice
// and falling damage is calculated correctly
if (pmove.onground && -DotProduct(pmove.gravitydir, pmove.velocity) < -300
if (!movevars.pground && pmove.onground && -DotProduct(pmove.gravitydir, pmove.velocity) < -300
&& DotProduct(pmove.velocity, groundplane.normal) < -0.1)
{
PM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1);

View file

@ -117,6 +117,7 @@ typedef struct {
int walljump;
qboolean slidefix;
qboolean airstep;
qboolean pground;
qboolean stepdown;
qboolean slidyslopes;
int stepheight;

View file

@ -2358,12 +2358,22 @@ static int PF_fread_internal (pubprogfuncs_t *prinst, int fnum, char *buf, size_
return 0; //this just isn't ours.
}
if (pf_fopen_files[fnum].accessmode == FRIK_FILE_READ_DELAY)
{ //on first read, convert into a regular file.
pf_fopen_files[fnum].accessmode = FRIK_FILE_READ;
pf_fopen_files[fnum].data = BZ_Malloc(pf_fopen_files[fnum].len+1);
pf_fopen_files[fnum].data[pf_fopen_files[fnum].len] = 0;
pf_fopen_files[fnum].len = pf_fopen_files[fnum].bufferlen = VFS_READ(pf_fopen_files[fnum].file, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);
VFS_CLOSE(pf_fopen_files[fnum].file);
pf_fopen_files[fnum].file = NULL;
}
switch(pf_fopen_files[fnum].accessmode)
{
default:
PF_Warningf(prinst, "PF_fread: File not opened for reading\n");
return 0;
case FRIK_FILE_READ:
//UTF-8-FIXME: de-modify utf-8
if (pf_fopen_files[fnum].ofs + len > pf_fopen_files[fnum].len)
len = pf_fopen_files[fnum].len - pf_fopen_files[fnum].ofs;
@ -5050,7 +5060,9 @@ void QCBUILTIN PF_argescape(pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
void QCBUILTIN PF_random (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_FLOAT(OFS_RETURN) = (rand ()&0x7fff) / ((float)0x8000);
//don't return 1 (it would break array[random()*array.length];
//don't return 0 either, it would break the self.nextthink = time+random()*foo; lines in walkmonster_start, resulting rarely in statue-monsters.
G_FLOAT(OFS_RETURN) = (rand ()&0x7fff) / ((float)0x08000) + (0.5/0x08000);
}
//float(float number, float quantity) bitshift = #218;

View file

@ -753,7 +753,7 @@ typedef enum
#define FRIK_FILE_MMAP_READ 5 /*fgets returns a pointer. memory is not guarenteed to be released.*/
#define FRIK_FILE_MMAP_RW 6 /*fgets returns a pointer. file is written upon close. memory is not guarenteed to be released.*/
#define FRIK_FILE_READ_DELAY (7) /*internal special mode where the file is not read until the first read. this avoids extra slowness with xonotic*/
#define FRIK_FILE_READ_DELAY (7) /*internal special mode where the file is not read until the first read. this avoids extra slowness with xonotic (where it uses fopen to see if (large) binary file exists, resulting in large binary files getting decompressed repeatedly then discarded without reading)*/
#define MASK_DELTA 1
#define MASK_STDVIEWMODEL 2

View file

@ -92,15 +92,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define Z_EXT_JOIN_OBSERVE (1<<5) // server: "join" and "observe" commands are supported
// client: on-the-fly spectator <-> player switching supported
//#define Z_EXT_PF_ONGROUND (1<<6) // server: PF_ONGROUND is valid for all svc_playerinfo
#define Z_EXT_PF_ONGROUND (1<<6) // server: PF_ONGROUND is valid for all svc_playerinfo
#define Z_EXT_VWEP (1<<7)
//#define Z_EXT_PF_SOLID (1<<8) //conflicts with many FTE extensions.
#define Z_EXT_PF_SOLID (1<<8) //conflicts with many FTE extensions.
#ifdef QUAKESTATS
#define SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_VIEWHEIGHT|Z_EXT_SERVERTIME|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE|Z_EXT_VWEP)
#define SERVER_SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_VIEWHEIGHT|Z_EXT_SERVERTIME|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE/*|Z_EXT_PF_ONGROUND*/|Z_EXT_VWEP/*|Z_EXT_PF_SOLID*/)
#else
#define SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE|Z_EXT_VWEP)
#define SERVER_SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE/*|Z_EXT_PF_ONGROUND*/|Z_EXT_VWEP/*|Z_EXT_PF_SOLID*/)
#endif
#define CLIENT_SUPPORTED_Z_EXTENSIONS (SERVER_SUPPORTED_Z_EXTENSIONS|Z_EXT_PF_ONGROUND|Z_EXT_PF_SOLID)
#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake
@ -519,29 +520,30 @@ enum {
#define PF_GIB (1<<10) // offset the view height differently
//ZQuake.
#define PF_PMC_MASK ((1<<11) +\
(1<<12) +\
#define PF_PMC_MASK ((1<<11) | \
(1<<12) | \
(1<<13))
#ifdef PEXT_HULLSIZE
#define PF_HULLSIZE_Z (1<<14)
#define PF_HULLSIZE_Z (1<<14)
#endif
#define PF_EXTRA_PFS (1<<15)
#ifdef PEXT_SCALE
#define PF_SCALE (1<<16)
#define PF_SCALE (1<<16)
#endif
#ifdef PEXT_TRANS
#define PF_TRANS (1<<17)
#define PF_TRANS (1<<17)
#endif
#ifdef PEXT_FATNESS
#define PF_FATNESS (1<<18)
#endif
#define PF_COLOURMOD (1<<19)
#define PF_COLOURMOD (1<<19)
//#define PF_UNUSED (1<<20) //remember to faff with zext
//#define PF_UNUSED (1<<21) //remember to faff with zext
//note that if you add any more, you may need to change the check in the client so more can be parsed
#define PF_ONGROUND (1<<22) //or 14, depending on extensions... messy.
#define PF_SOLID (1<<23) //or 15, depending on extensions... messy.
#define PF_PMC_SHIFT 11
@ -1087,7 +1089,7 @@ typedef struct entity_state_s
struct
{
/*info to predict other players, so I don't get yelled at if fte were to stop supporting it*/
qbyte pmovetype;
qbyte pmovetype; //&128 means onground.
qbyte msec;
short vangle[3];

View file

@ -23,6 +23,8 @@ void DumpGLState(void);
extern cvar_t gl_overbright;
extern cvar_t r_tessellation;
extern cvar_t r_wireframe;
extern cvar_t r_outline;
extern cvar_t r_outline_width;
extern cvar_t r_refract_fbo;
extern texid_t missing_texture;
@ -3803,10 +3805,16 @@ static void BE_Program_Set_Attributes(const program_t *prog, struct programpermu
qglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_dir);
break;
case SP_E_L_MUL:
qglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_range);
if (shaderstate.mode == BEM_DEPTHDARK)
qglUniform3fvARB(ph, 1, vec3_origin);
else
qglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_range);
break;
case SP_E_L_AMBIENT:
qglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_avg);
if (shaderstate.mode == BEM_DEPTHDARK)
qglUniform3fvARB(ph, 1, vec3_origin);
else
qglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_avg);
break;
case SP_E_TIME:
@ -6283,6 +6291,19 @@ void GLBE_DrawWorld (batch_t **worldbatches)
#endif
}
if (r_outline.ival && !r_wireframe.ival && qglPolygonMode && qglLineWidth)
{
shaderstate.identitylighting = 0;
shaderstate.identitylightmap = 0;
BE_SelectMode(BEM_DEPTHDARK);
qglLineWidth (bound(0.1, r_outline_width.value, 2000.0));
qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
GLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);
BE_SelectMode(BEM_STANDARD);
qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
qglLineWidth (1);
}
shaderstate.identitylighting = 1;
RSpeedRemark();

View file

@ -152,7 +152,13 @@ void GL_SetupFormats(void)
if (GL_CheckExtension("GL_OES_texture_float"))
glfmtc(PTI_RGBA32F, GL_RGBA, GL_RGBA, GL_RGBA, GL_FLOAT, 0);
if (GL_CheckExtension("GL_OES_depth_texture"))
if (GL_CheckExtension("GL_WEBGL_depth_texture"))
{ //24bit is okay with this one.
glfmt(PTI_DEPTH16, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT);
glfmt(PTI_DEPTH24, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8);
glfmt(PTI_DEPTH32, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
}
else if (GL_CheckExtension("GL_OES_depth_texture") || GL_CheckExtension("GL_ANGLE_depth_texture"))
{ //16+32, not 24.
glfmt(PTI_DEPTH16, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT);
glfmt(PTI_DEPTH32, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);

View file

@ -48,7 +48,8 @@ extern int r_framecount; // used for dlight push checking
extern cvar_t gl_part_flame;
extern cvar_t r_bloom;
extern cvar_t r_wireframe_smooth;
extern cvar_t r_wireframe, r_wireframe_smooth;
extern cvar_t r_outline;
cvar_t gl_affinemodels = CVAR("gl_affinemodels","0");
cvar_t gl_finish = CVAR("gl_finish","0");
@ -629,7 +630,7 @@ void R_SetupGL (float stereooffset, int i)
if (!gl_config.gles && r_wireframe_smooth.modified)
{
r_wireframe_smooth.modified = false;
if (r_wireframe_smooth.ival)
if (r_wireframe_smooth.ival || (r_outline.ival && !r_wireframe.ival))
{
qglEnable(GL_LINE_SMOOTH);
if (qglHint)
@ -2047,7 +2048,13 @@ void GLR_RenderView (void)
vid.fbvheight = vid.fbpheight;
fmt = PTI_RGBA8;
if ((r_refdef.flags&RDF_SCENEGAMMA)||(vid.flags&(VID_SRGBAWARE|VID_FP16))||r_hdr_framebuffer.ival)
if (r_hdr_framebuffer.ival < 0)
{
fmt = -r_hdr_framebuffer.ival;
if (!sh_config.texfmt[fmt])
fmt = PTI_RGB565;
}
else if ((r_refdef.flags&RDF_SCENEGAMMA)||(vid.flags&(VID_SRGBAWARE|VID_FP16))||r_hdr_framebuffer.ival)
{ //gamma ramps really need higher colour precision, otherwise the entire thing looks terrible.
if (sh_config.texfmt[PTI_RGBA16F])
fmt = PTI_RGBA16F;

View file

@ -208,11 +208,9 @@ qboolean GLSCR_UpdateScreen (void)
scr_con_forcedraw = false;
if (noworld)
{
extern char levelshotname[];
//draw the levelshot or the conback fullscreen
if (*levelshotname)
R2D_ScalePic(0, 0, vid.width, vid.height, R2D_SafeCachePic (levelshotname));
if (R2D_DrawLevelshot())
;
else if (scr_con_current != vid.height)
R2D_ConsoleBackground(0, vid.height, true);
else

View file

@ -5594,16 +5594,17 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons
//make sure the noalpha thing is set properly.
switch(basefmt)
{
case TF_MIP4_P8:
case TF_MIP4_8PAL24:
case TF_MIP4_SOLID8:
case TF_SOLID8:
imageflags |= IF_NOALPHA;
//fallthrough
case TF_MIP4_8PAL24_T255:
if (!mipdata || !mipdata[0] || !mipdata[1] || !mipdata[2] || !mipdata[3])
basefmt = TF_SOLID8;
break;
default:
if (!mipdata || !mipdata[0] || !mipdata[1] || !mipdata[2] || !mipdata[3])
basefmt = TF_SOLID8;
break;
}
imageflags |= IF_MIPCAP;

View file

@ -170,6 +170,7 @@ void (APIENTRY *qglMultMatrixf) (const GLfloat *m);
void (APIENTRY *qglNewList) (GLuint list, GLenum mode);
//void (APIENTRY *qglOrtho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
void (APIENTRY *qglPolygonMode) (GLenum face, GLenum mode);
void (APIENTRY *qglLineWidth) (GLfloat width);
void (APIENTRY *qglPopMatrix) (void);
void (APIENTRY *qglPushMatrix) (void);
void (APIENTRY *qglReadBuffer) (GLenum mode);
@ -1088,7 +1089,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
if (Cvar_Get("gl_blacklist_invariant", "0", CVAR_VIDEOLATCH, "gl blacklists")->ival)
gl_config.blacklist_invariant = true;
else if (gl_config.arb_shader_objects && !gl_config_nofixedfunc &&
strstr(gl_renderer, " Mesa ") && gl_config.glversion <= 3.1 && Cvar_Get("gl_blacklist_mesa_invariant", "1", CVAR_VIDEOLATCH, "gl blacklists")->ival)
(strstr(gl_renderer, " Mesa ") || strstr(gl_version, " Mesa ")) && gl_config.glversion <= 3.1 && Cvar_Get("gl_blacklist_mesa_invariant", "1", CVAR_VIDEOLATCH, "gl blacklists")->ival)
{
gl_config.blacklist_invariant = true;
Con_Printf(CON_NOTICE "Mesa detected, disabling the use of glsl's invariant keyword. This will result in z-fighting. Use '+set gl_blacklist_mesa_invariant 0' on the commandline to reenable it.\n");
@ -3228,6 +3229,7 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
qglMultMatrixf = (void *)getglcore("glMultMatrixf");
// qglOrtho = (void *)getglcore("glOrtho");
qglPolygonMode = (void *)getglcore("glPolygonMode");
qglLineWidth = (void *)getglcore("glLineWidth");
qglPopMatrix = (void *)getglcore("glPopMatrix");
qglPushMatrix = (void *)getglcore("glPushMatrix");
qglReadBuffer = (void *)getglcore("glReadBuffer");

View file

@ -879,7 +879,12 @@ static qboolean XRandR_FindOutput(const char *name)
{
XRROutputInfo *primary = NULL;
int i;
xrandr.output = NULL;
if (!xrandr.canmodechange12)
return false;
if (!xrandr.res)
xrandr.res = xrandr.pGetScreenResources(vid_dpy, DefaultRootWindow(vid_dpy));
if (!xrandr.res)
return false;
if (!xrandr.outputs)
{
xrandr.outputs = Z_Malloc(sizeof(*xrandr.outputs) * xrandr.res->noutput);
@ -888,6 +893,12 @@ static qboolean XRandR_FindOutput(const char *name)
}
xrandr.output = NULL;
xrandr.crtc = None;
if (xrandr.origgamma)
{
xrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);
xrandr.pFreeGamma(xrandr.origgamma);
xrandr.origgamma = NULL;
}
if (xrandr.crtcinfo)
xrandr.pFreeCrtcInfo(xrandr.crtcinfo);
xrandr.crtcinfo = NULL;
@ -922,19 +933,15 @@ static qboolean XRandR_FindOutput(const char *name)
//(sets up crtc data
static qboolean XRandr_PickScreen(const char *devicename, int *x, int *y, int *width, int *height)
{
if (xrandr.canmodechange12)
if (xrandr.crtcinfo || XRandR_FindOutput(devicename))
{
xrandr.res = xrandr.pGetScreenResources(vid_dpy, DefaultRootWindow(vid_dpy));
if (XRandR_FindOutput(devicename))
{
XRRCrtcInfo *c = xrandr.crtcinfo;
*x = c->x;
*y = c->y;
*width = c->width;
*height = c->height;
Con_Printf("Found monitor %s %ix%i +%i,%i\n", xrandr.output->name, c->width, c->height, c->x, c->y);
return true;
}
XRRCrtcInfo *c = xrandr.crtcinfo;
*x = c->x;
*y = c->y;
*width = c->width;
*height = c->height;
Con_Printf("Found monitor %s %ix%i +%i,%i\n", xrandr.output->name, c->width, c->height, c->x, c->y);
return true;
}
return false;
}
@ -945,34 +952,27 @@ static void XRandR_SelectMode(const char *devicename, int *x, int *y, int *width
if (COM_CheckParm("-current"))
return;
if (xrandr.canmodechange12)
if (XRandR_FindOutput(devicename))
{
XRRCrtcInfo *c;
xrandr.res = xrandr.pGetScreenResources(vid_dpy, DefaultRootWindow(vid_dpy));
if (xrandr.res)
xrandr.crtcmode = XRandR_FindBestMode(*width, *height, rate);
c = xrandr.crtcinfo;
if (!*width || !*height || c->mode == xrandr.crtcmode->id)
{
if (XRandR_FindOutput(devicename))
{
xrandr.crtcmode = XRandR_FindBestMode(*width, *height, rate);
c = xrandr.crtcinfo;
if (!*width || !*height || c->mode == xrandr.crtcmode->id)
{
fullscreenflags |= FULLSCREEN_DESKTOP;
Con_Printf("XRRSetCrtcConfig not needed\n");
}
else if (xrandr.crtcmode && Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, c->timestamp, c->x, c->y, xrandr.crtcmode->id, c->rotation, c->outputs, c->noutput))
{
*x = c->x;
*y = c->y;
*width = xrandr.crtcmode->width;
*height = xrandr.crtcmode->height;
fullscreenflags |= FULLSCREEN_XRANDR | FULLSCREEN_XRANDRACTIVE;
Con_Printf("XRRSetCrtcConfig succeeded\n");
}
else
Con_Printf("XRRSetCrtcConfig failed\n");
}
fullscreenflags |= FULLSCREEN_DESKTOP;
Con_Printf("XRRSetCrtcConfig not needed\n");
}
else if (xrandr.crtcmode && Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, c->timestamp, c->x, c->y, xrandr.crtcmode->id, c->rotation, c->outputs, c->noutput))
{
*x = c->x;
*y = c->y;
*width = xrandr.crtcmode->width;
*height = xrandr.crtcmode->height;
fullscreenflags |= FULLSCREEN_XRANDR | FULLSCREEN_XRANDRACTIVE;
Con_Printf("XRRSetCrtcConfig succeeded\n");
}
else
Con_Printf("XRRSetCrtcConfig failed\n");
}
else if (xrandr.canmodechange11)
{
@ -1194,24 +1194,27 @@ static struct xidevinfo *XI2_GetDeviceInfo(int devid)
{
int devs;
XIDeviceInfo *dev = xi2.pXIQueryDevice(vid_dpy, xi2.ndeviceinfos, &devs);
if (devs==1)
if (dev)
{
int j;
for (j = 0; j < dev->num_classes; j++)
if (devs==1)
{
if (dev->classes[j]->sourceid == xi2.ndeviceinfos && dev->classes[j]->type == XIValuatorClass)
int j;
for (j = 0; j < dev->num_classes; j++)
{
XIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[j];
if (v->mode == XIModeAbsolute && v->number >= 0 && v->number < countof(xi2.deviceinfo[xi2.ndeviceinfos].axis))
if (dev->classes[j]->sourceid == xi2.ndeviceinfos && dev->classes[j]->type == XIValuatorClass)
{
xi2.deviceinfo[xi2.ndeviceinfos].abs = xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].abs = true;
xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].min = v->min;
xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].max = v->max;
XIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[j];
if (v->mode == XIModeAbsolute && v->number >= 0 && v->number < countof(xi2.deviceinfo[xi2.ndeviceinfos].axis))
{
xi2.deviceinfo[xi2.ndeviceinfos].abs = xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].abs = true;
xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].min = v->min;
xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].max = v->max;
}
}
}
}
}
xi2.pXIFreeDeviceInfo(dev);
}
xi2.pXIFreeDeviceInfo(dev);
}
xi2.ndeviceinfos++;
@ -2528,8 +2531,8 @@ static void UpdateGrabs(void)
allownullcursor = wantmgrabs; //this says whether we can possibly want it. if false then we disallow the null cursor. Yes, this might break mods that do their own sw cursors. such mods are flawed in other ways too.
if (Key_MouseShouldBeFree())
wantmgrabs = false;
if (modeswitchpending)
wantmgrabs = false;
// if (modeswitchpending)
// wantmgrabs = false;
if (wantmgrabs)
install_grabs();
@ -2834,6 +2837,7 @@ static void GetEvent(void)
{
modeswitchpending = 1;
modeswitchtime = Sys_Milliseconds() + 1500; /*fairly slow, to make sure*/
UpdateGrabs();
}
if (event.xfocus.window == vid_window)
@ -2853,6 +2857,10 @@ static void GetEvent(void)
break;
}
#ifdef USE_XRANDR
if (xrandr.origgamma)
xrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);
#endif
#ifdef USE_VMODE
if (vm.originalapplied)
vm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);
@ -3389,6 +3397,33 @@ qboolean GLVID_ApplyGammaRamps(unsigned int rampcount, unsigned short *ramps)
}
}
#ifdef USE_XRANDR
if (xrandr.origgamma)
{ //we favour xrandr - xf86 gamma seems to cache old values which screws up gamma after having previously quit ezquake.
if (ramps && rampcount == xrandr.origgamma->size)
{
XRRCrtcGamma g;
g.size = rampcount;
g.red = &ramps[0];
g.green = &ramps[rampcount];
g.blue = &ramps[rampcount*2];
if (vid.activeapp)
{
if (gammaworks)
xrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, &g);
gammaworks = true;
return gammaworks;
}
return false;
}
else if (gammaworks)
{
xrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);
return true;
}
}
#endif
#ifdef USE_VMODE
//if we don't know the original ramps yet, don't allow changing them, because we're probably invalid anyway, and even if it worked, it'll break something later.
if (vm.originalapplied)
@ -3818,6 +3853,7 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int
XRandR_Init();
if (fullscreen && !(fullscreenflags & FULLSCREEN_ANYMODE))
XRandR_SelectMode(info->devicename, &x, &y, &width, &height, rate);
XRandR_FindOutput(info->devicename);
#endif
#ifdef USE_VMODE
@ -3932,8 +3968,13 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int
x11.pXFlush(vid_dpy);
#ifdef USE_XRANDR
if (xrandr.origgamma)
vid.gammarampsize = xrandr.origgamma->size;
else
#endif
#ifdef USE_VMODE
if (vm.vmajor >= 2)
if (!xrandr.origgamma)
{
int rampsize = 256;
vm.pXF86VidModeGetGammaRampSize(vid_dpy, scrnum, &rampsize);
@ -3948,7 +3989,9 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int
vm.originalapplied = vm.pXF86VidModeGetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);
}
}
else
#endif
vid.gammarampsize = 256;
switch(currentpsl)
{
@ -4568,22 +4611,25 @@ void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type
{
int i, devs;
XIDeviceInfo *dev = xi2.pXIQueryDevice(vid_dpy, xi2.devicegroup, &devs);
for (i = 0; i < devs; i++)
if (dev)
{
if (!dev[i].enabled)
continue;
if (/*dev[i].use == XIMasterPointer ||*/ dev[i].use == XISlavePointer)
for (i = 0; i < devs; i++)
{
struct xidevinfo *devi = XI2_GetDeviceInfo(dev[i].deviceid);
callback(ctx, devi->abs?"tablet":"mouse", dev[i].name, &devi->qdev);
if (!dev[i].enabled)
continue;
if (/*dev[i].use == XIMasterPointer ||*/ dev[i].use == XISlavePointer)
{
struct xidevinfo *devi = XI2_GetDeviceInfo(dev[i].deviceid);
callback(ctx, devi->abs?"tablet":"mouse", dev[i].name, &devi->qdev);
}
// else if (dev[i].use == XIMasterKeyboard || dev[i].use == XISlaveKeyboard)
// {
// int qdev = dev[i].deviceid;
// callback(ctx, "xi2kb", dev[i].name, &qdev);
// }
}
// else if (dev[i].use == XIMasterKeyboard || dev[i].use == XISlaveKeyboard)
// {
// int qdev = dev[i].deviceid;
// callback(ctx, "xi2kb", dev[i].name, &qdev);
// }
xi2.pXIFreeDeviceInfo(dev);
}
xi2.pXIFreeDeviceInfo(dev);
}
break;
}

View file

@ -1087,6 +1087,24 @@ r_part ce_snow
flurry 32
}
//eidolon's arena
r_part ce_rain
{
texture "particles/fteparticlefont.tga"
tcoords 1 1 63 63 256 2 64
type texturedspark
count 1
scale 2
scalefactor 1
die 1
alpha 0.2
alphadelta 0
rgb 255 255 255
friction 0
blend add
veladd 1
gravity 0
}
//this teleport effect is nothing like hexen2's. hopefully it'll be acceptable :s
//the down ring
@ -1123,7 +1141,6 @@ r_part +ce_teleporterbody
}
//h2part.ce_rain was not loaded
//h2part.ce_quake was not loaded
//h2part.ce_ghost was not loaded
//h2part.ce_teleporterbody_1 was not loaded

View file

@ -8222,7 +8222,7 @@ void SV_RegisterH2CustomTents(void)
}
static void QCBUILTIN PF_h2starteffect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
float *min, *max, *size;
float *min, *max;//, *size;
// float *angle;
float colour;
// float wait, radius, frame, framelength, duration;
@ -8239,18 +8239,29 @@ static void QCBUILTIN PF_h2starteffect(pubprogfuncs_t *prinst, struct globalvars
/*this effect is meant to be persistant (endeffect is never used)*/
min = G_VECTOR(OFS_PARM1);
max = G_VECTOR(OFS_PARM2);
size = G_VECTOR(OFS_PARM3);
dir = G_VECTOR(OFS_PARM4);
colour = G_FLOAT(OFS_PARM5);
count = G_FLOAT(OFS_PARM6);
//wait = G_FLOAT(OFS_PARM7);
//size = G_VECTOR(OFS_PARM3); /*maxs-min, so useless*/
dir = G_VECTOR(OFS_PARM4); /*[125,100] or [0,0]*/
colour = G_FLOAT(OFS_PARM5); /*414 default*/
count = G_FLOAT(OFS_PARM6); /*300 default*/
count *= /*wait =*/ G_FLOAT(OFS_PARM7); /*0.1 default*/
/*FIXME: not spawned - this persistant effect is created by a map object, all attributes are custom.*/
if (colour == 0 && size == 0)
SV_CustomTEnt_Spawn(h2customtents[efnum], min, max, count, dir);
if (colour == 414)
efnum = h2customtents[efnum];
else
Con_Printf("FTE-H2 FIXME: ce_rain not supported!\n");
efnum = SV_CustomTEnt_Register(va("h2part.ce_rain_%i", (int)colour), CTE_PERSISTANT|CTE_CUSTOMVELOCITY|CTE_ISBEAM|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);
{ //align to the top of the rain volume
//particles should be removed once they reach the bottom
vec3_t nmin;
vec3_t ndir;
nmin[0] = min[0];
nmin[1] = min[1];
nmin[2] = max[2];
VectorSet(ndir, dir[0], dir[1], (min[2]-max[2])/1); //divide by expected lifetime.
SV_CustomTEnt_Spawn(efnum, nmin, max, count, ndir);
}
return;
case ce_snow:
/*this effect is meant to be persistant (endeffect is never used)*/
@ -9887,7 +9898,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars
pmove.physents[0].model = sv.world.worldmodel;
pmove.onladder = false;
pmove.onground = false;
pmove.onground = ((int)ent->v->flags & FL_ONGROUND) != 0;
pmove.groundent = 0;
pmove.waterlevel = 0;
pmove.watertype = 0;
@ -11853,7 +11864,7 @@ void PR_DumpPlatform_f(void)
{"total_monsters", "float", QW|NQ},
{"found_secrets", "float", QW|NQ},
{"killed_monsters", "float", QW|NQ},
{"parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16", "float", QW|NQ},
{"parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16", "float", QW|NQ, "Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid."},
{"intermission", "float", CS},
{"v_forward, v_up, v_right", "vector", QW|NQ|CS},
{"view_angles", "vector", CS, D("+x=DOWN")},
@ -12090,9 +12101,11 @@ void PR_DumpPlatform_f(void)
{"m_toggle", "void(float wantmode)", MENU},
{"m_consolecommand", "float(string cmd)", MENU},
{"parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32", "float", QW|NQ},
{"parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32", "float", QW|NQ, "Additional spawn parms, following the same parmN theme."},
{"parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48", "float", QW|NQ},
{"parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64", "float", QW|NQ},
{"parm_string", "string", QW|NQ, "Like the regular parmN globals, but preserves string contents."},
{"startspot", "string", QW|NQ, "Receives the value of the second argument to changelevel from the previous map."},
{"dimension_send", "var float", QW|NQ, "Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero."},
{"dimension_default", "//var float", QW|NQ, "Default dimension bitmask", 255},
{"physics_mode", "__used var float", QW|NQ|CS, "0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc.", 2},

View file

@ -232,7 +232,7 @@ and the extension fields are added on the end and can have extra vm-specific stu
#define comextqcfields \
comfieldvector(punchangle,NULL) /*std in nq*/\
comfieldfloat(gravity,NULL) /*added in quake 1.09 (for hipnotic)*/\
comfieldfloat(gravity,"Multiplier applied in addition to sv_gravity (not absolute units), to control the gravity affecting this entity specifically.") /*added in quake 1.09 (for hipnotic)*/\
comfieldfloat(hull,"Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox.")/*PEXT_HEXEN2*/\
comfieldentity(movechain,"This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity.")/*hexen2*/\
comfieldfunction(chainmoved, ".void()","Called when the entity is moved as a result of being part of another entity's .movechain")/*hexen2*/\

View file

@ -2113,6 +2113,8 @@ typedef struct {
int health;
int spectator; //0=send to a player. 1=non-tracked player, to a spec. 2=tracked player, to a spec(or self)
qboolean isself;
qboolean onground;
qboolean solid;
int fteext;
int zext;
int hull;
@ -2251,17 +2253,24 @@ void SV_WritePlayerToClient(sizebuf_t *msg, clstate_t *ent)
pflags |= pm_code << PF_PMC_SHIFT;
}
if (pflags & 0xff0000)
pflags |= PF_EXTRA_PFS;
if ((zext & Z_EXT_PF_ONGROUND) && ent->onground)
pflags |= PF_ONGROUND;
if ((zext & Z_EXT_PF_SOLID) && ent->solid)
pflags |= PF_SOLID;
MSG_WriteByte (msg, svc_playerinfo);
MSG_WriteByte (msg, ent->playernum);
MSG_WriteShort (msg, pflags&0xffff);
if (pflags & PF_EXTRA_PFS)
if (ent->fteext & (PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS))
{
MSG_WriteByte(msg, (pflags&0xff0000)>>16);
if (pflags & 0xff0000)
pflags |= PF_EXTRA_PFS;
MSG_WriteShort (msg, pflags&0xffff);
if (pflags & PF_EXTRA_PFS)
MSG_WriteByte(msg, (pflags&0xff0000)>>16);
}
else
MSG_WriteShort (msg, (pflags&0x3fff) | ((pflags&0xc00000)>>8));
//we need to tell the client that it's moved, as it's own origin might not be natural
for (i=0 ; i<3 ; i++)
@ -2321,9 +2330,7 @@ void SV_WritePlayerToClient(sizebuf_t *msg, clstate_t *ent)
}
if (pflags & PF_MODEL)
{
MSG_WriteByte (msg, ent->modelindex);
}
if (pflags & PF_SKINNUM)
MSG_WriteByte (msg, ent->skin | (((pflags & PF_MODEL)&&(ent->modelindex>=256))<<7));
@ -2551,6 +2558,8 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t *
clst.zext = 0;//client->zquake_extensions;
clst.cl = NULL;
clst.vw_index = 0;
clst.solid = true;
clst.onground = true;
lerp = (realtime - olddemotime) / (nextdemotime - olddemotime);
if (lerp < 0)
@ -2712,6 +2721,8 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t *
clst.velocity = vent->v->velocity;
clst.effects = ent->v->effects;
clst.vw_index = ent->xv->vw_index;
clst.onground = (int)ent->v->flags & FL_ONGROUND;
clst.solid = ent->v->solid && ent->v->solid != SOLID_CORPSE && ent->v->solid != SOLID_TRIGGER;
if (progstype == PROG_H2 && ((int)vent->v->effects & H2EF_NODRAW))
{
@ -3198,6 +3209,8 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli
if (cl->isindependant)
{
state->u.q1.pmovetype = ent->v->movetype;
if (state->u.q1.pmovetype && ((int)ent->v->flags & FL_ONGROUND) && (client->zquake_extensions&Z_EXT_PF_ONGROUND))
state->u.q1.pmovetype |= 0x80;
if (cl != client && client)
{ /*only generate movement values if the client doesn't already know them...*/
state->u.q1.movement[0] = ent->xv->movement[0];
@ -3227,7 +3240,7 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli
else
{
if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
if (state->u.q1.pmovetype && (state->u.q1.pmovetype != MOVETYPE_TOSS && state->u.q1.pmovetype != MOVETYPE_BOUNCE))
if (state->u.q1.pmovetype && ((state->u.q1.pmovetype&0x7f) != MOVETYPE_TOSS && (state->u.q1.pmovetype&0x7f) != MOVETYPE_BOUNCE))
{
state->angles[0] = ent->v->v_angle[0]/-3.0;
state->angles[1] = ent->v->v_angle[1];
@ -4048,7 +4061,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
// on client side doesn't stray too far off
if (ISQWCLIENT(client))
{
if (client->fteprotocolextensions & PEXT_ACCURATETIMINGS && sv.world.physicstime - client->nextservertimeupdate > 0)
if ((client->fteprotocolextensions & PEXT_ACCURATETIMINGS )&& sv.world.physicstime - client->nextservertimeupdate > 0)
{ //the fte pext causes the server to send out accurate timings, allowing for perfect interpolation.
MSG_WriteByte (msg, svcqw_updatestatlong);
MSG_WriteByte (msg, STAT_TIME);
@ -4056,7 +4069,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
client->nextservertimeupdate = sv.world.physicstime;
}
else if (client->zquake_extensions & Z_EXT_SERVERTIME && sv.world.physicstime - client->nextservertimeupdate > 0)
else if ((client->zquake_extensions & Z_EXT_SERVERTIME) && sv.world.physicstime - client->nextservertimeupdate > 0)
{ //the zquake ext causes the server to send out peridoic timings, allowing for moderatly accurate game time.
MSG_WriteByte (msg, svcqw_updatestatlong);
MSG_WriteByte (msg, STAT_TIME);

View file

@ -37,6 +37,23 @@ int host_hunklevel;
client_t *host_client; // current client
void CvarPostfixKMG(cvar_t *v, char *oldval)
{
double k = 1000; //scientific units are in thousands. 10ki is 10*1024.
char *end;
double f = strtod(v->string, &end);
if (*end && end[1]=='i')
k = 1024; //kibi
if (*end == 'k' || *end == 'K')
f *= k;
if (*end == 'm' || *end == 'M')
f *= k*k;
if (*end == 'b' || *end == 'B' || *end == 'g' || *end == 'G')
f *= k*k*k;
v->ival = f;
v->value = f;
}
// bound the size of the physics time tic
#ifdef SERVERONLY
cvar_t sv_mintic = CVARD("sv_mintic","0.013", "The minimum interval between running physics frames.");
@ -100,9 +117,9 @@ extern cvar_t net_enable_dtls;
cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.");
cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.");
cvar_t sv_highchars = CVAR("sv_highchars", "1");
cvar_t sv_maxrate = CVARD("sv_maxrate", "50000", "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.");
cvar_t sv_maxdrate = CVARAFD("sv_maxdrate", "500000",
"sv_maxdownloadrate", 0, "This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)");
cvar_t sv_maxrate = CVARCD("sv_maxrate", "50k", CvarPostfixKMG, "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.");
cvar_t sv_maxdrate = CVARAFCD("sv_maxdrate", "500k",
"sv_maxdownloadrate", 0, CvarPostfixKMG, "This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)");
cvar_t sv_minping = CVARFD("sv_minping", "", CVAR_SERVERINFO, "Simulate fake lag for any players with a ping under the value specified here. Value is in milliseconds.");
cvar_t sv_bigcoords = CVARFD("sv_bigcoords", "1", 0, "Uses floats for coordinates instead of 16bit values.\nAlso boosts angle precision, so can be useful even on small maps.\nAffects clients thusly:\nQW: enforces a mandatory protocol extension\nDP: enables DPP7 protocol support\nNQ: uses RMQ protocol (protocol 999).");
@ -123,7 +140,7 @@ cvar_t sv_masterport = CVAR("sv_masterport", "0");
cvar_t pext_ezquake_nochunks = CVARD("pext_ezquake_nochunks", "0", "Prevents ezquake clients from being able to use the chunked download extension. This sidesteps numerous ezquake issues, and will make downloads slower but more robust.");
cvar_t sv_gamespeed = CVARAF("sv_gamespeed", "1", "slowmo", 0);
cvar_t sv_csqcdebug = CVAR("sv_csqcdebug", "0");
cvar_t sv_csqcdebug = CVARD("sv_csqcdebug", "0", "Inject packet size information for data directed to csqc.");
cvar_t sv_csqc_progname = CVAR("sv_csqc_progname", "csprogs.dat");
cvar_t pausable = CVAR("pausable", "");
cvar_t sv_banproxies = CVARD("sv_banproxies", "0", "If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies.");
@ -3060,12 +3077,27 @@ client_t *SVC_DirectConnect(void)
//its causing far far far too many connectivity issues. seriously. its beyond a joke. I cannot stress that enough.
//as the client needs to listen for the serverinfo to know which extensions will actually be used (yay demos), we can just forget that it supports svc-level extensions, at least for anything that isn't spammed via clc_move etc before the serverinfo.
s = InfoBuf_ValueForKey(&newcl->userinfo, "*client");
if (!strncmp(s, "ezQuake", 7) && (newcl->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS))
if (!strncmp(s, "ezQuake", 7))
{
if (pext_ezquake_nochunks.ival)
if (newcl->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)
{
newcl->fteprotocolextensions &= ~PEXT_CHUNKEDDOWNLOADS;
Con_TPrintf("%s: ignoring ezquake chunked downloads extension.\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
if (pext_ezquake_nochunks.ival)
{
newcl->fteprotocolextensions &= ~PEXT_CHUNKEDDOWNLOADS;
Con_TPrintf("%s: ignoring ezquake chunked downloads extension.\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
}
}
if (newcl->zquake_extensions & (Z_EXT_PF_SOLID|Z_EXT_PF_ONGROUND))
{
if (newcl->fteprotocolextensions & PEXT_HULLSIZE)
Con_TPrintf("%s: ignoring ezquake hullsize extension (conflicts with z_ext_pf_onground).\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
if (newcl->fteprotocolextensions & PEXT_SCALE)
Con_TPrintf("%s: ignoring ezquake scale extension (conflicts with z_ext_pf_solid).\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
if (newcl->fteprotocolextensions & PEXT_FATNESS)
Con_TPrintf("%s: ignoring ezquake fatness extension (conflicts with z_ext_pf_solid).\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
if (newcl->fteprotocolextensions & PEXT_TRANS)
Con_TPrintf("%s: ignoring ezquake transparency extension (buggy on players, conflicts with z_ext_pf_solid).\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
newcl->fteprotocolextensions &= ~(PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS);
}
}
@ -5104,6 +5136,7 @@ void SV_InitLocal (void)
extern cvar_t pm_ktjump;
extern cvar_t pm_slidefix;
extern cvar_t pm_airstep;
extern cvar_t pm_pground;
extern cvar_t pm_stepdown;
extern cvar_t pm_walljump;
extern cvar_t pm_slidyslopes;
@ -5163,6 +5196,7 @@ void SV_InitLocal (void)
Cvar_Register (&pm_slidefix, cvargroup_serverphysics);
Cvar_Register (&pm_slidyslopes, cvargroup_serverphysics);
Cvar_Register (&pm_airstep, cvargroup_serverphysics);
Cvar_Register (&pm_pground, cvargroup_serverphysics);
Cvar_Register (&pm_stepdown, cvargroup_serverphysics);
Cvar_Register (&pm_walljump, cvargroup_serverphysics);
Cvar_Register (&pm_edgefriction, cvargroup_serverphysics);
@ -5243,6 +5277,8 @@ void SV_InitLocal (void)
Cvar_Register (&sv_maxrate, cvargroup_servercontrol);
Cvar_Register (&sv_maxdrate, cvargroup_servercontrol);
Cvar_ForceCallback(&sv_maxrate);
Cvar_ForceCallback(&sv_maxdrate);
Cvar_Register (&sv_minping, cvargroup_servercontrol);
Cvar_Register (&sv_nailhack, cvargroup_servercontrol);

View file

@ -70,12 +70,13 @@ cvar_t pm_ktjump = CVARF("pm_ktjump", "", CVAR_SERVERINFO);
cvar_t pm_bunnyspeedcap = CVARFD("pm_bunnyspeedcap", "", CVAR_SERVERINFO, "0 or 1, ish. If the player is traveling faster than this speed while turning, their velocity will be gracefully reduced to match their current maxspeed. You can still rocket-jump to gain high velocity, but turning will reduce your speed back to the max. This can be used to disable bunny hopping.");
cvar_t pm_watersinkspeed = CVARFD("pm_watersinkspeed", "", CVAR_SERVERINFO, "This is the speed that players will sink at while inactive in water. Empty means 60.");
cvar_t pm_flyfriction = CVARFD("pm_flyfriction", "", CVAR_SERVERINFO, "Amount of friction that applies in fly or 6dof mode. Empty means 4.");
cvar_t pm_slidefix = CVARF("pm_slidefix", "", CVAR_SERVERINFO);
cvar_t pm_slidyslopes = CVARF("pm_slidyslopes", "", CVAR_SERVERINFO);
cvar_t pm_airstep = CVARAF("pm_airstep", "", "sv_jumpstep", CVAR_SERVERINFO);
cvar_t pm_stepdown = CVARF("pm_stepdown", "", CVAR_SERVERINFO);
cvar_t pm_walljump = CVARF("pm_walljump", "", CVAR_SERVERINFO);
cvar_t pm_edgefriction = CVARAFD("pm_edgefriction", "", "edgefriction", CVAR_SERVERINFO, "Default value of 2"); //alternative name (without prefix) for compat with dp
cvar_t pm_slidefix = CVARFD("pm_slidefix", "", CVAR_SERVERINFO, "Fixes an issue when jumping down slopes (ie: they act more like slopes and not steps)");
cvar_t pm_slidyslopes = CVARFD("pm_slidyslopes", "", CVAR_SERVERINFO, "Replicates NQ behaviour, where players will slowly slide down ramps");
cvar_t pm_airstep = CVARAFD("pm_airstep", "", /*dp*/"sv_jumpstep", CVAR_SERVERINFO, "Allows players to step up while jumping. This makes stairs more graceful but also increases potential jump heights.");
cvar_t pm_pground = CVARFD("pm_pground", "", CVAR_SERVERINFO, "Use persisten onground state instead of recalculating every frame."CON_WARNING"Do NOT use with nq mods, as most nq mods will interfere with onground state, resulting in glitches.");
cvar_t pm_stepdown = CVARFD("pm_stepdown", "", CVAR_SERVERINFO, "Causes physics to stick to the ground, instead of constantly losing traction whiloe going down steps.");
cvar_t pm_walljump = CVARFD("pm_walljump", "", CVAR_SERVERINFO, "Allows the player to bounce off walls while arborne.");
cvar_t pm_edgefriction = CVARAFD("pm_edgefriction", "", /*dp*/"edgefriction", CVAR_SERVERINFO, "Default value of 2");
#define cvargroup_serverphysics "server physics variables"
void WPhys_Init(void)

View file

@ -106,6 +106,7 @@ extern cvar_t pm_ktjump;
extern cvar_t pm_slidefix;
extern cvar_t pm_slidyslopes;
extern cvar_t pm_airstep;
extern cvar_t pm_pground;
extern cvar_t pm_stepdown;
extern cvar_t pm_walljump;
extern cvar_t pm_watersinkspeed;
@ -6981,6 +6982,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
movevars.ktjump = pm_ktjump.value;
movevars.slidefix = (pm_slidefix.value != 0);
movevars.airstep = (pm_airstep.value != 0);
movevars.pground = (pm_pground.value != 0);
movevars.stepdown = (pm_stepdown.value != 0);
movevars.walljump = (pm_walljump.value);
movevars.slidyslopes = (pm_slidyslopes.value!=0);
@ -7154,7 +7156,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
}
{
float ptime = sv.world.physicstime;
double ptime = sv.world.physicstime;
sv.world.physicstime = sv.time; //urgh, WPhys_RunThink uses the wrong time base
WPhys_RunThink (&sv.world, (wedict_t*)sv_player);
sv.world.physicstime = ptime;
@ -7206,6 +7208,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
VectorCopy (sv_player->v->v_angle, pmove.angles);
pmove.pm_type = SV_PMTypeForClient (host_client, sv_player);
pmove.onground = ((int)sv_player->v->flags & FL_ONGROUND) != 0;
pmove.jump_held = host_client->jump_held;
pmove.jump_msec = 0;
if (progstype != PROG_QW) //this is just annoying.
@ -7224,6 +7227,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
movevars.ktjump = pm_ktjump.value;
movevars.slidefix = (pm_slidefix.value != 0);
movevars.airstep = (pm_airstep.value != 0);
movevars.pground = (pm_pground.value != 0);
movevars.stepdown = (pm_stepdown.value != 0);
movevars.walljump = (pm_walljump.value);
movevars.slidyslopes = (pm_slidyslopes.value!=0);

View file

@ -2404,6 +2404,7 @@ qboolean SV_AntiKnockBack(world_t *w, client_t *client)
VectorCopy(frame->pmorigin, pmove.origin);
VectorCopy(frame->pmvelocity, pmove.velocity);
pmove.pm_type = frame->pmtype;
pmove.onground = true;//FIXME
pmove.jump_held = frame->pmjumpheld;
pmove.waterjumptime = frame->pmwaterjumptime;
pmove.onladder = frame->pmonladder;

View file

@ -3091,7 +3091,13 @@ static void VK_PaintScreen(void)
//draw the levelshot or the conback fullscreen
if (*levelshotname)
R2D_ScalePic(0, 0, vid.width, vid.height, R2D_SafeCachePic (levelshotname));
{
shader_t *pic = R2D_SafeCachePic (levelshotname);
int w,h;
if (!R_GetShaderSizes(pic, &w, &h, true))
w = h = 1;
R2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h);
}
else if (scr_con_current != vid.height)
R2D_ConsoleBackground(0, vid.height, true);
else

View file

@ -541,14 +541,14 @@ mergeInto(LibraryManager.library,
FTEC.vrDisplay.getFrameData(FTEC.vrframeData);
}
// try
// {
try //this try is needed to handle Host_EndGame properly.
{
dovsync = Runtime.dynCall('i', fnc, []);
// }
// catch(err)
// {
// console.log(err);
// }
}
catch(err)
{
console.log(err);
}
if (vr)
FTEC.vrDisplay.submitFrame();
if (dovsync)
@ -771,7 +771,7 @@ mergeInto(LibraryManager.library,
s.ws.onopen = function(event) {s.con = 1;};
s.ws.onmessage = function(event)
{
assert(typeof event.data !== 'string' && event.data.byteLength);
assert(typeof event.data !== 'string' && event.data.byteLength, 'websocket data is not usable');
s.inq.push(new Uint8Array(event.data));
};
@ -1088,7 +1088,7 @@ console.log("onerror: " + _url);
return;
buf = buf - 1;
var ctx = AL.currentContext;
var ctx = AL.currentContext || AL.currentCtx;
try
{
//its async, so it needs its own copy of an arraybuffer

View file

@ -243,14 +243,20 @@ void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length)
{
unsigned int c;
int enclen = 0;
int datatype = 2; //1=utf-8, 2=binary
/*work out how much buffer space we'll need*/
for (c = 0; c < length; c++)
if (datatype == 2)
enclen += length;
else
{
if (((unsigned char*)buffer)[c] == 0 || ((unsigned char*)buffer)[c] >= 0x80)
enclen += 2;
else
enclen += 1;
for (c = 0; c < length; c++)
{
if (((unsigned char*)buffer)[c] == 0 || ((unsigned char*)buffer)[c] >= 0x80)
enclen += 2;
else
enclen += 1;
}
}
if (prox->buffersize-prox->bufferpos + (enclen+4) > MAX_PROXY_BUFFER)
@ -269,29 +275,37 @@ void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length)
if (enclen >= 126)
{
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x81;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80|datatype;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 126;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen>>8;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;
}
else
{
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x81;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80|datatype;
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;
}
while(length-->0)
if (datatype == 2)
{
c = *(unsigned char*)buffer;
buffer = (char*)buffer+1;
if (!c)
c |= 0x100; /*will get truncated at the other end*/
if (c >= 0x80)
for(; length-->0; buffer = (char*)buffer+1)
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = *(unsigned char*)buffer;
}
else
{ //utf-8 (or really just bytes with upper bits truncated. sue me.
while(length-->0)
{
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0xc0 | (c>>6);
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80 | (c & 0x3f);
c = *(unsigned char*)buffer;
buffer = (char*)buffer+1;
if (!c)
c |= 0x100; /*will get truncated at the other end*/
if (c >= 0x80)
{
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0xc0 | (c>>6);
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80 | (c & 0x3f);
}
else
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = c;
}
else
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = c;
}
Net_TryFlushProxyBuffer(cluster, prox); //try flushing in a desperate attempt to reduce bugs in google chrome.
return;

View file

@ -202,32 +202,6 @@ static void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *titl
);
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
#if 0
if (plugin)
{
s =
"<script>"
"if (!parent.getplug || parent.getplug().plugver == undefined || parent.getplug().plugver < "MINPLUGVER")"
"{"
"if (!parent.getplug || parent.getplug().plugver == undefined)"
"{"
"document.write(\"You need a plugin! Get one here!\");"
"}"
"else"
"{"
"document.write(\"Update your plugin!\");"
"}"
"document.write(\"<br/>"
"<a href=\\\"npfte.xpi\\\">Firefox (open with firefox itself)</a><br/>"
"<a href=\\\"iefte.exe\\\">Internet Explorer</a><br/>"
"<a href=\\\"npfte.exe\\\">Others</a><br/>"
"\");"
"}"
"</script>";
Net_ProxySend(cluster, dest, s, strlen(s));
}
#endif
}
static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest)
@ -239,12 +213,6 @@ static void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest)
snprintf(buffer, sizeof(buffer), "<br/>Server Version: "QTV_VERSION_STRING" <a href=\""PROXYWEBSITE"\" target=\"_blank\">"PROXYWEBSITE"</a>");
Net_ProxySend(cluster, dest, buffer, strlen(buffer));
#if 0
/*Plugin version*/
s = "<script>if (parent.getplug != null) document.write(\"<br/>Plugin Version: \" + parent.getplug().build + parent.getplug().server);</script>";
Net_ProxySend(cluster, dest, s, strlen(s));
#endif
/*terminate html page*/
s = "</body>\n"
"</html>\n";
@ -347,8 +315,11 @@ static void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest)
HTTPSV_SendHTTPHeader(cluster, dest, "200", "text/css", false);
HTMLPRINT("* { font-family: Verdana, Helvetica, sans-serif; }");
HTMLPRINT("body { color: #000; background-color: #fff; padding: 0 40px; }");
HTMLPRINT("body { color: #000; background-color: #fff; padding: 0 0px; }");
HTMLPRINT("a { color: #00f; }");
HTMLPRINT("div.topdiv { display:flex; align-items: stretch; position: absolute; top: 0; right: 0; bottom: 0; left: 0; }");
HTMLPRINT("div.left { resize: horizontal; overflow: auto; flex 0 0 25%;}");
HTMLPRINT("div.right { padding:0; margin: 0; flex: auto; }");
HTMLPRINT("a.qtvfile { font-weight: bold; }");
HTMLPRINT("a:visited { color: #00f; }");
HTMLPRINT("a:hover { background-color: black; color: yellow; }");
@ -357,6 +328,7 @@ static void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest)
HTMLPRINT("dl.nowplaying dt { margin: 1em 0 0 0; font-size: 1.1em; font-weight: bold; }");
HTMLPRINT("dl.nowplaying li { list-style: none; margin: 0 0 0 1em; padding: 0; }");
HTMLPRINT("dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }");
HTMLPRINT("canvas.emscripten { border: 0px none; padding:0; margin: 0; width: 100%; height: 100%;}");
HTMLPRINT("#navigation { background-color: #eef; }");
HTMLPRINT("#navigation li { display: inline; list-style: none; margin: 0 3em; }");
}
@ -788,154 +760,66 @@ static void HTTPSV_GeneratePlugin(cluster_t *cluster, oproxy_t *dest)
html = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
"<HTML><HEAD><TITLE>QuakeTV With Plugin</TITLE>"
" <link rel=\"StyleSheet\" href=\"/style.css\" type=\"text/css\" />\n"
"<meta charset='utf-8' />"
"</HEAD><body>"
"<div id=optdiv style='position:fixed; left:0%; width:50%; top:0%; height:100%;'>"
"<iframe frameborder=0 src=\"nowplaying.html?p\" width=\"100%\" height=\"100%\">"
"oh dear. your browser doesn't support this site"
"</iframe>"
"<div class='topdiv'>"
"<div class='left' id=optdiv>"
"<iframe frameborder=0 src=\"nowplaying.html?p\" width=\"100%\" height=\"100%\">"
"oh dear. your browser doesn't support this site"
"</iframe>"
"</div>"
"<div class='right' id=plugdiv>"
"<canvas class='emscripten' id='canvas' oncontextmenu='event.preventDefault()'>Canvas not supported</canvas>"
"</div>"
"</div>"
"<div id=plugdiv style='position:fixed; left:50%; width:50%; top:0%; height:100%;'>"
"<script type='text/javascript'>"
"var Module = {"
"canvas: document.getElementById('canvas'),"
"print: function(msg)"
"{"
"console.log(msg);"
"},"
"printErr: function(text)"
"{"
"if (text.substr(0, 28) == 'Cannot enlarge memory arrays')"
"alert('Memory full/fragmented. Please reload the page.');"
"else "
"console.log(text);"
"},"
"arguments: ["
"'-nohome',"
"'-manifest',"
"'http://triptohell.info/demo.fmf',"
"'+connect',"
"window.location.host"
"]"
"};"
#if 1
#define EMBEDDEDWEBGLORIGIN "http://86.191.129.12:80"//127.0.0.1:80"
#define EMBEDDEDWEBGLURL EMBEDDEDWEBGLORIGIN"/ftewebgl.html"
#else
#define EMBEDDEDWEBGLORIGIN "http://triptohell.info"
#define EMBEDDEDWEBGLURL EMBEDDEDWEBGLORIGIN"/moodles/web/ftewebgl.html"
#endif
//create the script object
"var s = document.createElement('script');"
// set it up
"s.setAttribute('src','http://triptohell.info/ftewebgl.js');"
"s.setAttribute('type','text/javascript');"
"s.setAttribute('charset','utf-8');"
"s.addEventListener('error', function() {alert('Error loading game script.');}, false);"
// add to DOM
"document.head.appendChild(s);"
#if 1
"<iframe frameborder=0 allowfullscreen=1 name=\"webgl\" src=\""EMBEDDEDWEBGLURL"\" width=\"100%\" height=\"100%\">"
"oh dear. your browser doesn't support this site"
"</iframe>"
#else
/*once for IE*/
"<object name=\"ieplug\""
" type=\"text/x-quaketvident\""
" classid=\"clsid:7d676c9f-fb84-40b6-b3ff-e10831557eeb\""
//" codebase=\""PROXYWEBSITE"/test.cab\""
" width=100%"
" height=100%"
" >"
"<param name=\"splash\" value=\"http://"
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"/qtvsplash.jpg\">"
"<param name=\"availver\" value=\""MINPLUGVER"\">"
"<param name=\"game\" value=\"q1\">"
"<param name=\"dataDownload\" value='";
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, cluster->plugindatasource, strlen(cluster->plugindatasource));
html =
"'>"
/*once again for firefox and similar friends*/
"<object name=\"npplug\""
" type=\"text/x-quaketvident\""
" width=100%"
" height=100%"
" >"
"<param name=\"splash\" value=\"http://"
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"/qtvsplash.jpg\">"
"<param name=\"availver\" value=\""MINPLUGVER"\">"
"<param name=\"game\" value=\"q1\">"
"<param name=\"dataDownload\" value='";
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, cluster->plugindatasource, strlen(cluster->plugindatasource));
html =
"'>"
"Plugin failed to load"
"</object>"
"</object>"
#endif
"</div>"
//set up some functions that the embedded stuff can use
"parent.joinserver = function(d)"
"{"
"}\n"
"<script>"
#if 0
"function getplugnp(d)\n"
"{\n"
"return document.npplug;\n"
"}\n"
"function getplugie(d)\n"
"{\n"
"return document.ieplug;\n"
"}\n"
"parent.host = \""
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"\";\n"
"parent.playdemo = function(d)"
"{"
"}\n"
"if (getplugie() != undefined && getplugie().plugver != undefined)\n"
"{\nparent.getplug = getplugie;\n}\n"
"else\n"
"{\nparent.getplug = getplugnp;\n}\n"
"function joinserver(d)\n"
"{\n"
"getplug().mapsrc = \""
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, cluster->plugindatasource, strlen(cluster->plugindatasource));
html =
"\";\n"
"getplug().server = d;\n"
"getplug().running = 1;\n"
"}\n"
"function playdemo(d)\n"
"{\n"
// "getplug().mapsrc = \"http://bigfoot.morphos-team.net/misc/quakemaps/\";\n"
"getplug().stream = \"file:\"+d+\"@"
;
Net_ProxySend(cluster, dest, html, strlen(html));
Net_ProxySend(cluster, dest, hostname, strlen(hostname));
html =
"\";\n"
"getplug().running = 1;\n"
"}\n"
#else
"function joinserver(d)"
"{"
// "webgl.postMessage({cmd:'mapsrc',url:\""
// ;
// Net_ProxySend(cluster, dest, html, strlen(html));
// Net_ProxySend(cluster, dest, cluster->plugindatasource, strlen(cluster->plugindatasource));
// html =
// "\"}, \""EMBEDDEDWEBGLORIGIN"\");"
"webgl.postMessage({'cmd':'observeurl','url':'ws://86.191.129.12:27500'}, \""EMBEDDEDWEBGLORIGIN"\");"
"}\n"
"function playdemo(d)"
"{"
// "parent.webgl.postMessage({cmd:'mapsrc',url:\""
// ;
// Net_ProxySend(cluster, dest, html, strlen(html));
// Net_ProxySend(cluster, dest, cluster->plugindatasource, strlen(cluster->plugindatasource));
// html =
// "\"}, \""EMBEDDEDWEBGLORIGIN"\");"
"parent.webgl.postMessage({cmd:'demourl',url:d}, \""EMBEDDEDWEBGLORIGIN"\");"
"}\n"
#endif
"parent.joinserver = joinserver;\n"
"parent.playdemo = playdemo;\n"
/* "if (getplug().plugver == undefined)"
"{"
"document.getElementById('plugdiv').style.left = '75%';"
"document.getElementById('optdiv').style.width = '25%';"
"document.getElementById('plugdiv').style.width = '0';"
"parent.getplug = null;"
"}"
*/
"</script>"
"<noscript>"
"It looks like you have javascript disabled.<br/>"
"If you want to run a javascript port of a game engine, it helps to have javascript enabled. Just saying.<br/>"
"<br/>"
"</noscript>"
"</body></HTML>";
Net_ProxySend(cluster, dest, html, strlen(html));
@ -1171,7 +1055,7 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
uriend = s;
urilen = uriend - uri;
if (!pend->websocket.websocket && HTTPSV_GetHeaderField((char*)pend->inbuffer, "Connection", connection, sizeof(connection)) && !stricmp(connection, "Upgrade"))
if (!pend->websocket.websocket && HTTPSV_GetHeaderField((char*)pend->inbuffer, "Connection", connection, sizeof(connection)) && strstr(connection, "Upgrade"))
{
if (HTTPSV_GetHeaderField((char*)pend->inbuffer, "Upgrade", upgrade, sizeof(upgrade)) && !stricmp(upgrade, "websocket"))
{
@ -1252,16 +1136,7 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
#endif
else REDIRECTIF("/", "/nowplaying.html")
else REDIRECTIF("/about.html", PROXYWEBSITE)
#if 0
else REDIRECTIF("/qtvsplash.jpg", "/file/qtvsplash.jpg") /*lame, very lame*/
#if defined(_DEBUG) || defined(DEBUG)
else REDIRECTIF("/npfte.xpi", "/file/npfte_dbg.xpi") /*lame, very lame*/
#else
else REDIRECTIF("/npfte.xpi", "/file/npfte.xpi") /*lame, very lame*/
#endif
else REDIRECTIF("/npfte.exe", "/file/npfte.exe") /*lame, very lame*/
else REDIRECTIF("/iefte.exe", "/file/iefte.exe") /*lame, very lame*/
#endif
else REDIRECTIF("/favicon.ico", "http://triptohell.info/favicon.ico")
else if (uriargmatch(uri, "/demos.html", urilen, &args))
{
HTTPSV_GenerateDemoListing(cluster, pend, args);

View file

@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define curtime Sys_Milliseconds()
void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6)
void NET_InitUDPSocket(cluster_t *cluster, int port, int socketid)
{
int sock;
@ -38,39 +38,42 @@ void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6)
unsigned long v6only = false;
#pragma message("fixme")
if (ipv6)
switch(socketid)
{
case SG_IPV6:
pf = PF_INET6;
memset(&address6, 0, sizeof(address6));
address6.sin6_family = AF_INET6;
address6.sin6_port = htons((u_short)port);
address = (struct sockaddr*)&address6;
addrlen = sizeof(address6);
}
else
{
break;
case SG_IPV4:
pf = PF_INET;
address4.sin_family = AF_INET;
address4.sin_addr.s_addr = INADDR_ANY;
address4.sin_port = htons((u_short)port);
address = (struct sockaddr*)&address4;
addrlen = sizeof(address4);
break;
default:
return; //erk
}
if (!ipv6 && !v6only && cluster->qwdsocket[1] != INVALID_SOCKET)
if (socketid == SG_IPV4 && !v6only && cluster->qwdsocket[SG_IPV6] != INVALID_SOCKET)
{
int sz = sizeof(v6only);
if (getsockopt(cluster->qwdsocket[1], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only)
if (getsockopt(cluster->qwdsocket[SG_IPV6], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only)
port = 0;
}
if (!port)
{
if (cluster->qwdsocket[ipv6] != INVALID_SOCKET)
if (cluster->qwdsocket[socketid] != INVALID_SOCKET)
{
closesocket(cluster->qwdsocket[ipv6]);
cluster->qwdsocket[ipv6] = INVALID_SOCKET;
Sys_Printf(cluster, "closed udp%i port\n", ipv6?6:4);
closesocket(cluster->qwdsocket[socketid]);
cluster->qwdsocket[socketid] = INVALID_SOCKET;
Sys_Printf(cluster, "closed udp%i port\n", socketid?6:4);
}
return;
}
@ -97,14 +100,14 @@ void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6)
return;
}
if (cluster->qwdsocket[ipv6] != INVALID_SOCKET)
if (cluster->qwdsocket[socketid] != INVALID_SOCKET)
{
closesocket(cluster->qwdsocket[ipv6]);
Sys_Printf(cluster, "closed udp%i port\n", ipv6?6:4);
closesocket(cluster->qwdsocket[socketid]);
Sys_Printf(cluster, "closed udp%i port\n", socketid?6:4);
}
cluster->qwdsocket[ipv6] = sock;
cluster->qwdsocket[socketid] = sock;
if (v6only)
Sys_Printf(cluster, "opened udp%i port %i\n", ipv6?6:4, port);
Sys_Printf(cluster, "opened udp%i port %i\n", socketid?6:4, port);
else
Sys_Printf(cluster, "opened udp port %i\n", port);
}
@ -115,9 +118,9 @@ SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *toadr, netadr_t ina)
if (((struct sockaddr *)ina.sockaddr)->sa_family == AF_INET6)
{
*toadr = ina;
return sock[1];
return sock[SG_IPV6];
}
if (sock[0] == INVALID_SOCKET && sock[1] != INVALID_SOCKET)
if (sock[0] == INVALID_SOCKET && sock[SG_IPV6] != INVALID_SOCKET)
{
struct sockaddr_in6 *out = (struct sockaddr_in6*)toadr->sockaddr;
struct sockaddr_in *in = (struct sockaddr_in*)ina.sockaddr;
@ -128,11 +131,11 @@ SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *toadr, netadr_t ina)
*(short*)&out->sin6_addr.s6_addr[10] = 0xffff;
*(int*)&out->sin6_addr.s6_addr[12] = in->sin_addr.s_addr;
out->sin6_port = in->sin_port;
return sock[1];
return sock[SG_IPV6];
}
#endif
*toadr = ina;
return sock[0];
return sock[SG_IPV4];
}
#ifdef LIBQTV
@ -172,42 +175,57 @@ void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, net
if (dest->websocket.websocket)
{
int datatype = 2; //1=utf-8, 2=binary
int enclen = 0, c;
for (c = 0; c < length; c++)
if (datatype == 2)
enclen = length;
else
{
if (((unsigned char*)data)[c] == 0 || ((unsigned char*)data)[c] >= 0x80)
enclen += 2;
else
enclen += 1;
for (c = 0; c < length; c++)
{
if (((unsigned char*)data)[c] == 0 || ((unsigned char*)data)[c] >= 0x80)
enclen += 2;
else
enclen += 1;
}
}
if (dest->outbuffersize + 4+enclen < sizeof(dest->outbuffer))
{
if (enclen >= 126)
{
dest->outbuffer[dest->outbuffersize++] = 0x81;
dest->outbuffer[dest->outbuffersize++] = 0x80|datatype;
dest->outbuffer[dest->outbuffersize++] = 126;
dest->outbuffer[dest->outbuffersize++] = enclen>>8;
dest->outbuffer[dest->outbuffersize++] = enclen;
}
else
{
dest->outbuffer[dest->outbuffersize++] = 0x81;
dest->outbuffer[dest->outbuffersize++] = 0x80|datatype;
dest->outbuffer[dest->outbuffersize++] = enclen;
}
while(length-->0)
if (datatype == 2)
{
c = *(unsigned char*)data;
data = (char*)data+1;
if (!c)
c |= 0x100; /*will get truncated at the other end*/
if (c >= 0x80)
memcpy(dest->outbuffer+dest->outbuffersize, data, enclen);
dest->outbuffersize += enclen;
}
else
{
while(length-->0)
{
dest->outbuffer[dest->outbuffersize++] = 0xc0 | (c>>6);
dest->outbuffer[dest->outbuffersize++] = 0x80 | (c & 0x3f);
c = *(unsigned char*)data;
data = (char*)data+1;
if (!c)
c |= 0x100; /*will get truncated at the other end*/
if (c >= 0x80)
{
dest->outbuffer[dest->outbuffersize++] = 0xc0 | (c>>6);
dest->outbuffer[dest->outbuffersize++] = 0x80 | (c & 0x3f);
}
else
dest->outbuffer[dest->outbuffersize++] = c;
}
else
dest->outbuffer[dest->outbuffersize++] = c;
}
}
}

View file

@ -469,6 +469,7 @@ typedef struct viewer_s {
qboolean thinksitsconnected;
qboolean conmenussupported;
qboolean isproxy;
unsigned int pext1, pext2;
int servercount;
@ -926,7 +927,7 @@ void PM_PlayerMove (pmove_t *pmove);
void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient);
void Netchan_OutOfBandPrint (cluster_t *cluster, netadr_t adr, char *format, ...) PRINTFWARNING(3);
//int Netchan_IsLocal (netadr_t adr);
void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6);
void NET_InitUDPSocket(cluster_t *cluster, int port, int socketid);
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr);
SOCKET NET_ChooseSocket(SOCKET sock[], netadr_t *toadr, netadr_t in);
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);

View file

@ -158,6 +158,16 @@ void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)
{
movevars_t movevars;
WriteByte(msg, svc_serverdata);
if (viewer->pext1)
{
WriteLong(msg, PROTOCOL_VERSION_FTE);
WriteLong(msg, viewer->pext1);
}
if (viewer->pext2)
{
WriteLong(msg, PROTOCOL_VERSION_FTE2);
WriteLong(msg, viewer->pext2);
}
WriteLong(msg, PROTOCOL_VERSION);
WriteLong(msg, servercount);
@ -319,6 +329,14 @@ void SendServerData(sv_t *tv, viewer_t *viewer)
InitNetMsg(&msg, buffer, viewer->netchan.maxreliablelen);
if (tv)
{
viewer->pext1 = tv->pext1;
viewer->pext2 = tv->pext2;
}
else
viewer->pext1 = viewer->pext2 = 0;
if (tv && (tv->controller == viewer || !tv->controller))
viewer->thisplayer = tv->map.thisplayer;
else
@ -2642,7 +2660,7 @@ I've removed the following from this function as it covered the menu (~Moodles):
"conmenu menucallback\n"
"menuedit 48 36 \"Óåòöåòº\" \"_server\"\n"
"menuedit 48 36 \"^aServer:\" \"_server\"\n"
"menutext 48 52 \"Demos\" DEMOS\n"
@ -2672,7 +2690,8 @@ I've removed the following from this function as it covered the menu (~Moodles):
if (!shownheader)
{
shownheader = true;
QW_StuffcmdToViewer(v, "menutext 72 %i \"Áãôéöå Çáíåóº\"\n", y);
QW_StuffcmdToViewer(v, "menutext 72 %i \"^aActive Games:\"\n", y);
y+=8;
}
QW_StuffcmdToViewer(v, "menutext 32 %i \"%30s\" \"stream %i\"\n", y, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid);
@ -4041,9 +4060,9 @@ void ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)
}
break;
case clc_tmove:
v->origin[0] = ((signed short)ReadShort(m))/8.0f;
v->origin[1] = ((signed short)ReadShort(m))/8.0f;
v->origin[2] = ((signed short)ReadShort(m))/8.0f;
v->origin[0] = ReadCoord(m, v->pext1);
v->origin[1] = ReadCoord(m, v->pext1);
v->origin[2] = ReadCoord(m, v->pext1);
break;
case clc_upload:

View file

@ -402,8 +402,8 @@ void Cmd_Master(cmdctxt_t *ctx)
void Cmd_UDPPort(cmdctxt_t *ctx)
{
int newp = atoi(Cmd_Argv(ctx, 1));
NET_InitUDPSocket(ctx->cluster, newp, true);
NET_InitUDPSocket(ctx->cluster, newp, false);
NET_InitUDPSocket(ctx->cluster, newp, SG_IPV6);
NET_InitUDPSocket(ctx->cluster, newp, SG_IPV4);
}
void Cmd_AdminPassword(cmdctxt_t *ctx)
{

View file

@ -473,7 +473,7 @@ static size_t JSON_ReadBody(json_t *t, char *out, size_t outsize)
//glTF 1.0 and 2.0 differ in that 1 uses names and 2 uses indexes. There's also some significant differences with materials.
//we only support 2.0
//FTE does not support articulated models. we might be able to convert them to skeletal though.
//articulated models are handled by loading them as skeletal (should probably optimise the engine for this usecase)
//we don't support skeletal models either right now.
//buffers are raw blobs that can come from multiple different sources
@ -560,6 +560,7 @@ static void GLTF_RelativePath(const char *base, const char *relative, char *out,
out += t;
outsize -= t;
//FIXME: uris should be percent-decoded here.
t = strlen(relative);
if (t > outsize)
t = outsize;
@ -611,6 +612,8 @@ static struct gltf_buffer *GLTF_GetBufferData(gltf_t *gltf, int bufferidx)
VFS_READ(f, out->data, length);
VFS_CLOSE(f);
}
else
Con_Printf(CON_WARNING"%s: Unable to read buffer file %s\n", gltf->mod->name, filename);
}
}
return out->data?out:NULL;
@ -1891,8 +1894,10 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
gltf.buffers[0].length = buffersize;
gltf.warnlimit = 5;
//asset.version must exist, supposedly.
gltfver = JSON_GetFloat(gltf.r, "asset.version", 2.0);
//asset.version must exist, supposedly. so default to something b0rked
gltfver = JSON_GetFloat(gltf.r, "asset.minVersion", 0.0);
if (gltfver != 2.0)
gltfver = JSON_GetFloat(gltf.r, "asset.version", 0.0);
if (gltfver == 2.0)
{
JSON_FlagAsUsed(gltf.r, "asset.copyright");
@ -2042,9 +2047,7 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
}
}
//TODO: make a guess at the number of frames+framerate
//(input samplers have min+max values).
//TODO: make a guess at the framerate according to sampler intervals
fg->rate = 30;
fg->numposes = max(1, maxtime*fg->rate);
if (maxtime)