diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index c720cc206..0b3b82b0a 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -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); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ce6198c3c..754b4451b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -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")); diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index d8f556f90..792b8dc7d 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -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); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index 020a38a4d..13a8fb699 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -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); diff --git a/engine/client/image.c b/engine/client/image.c index 4396b6413..e53b4b37f 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -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) diff --git a/engine/client/merged.h b/engine/client/merged.h index 1c960a825..69aff5a5d 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -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); diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 2fd5b84d9..14d8c0905 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -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)) diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 4eddb6e11..2f6332252 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -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); diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index ec5c03725..a10da7bb2 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -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; diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 86b2b2e9f..c34bdcd14 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -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 diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 817bfb579..c9608da6a 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -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 diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 1bf1e6852..3f26c95a2 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -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); diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 3d0c94f2b..7f5922cf0 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -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); diff --git a/engine/client/view.c b/engine/client/view.c index 548968a39..bb1433653 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -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); diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 244edbecc..756c9e458 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -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 diff --git a/engine/common/common.c b/engine/common/common.c index f0dd7e259..ccc1932fe 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -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; diff --git a/engine/common/fs.c b/engine/common/fs.c index 5eb32bf7a..b2a5bb50e 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -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"*/}, diff --git a/engine/common/pmove.c b/engine/common/pmove.c index 11e7a2f5f..16f0385d9 100644 --- a/engine/common/pmove.c +++ b/engine/common/pmove.c @@ -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 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; diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 3926742d7..19845f0b5 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -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 diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 1e3637637..c822e442d 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -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]; diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 9a78c68dd..641b57bcc 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -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(); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 32c46cd86..b386f4ce5 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -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); diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 4f57f15fa..7195d778d 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -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; diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index 0f41c09cb..3e9ff313d 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -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 diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 218694df4..318dbd7e6 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -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; diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 75616c67a..aa91278c6 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -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"); diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 6e426c072..8c9827eb2 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -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; } diff --git a/engine/partcfgs/h2part.cfg b/engine/partcfgs/h2part.cfg index 21775aa63..d581d7984 100644 --- a/engine/partcfgs/h2part.cfg +++ b/engine/partcfgs/h2part.cfg @@ -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 diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 2b8d4d082..f8e11f543 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -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}, diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 4e8325936..f2a7b7f80 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -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*/\ diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 88a84c2f3..dfa6e0e51 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -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); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 7ad5e5580..186f69d73 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -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); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index a4fa2fbc1..87481e431 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -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) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index b25cb702c..950dcce50 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -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); diff --git a/engine/server/world.c b/engine/server/world.c index 72b2a8c4e..030fa987a 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -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; diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 672bbba45..9babeb7d4 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -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 diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index c2851ca49..004754f90 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -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 diff --git a/fteqtv/forward.c b/fteqtv/forward.c index dfa88012d..7be453d7f 100644 --- a/fteqtv/forward.c +++ b/fteqtv/forward.c @@ -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; diff --git a/fteqtv/httpsv.c b/fteqtv/httpsv.c index eb5077d7e..910801b10 100644 --- a/fteqtv/httpsv.c +++ b/fteqtv/httpsv.c @@ -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 = - ""; - 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), "
Server Version: "QTV_VERSION_STRING" "PROXYWEBSITE""); Net_ProxySend(cluster, dest, buffer, strlen(buffer)); -#if 0 - /*Plugin version*/ - s = ""; - Net_ProxySend(cluster, dest, s, strlen(s)); -#endif - /*terminate html page*/ s = "\n" "\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 = "\n" "QuakeTV With Plugin" " \n" + "" "" - "
" - "" + "
" + "
" + "" + "
" + "
" + "Canvas not supported" + "
" "
" - "
" + "" + "" ""; 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); diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 3994cdbd5..37689ce1f 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -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; } } } diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 73d4e0f62..52fb2669e 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -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); diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 702ddfd34..560b41268 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -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: diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 8dc859142..de12a1a9b 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -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) { diff --git a/plugins/models/gltf.c b/plugins/models/gltf.c index ac58629dc..9d5914a61 100644 --- a/plugins/models/gltf.c +++ b/plugins/models/gltf.c @@ -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)