From 3c5518afa8a2b6101142563a8f88ad107f43196c Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 23 May 2019 15:11:32 +0000 Subject: [PATCH] Optimised the textures of defaultwall with fast preset. Fix simpleitems transparency in gles2. Fix a couple of GL errors in gles2 (including some fixed function stuff that could result in segfaults). Dynamically adjust MTU values with legacy qw clients too. Added some small sleeps while waiting for downloads (this fixes insanely slow valgrind startups but might make non-valgrind start a smidge slower). Fix title metadata in the updates system. Clean up some issues for when WEBCLIENT is not defined. HAVE_HTTPSV also disables websocket connections, to completely avoid any http strings anywhere, in the hope that it reduces virus scanner false positives. Fix presets initial option, again. Writing files to FS_GAMEONLY no longer purges the entire fscache, which should give a speedup in certain situations. Added some new cvars to control heightmap lightmap generation. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5461 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 2 +- engine/client/cl_main.c | 17 +- engine/client/m_download.c | 26 ++- engine/client/m_options.c | 1 + engine/client/snd_al.c | 18 +- engine/client/sys_linux.c | 4 +- engine/common/com_mesh.c | 13 +- engine/common/com_mesh.h | 5 +- engine/common/common.h | 9 +- engine/common/config_fteqw.h | 1 + engine/common/cvar.h | 2 +- engine/common/fs.c | 99 ++++++---- engine/common/fs.h | 6 +- engine/common/fs_stdio.c | 28 ++- engine/common/fs_win32.c | 40 ++-- engine/common/gl_q2bsp.c | 33 ++-- engine/common/net.h | 5 +- engine/common/net_chan.c | 46 +++-- engine/common/net_wins.c | 36 ++-- engine/common/plugin.c | 2 +- engine/common/sys_linux_threads.c | 2 + engine/common/zone.c | 38 ++-- engine/common/zone.h | 34 ++-- engine/droid/fte.cfg | 1 + engine/gl/gl_backend.c | 14 ++ engine/gl/gl_draw.c | 11 +- engine/gl/gl_heightmap.c | 272 +++++++++++++++++++++++---- engine/gl/gl_shader.c | 66 ++++++- engine/gl/gl_vidcommon.c | 5 +- engine/gl/r_bishaders.h | 34 ++-- engine/qclib/initlib.c | 38 ++-- engine/server/pr_cmds.c | 5 + engine/server/sv_ccmds.c | 12 +- engine/server/sv_main.c | 18 +- engine/server/sv_nchan.c | 4 +- engine/server/sv_send.c | 11 +- engine/server/sv_user.c | 14 +- engine/server/svq2_game.c | 6 +- engine/shaders/glsl/defaultskin.glsl | 2 +- engine/shaders/glsl/defaultwall.glsl | 17 +- engine/shaders/glsl/terrain.glsl | 17 +- 41 files changed, 735 insertions(+), 279 deletions(-) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 95c2d0237..e4e2f0464 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -4195,7 +4195,7 @@ void CL_LinkPacketEntities (void) Q_snprintfz(name, sizeof(name), "textures/bmodels/simple_%s_%i.tga", basename, ent->skinnum); else Q_snprintfz(name, sizeof(name), "textures/models/simple_%s_%i.tga", basename, ent->skinnum); - model->simpleskin[ent->skinnum] = R_RegisterShader(name, 0, va("{\nnomipmaps\nprogram defaultsprite\nsurfaceparm noshadows\nsurfaceparm nodlight\nsort seethrough\n{\nmap \"%s\"\nalphafunc ge128\n}\n}\n", name)); + model->simpleskin[ent->skinnum] = R_RegisterShader(name, 0, va("{\nnomipmaps\nprogram defaultsprite#MASK=0.5\nsurfaceparm noshadows\nsurfaceparm nodlight\nsort seethrough\n{\nmap \"%s\"\nalphafunc ge128\n}\n}\n", name)); } VectorCopy(le->angles, angles); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 3959d5887..ec9dfba9f 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3493,9 +3493,14 @@ client_connect: //fixme: make function if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO) cls.netchan.qportsize = 1; } - cls.netchan.fragmentsize = connectinfo.mtu; + cls.netchan.pext_fragmentation = connectinfo.mtu?true:false; if (connectinfo.mtu >= 64) + { + cls.netchan.mtu = connectinfo.mtu; cls.netchan.message.maxsize = sizeof(cls.netchan.message_buf); + } + else + cls.netchan.mtu = MAX_QWMSGLEN; #ifdef HUFFNETWORK cls.netchan.compresstable = Huff_CompressionCRC(connectinfo.compresscrc); #else @@ -6396,7 +6401,12 @@ void Host_FinishLoading(void) FS_ChangeGame(NULL, true, true); if (waitingformanifest) + { +#ifdef MULTITHREAD + Sys_Sleep(0.1); +#endif return; + } Con_History_Load(); @@ -6437,7 +6447,12 @@ void Host_FinishLoading(void) } if (PM_IsApplying(true)) + { +#ifdef MULTITHREAD + Sys_Sleep(0.1); +#endif return; + } //android may find that it has no renderer at various points. if (r_forceheadless) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 683d44c0a..2f7a6940a 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1390,6 +1390,15 @@ static void PM_MarkPackage(package_t *package, unsigned int markflag) return; //looks like its already picked. marking it again will do no harm. } +#ifndef WEBCLIENT + //can't mark for download if we cannot download. + if (!(package->flags & DPF_PRESENT)) + { //though we can at least unmark it for deletion... + package->flags &= ~DPF_PURGE; + return; + } +#endif + //any file-conflicts prevent the package from being installable. //this is mostly for pak1.pak for (dep = package->deps; dep; dep = dep->next) @@ -1881,7 +1890,7 @@ static void PM_WriteInstalledPackages(void) if (*p->title && strcmp(p->title, p->name)) { Q_strncatz(buf, " ", sizeof(buf)); - COM_QuotedConcat(va("title=%s", p->version), buf, sizeof(buf)); + COM_QuotedConcat(va("title=%s", p->title), buf, sizeof(buf)); } if (*p->version) { @@ -2745,7 +2754,6 @@ qboolean PM_CanInstall(const char *packagename) void PM_Command_f(void) { - size_t i; package_t *p; const char *act = Cmd_Argv(1); const char *key; @@ -2878,6 +2886,7 @@ void PM_Command_f(void) } Con_Printf("\n"); } +#ifdef WEBCLIENT else if (!strcmp(act, "sources") || !strcmp(act, "addsource")) { if (Cmd_Argc() == 2) @@ -2890,6 +2899,7 @@ void PM_Command_f(void) else PM_AddSubList(Cmd_Argv(2), "", true, true); } +#endif else if (!strcmp(act, "remsource")) PM_RemSubList(Cmd_Argv(2)); else if (!strcmp(act, "apply")) @@ -2908,8 +2918,10 @@ void PM_Command_f(void) { PM_RevertChanges(); } +#ifdef WEBCLIENT else if (!strcmp(act, "update")) { //flush package cache, make a new request. + int i; for (i = 0; i < numdownloadablelists; i++) downloadablelist[i].received = 0; } @@ -2924,6 +2936,7 @@ void PM_Command_f(void) else Con_Printf("Already using latest versions of all packages\n"); } +#endif else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable")) { //FIXME: make sure this updates. int arg = 2; @@ -2938,6 +2951,7 @@ void PM_Command_f(void) } PM_PrintChanges(); } +#ifdef WEBCLIENT else if (!strcmp(act, "reinstall")) { //fixme: favour the current verson. int arg = 2; @@ -2955,6 +2969,7 @@ void PM_Command_f(void) } PM_PrintChanges(); } +#endif else if (!strcmp(act, "disable") || !strcmp(act, "rem") || !strcmp(act, "remove")) { int arg = 2; @@ -3234,9 +3249,11 @@ static qboolean MD_Key (struct menucustom_s *c, struct menu_s *m, int key, unsig case DPF_USERMARKED: case DPF_MARKED: p->flags |= DPF_PURGE; +#ifdef WEBCLIENT //now: re-get despite already having it. if ((p->flags & DPF_CORRUPT) || ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))) break; //only makes sense if we already have a cached copy that we're not going to use. +#endif //fallthrough case DPF_USERMARKED|DPF_PURGE: case DPF_AUTOMARKED|DPF_PURGE: @@ -3444,10 +3461,13 @@ static void MD_Download_UpdateStatus(struct menu_s *m) dlmenu_t *info = m->data; int i, y; package_t *p; - unsigned int totalpackages=0, selectedpackages=0, addpackages=0, rempackages=0, downloads=0; + unsigned int totalpackages=0, selectedpackages=0, addpackages=0, rempackages=0; menuoption_t *si; menubutton_t *b, *d; +#ifdef WEBCLIENT + unsigned int downloads=0; menucustom_t *c; +#endif if (info->downloadablessequence != downloadablessequence || !info->populated) { diff --git a/engine/client/m_options.c b/engine/client/m_options.c index e6bd71042..4a3e60045 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -1062,6 +1062,7 @@ void M_Menu_Preset_f (void) item = 6; //fast else item = 7; //simple + item++; //the autosave option item -= bias; while (item --> 0) menu->selecteditem = menu->selecteditem->common.next; diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index b226c5ac6..3f6be24fd 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -306,15 +306,15 @@ static void S_Info(void); static void S_Shutdown_f(void); */ -static cvar_t s_al_debug = CVAR("s_al_debug", "0"); -static cvar_t s_al_use_reverb = CVAR("s_al_use_reverb", "1"); +static cvar_t s_al_debug = CVARD("s_al_debug", "0", "Enables periodic checks for OpenAL errors."); +static cvar_t s_al_use_reverb = CVARD("s_al_use_reverb", "1", "Controls whether reverb effects will be used. Set to 0 to block them. Reverb requires gamecode to configure the reverb properties, other than underwater."); static cvar_t s_al_max_distance = CVARFC("s_al_max_distance", "1000",0,OnChangeALSettings); -static cvar_t s_al_speedofsound = CVARFC("s_al_speedofsound", "343.3",0,OnChangeALSettings); -static cvar_t s_al_dopplerfactor = CVARFC("s_al_dopplerfactor", "1.0",0,OnChangeALSettings); -static cvar_t s_al_distancemodel = CVARFC("s_al_distancemodel", "2",0,OnChangeALSettings); -static cvar_t s_al_rolloff_factor = CVAR("s_al_rolloff_factor", "1"); -static cvar_t s_al_reference_distance = CVAR("s_al_reference_distance", "120"); -static cvar_t s_al_velocityscale = CVAR("s_al_velocityscale", "1"); +static cvar_t s_al_speedofsound = CVARFCD("s_al_speedofsound", "343.3",0,OnChangeALSettings, "Configures the speed of sound, in game units per second. This affects doppler."); +static cvar_t s_al_dopplerfactor = CVARFCD("s_al_dopplerfactor", "1.0",0,OnChangeALSettings, "Multiplies the strength of doppler effects."); +static cvar_t s_al_distancemodel = CVARFCD("s_al_distancemodel", legacyval("2","0"),0,OnChangeALSettings, "Controls how sounds fade with distance.\n0: Inverse (most realistic)\n1: Inverse Clamped\n2: Linear (Quake-like)\n3: Linear Clamped\n4: Exponential\n5: Exponential Clamped\n6: None"); +//static cvar_t s_al_rolloff_factor = CVAR("s_al_rolloff_factor", "1"); +static cvar_t s_al_reference_distance = CVARD("s_al_reference_distance", "120", "This is the distance at which the sound is audiable with standard volume in the inverse distance models. Nearer sounds will be louder than the original sample."); +static cvar_t s_al_velocityscale = CVARD("s_al_velocityscale", "1", "Rescales velocity values, before doppler can be calculated."); static cvar_t s_al_static_listener = CVAR("s_al_static_listener", "0"); //cheat extern cvar_t snd_doppler; @@ -503,7 +503,7 @@ static void QDECL OpenAL_CvarInit(void) Cvar_Register(&s_al_dopplerfactor, SOUNDVARS); Cvar_Register(&s_al_distancemodel, SOUNDVARS); Cvar_Register(&s_al_reference_distance, SOUNDVARS); - Cvar_Register(&s_al_rolloff_factor, SOUNDVARS); +// Cvar_Register(&s_al_rolloff_factor, SOUNDVARS); Cvar_Register(&s_al_velocityscale, SOUNDVARS); Cvar_Register(&s_al_static_listener, SOUNDVARS); Cvar_Register(&s_al_speedofsound, SOUNDVARS); diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index 5855828c4..15ffe9d38 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -802,8 +802,7 @@ static void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext) #if defined(__i386__) //x86 signals don't leave the stack in a clean state, so replace the signal handler with the real crash address, and hide this function - ucontext_t *uc = vcontext; - array[1] = (void*)uc->uc_mcontext.gregs[REG_EIP]; + array[1] = (void*)((ucontext_t*)vcontext)->uc_mcontext.gregs[REG_EIP]; firstframe = 1; #elif defined(__amd64__) //amd64 is sane enough, but this function and the libc signal handler are on the stack, and should be ignored. @@ -995,7 +994,6 @@ int main (int c, const char **v) #endif TL_InitLanguages(parms.binarydir); - 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 diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index dbea5d859..0d8401be5 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -3362,10 +3362,15 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod galias->numanimations++; intervals = (daliasinterval_t *)(ingroup+1); - sinter = LittleFloat(intervals->interval); - if (sinter <= 0) - sinter = 0.1; - frame->rate = 1/sinter; + if (frame->numposes == 0) + frame->rate = 10; + else + { + sinter = LittleFloat(intervals->interval); + if (sinter <= 0) + sinter = 0.1; + frame->rate = 1/sinter; + } pinframe = (dtrivertx_t *)(intervals+frame->numposes); for (k = 0; k < frame->numposes; k++) diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 246251d1c..15b07cd85 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -52,7 +52,8 @@ typedef struct #endif qboolean loop; int numposes; - float rate; + //float *poseendtime; //first starts at 0, anim duration is poseendtime[numposes-1] + float rate; //average framerate of animation. #ifdef NONSKELETALMODELS galiaspose_t *poseofs; #endif @@ -219,7 +220,7 @@ typedef struct modplugfuncs_s void (QDECL *UnRegisterModelFormat)(int idx); void (QDECL *UnRegisterAllModelFormats)(void); - void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, int size); //ctx=&mod->memgroup and the data will be freed when the model is freed. + void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, size_t size); //ctx=&mod->memgroup and the data will be freed when the model is freed. void (QDECL *ConcatTransforms) (const float in1[3][4], const float in2[3][4], float out[3][4]); void (QDECL *M3x4_Invert) (const float *in1, float *out); diff --git a/engine/common/common.h b/engine/common/common.h index ae01b4b87..8c339fa13 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -131,6 +131,13 @@ typedef enum {false, true} qboolean; #define MAX_SERVERINFO_STRING 1024 //standard quake has 512 here. #define MAX_LOCALINFO_STRING 32768 + +#ifdef HAVE_LEGACY +#define legacyval(_legval,_newval) _legval +#else +#define legacyval(_legval,_newval) _newval +#endif + #ifdef HAVE_CLIENT #define cls_state cls.state #else @@ -448,7 +455,7 @@ struct cache_user_s; extern char com_gamepath[MAX_OSPATH]; extern char com_homepath[MAX_OSPATH]; -extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in +//extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in //extern char *com_basedir; //qofs_Make is used to 'construct' a variable of qofs_t type. this is so the code can merge two 32bit ints on old systems and use a long long type internally without generating warnings about bit shifts when qofs_t is only 32bit instead. diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index c8d0e616e..9248f9bb8 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -137,6 +137,7 @@ #define HAVE_WINSSPI //on windows #define FTPSERVER //sv_ftp cvar. #define WEBCLIENT //uri_get+any internal downloads etc +#define HAVE_HTTPSV //net_enable_http/websocket #define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo. //#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea. #define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections. diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 9e344f434..0dba5eaae 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -144,7 +144,7 @@ typedef struct cvar_group_s #define CVAR_WATCHED (1<<22) //report any attempts to change this cvar. #define CVAR_VIDEOLATCH (1<<23) -#define CVAR_LASTFLAG CVAR_SHADERSYSTEM +#define CVAR_LASTFLAG CVAR_VIDEOLATCH #define CVAR_LATCHMASK (CVAR_LATCH|CVAR_RENDERERLATCH|CVAR_SERVEROVERRIDE|CVAR_CHEAT|CVAR_SEMICHEAT) //you're only allowed one of these. #define CVAR_NEEDDEFAULT CVAR_CHEAT diff --git a/engine/common/fs.c b/engine/common/fs.c index b96cc34f2..f264f780e 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -172,14 +172,15 @@ char gamedirfile[MAX_OSPATH]; char pubgamedirfile[MAX_OSPATH]; //like gamedirfile, but not set to the fte-only paths - +searchpath_t *gameonly_homedir; +searchpath_t *gameonly_gamedir; char com_gamepath[MAX_OSPATH]; //c:\games\quake char com_homepath[MAX_OSPATH]; //c:\users\foo\my docs\fte\quake qboolean com_homepathenabled; qboolean com_homepathusable; //com_homepath is safe, even if not enabled. -char com_configdir[MAX_OSPATH]; //homedir/fte/configs +//char com_configdir[MAX_OSPATH]; //homedir/fte/configs int fs_hash_dups; int fs_hash_files; @@ -737,7 +738,7 @@ static void COM_Path_f (void) Con_Printf("pubgamedirfile: \"%s\"\n", pubgamedirfile); Con_Printf("com_gamepath: \"%s\"\n", com_gamepath); Con_Printf("com_homepath: \"%s\" (enabled: %s, usable: %s)\n", com_homepath, com_homepathenabled?"yes":"no", com_homepathusable?"yes":"no"); - Con_Printf("com_configdir: \"%s\"\n", com_configdir); +// Con_Printf("com_configdir: \"%s\"\n", com_configdir); if (fs_manifest) FS_Manifest_Print(fs_manifest); return; @@ -1915,6 +1916,9 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela fs_accessed_time = realtime; + if (fs_readonly && *mode == 'w') + return NULL; + if (relativeto == FS_SYSTEM) return VFSOS_Open(filename, mode); @@ -1937,23 +1941,48 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela //if there can only be one file (eg: write access) find out where it is. switch (relativeto) { - case FS_GAMEONLY: //OS access only, no paks + case FS_GAMEONLY: //OS access only, no paks. Used for (re)writing files. vfs = NULL; + //FIXME: go via a searchpath, because then the fscache can be selectively updated if (com_homepathenabled) { - if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homepath, gamedirfile, filename)) - return NULL; - if (*mode == 'w') - COM_CreatePath(fullname); - vfs = VFSOS_Open(fullname, mode); + if (gameonly_homedir) + { + if ((*mode == 'w') + ? gameonly_homedir->handle->CreateFile(gameonly_homedir->handle, &loc, filename) + : gameonly_homedir->handle->FindFile (gameonly_homedir->handle, &loc, filename, NULL)) + vfs = gameonly_homedir->handle->OpenVFS (gameonly_homedir->handle, &loc, mode); + else + vfs = NULL; + } + else + { + if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_homepath, gamedirfile, filename)) + return NULL; + if (*mode == 'w') + COM_CreatePath(fullname); + vfs = VFSOS_Open(fullname, mode); + } } if (!vfs && *gamedirfile) { - if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_gamepath, gamedirfile, filename)) - return NULL; - if (*mode == 'w') - COM_CreatePath(fullname); - vfs = VFSOS_Open(fullname, mode); + if (gameonly_gamedir) + { + if ((*mode == 'w') + ? gameonly_gamedir->handle->CreateFile(gameonly_gamedir->handle, &loc, filename) + : gameonly_gamedir->handle->FindFile (gameonly_gamedir->handle, &loc, filename, NULL)) + vfs = gameonly_gamedir->handle->OpenVFS (gameonly_gamedir->handle, &loc, mode); + else + vfs = NULL; + } + else + { + if (!try_snprintf(fullname, sizeof(fullname), "%s%s/%s", com_gamepath, gamedirfile, filename)) + return NULL; + if (*mode == 'w') + COM_CreatePath(fullname); + vfs = VFSOS_Open(fullname, mode); + } } if (vfs || !(*mode == 'w' || *mode == 'a')) return vfs; @@ -2793,7 +2822,7 @@ Sets com_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ -static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags) +static searchpath_t *FS_AddSingleGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags) { unsigned int keptflags; searchpath_t *search; @@ -2808,7 +2837,7 @@ static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, c if (!Q_strcasecmp(search->logicalpath, dir)) { search->flags |= flags & SPF_WRITABLE; - return; //already loaded (base paths?) + return search; //already loaded (base paths?) } } @@ -2831,7 +2860,20 @@ static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, c if (!handle) handle = VFSOS_OpenPath(NULL, NULL, dir, dir, ""); - FS_AddPathHandle(oldpaths, puredir, dir, handle, "", flags|keptflags, loadstuff); + return FS_AddPathHandle(oldpaths, puredir, dir, handle, "", flags|keptflags, loadstuff); +} +static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, unsigned int loadstuff, unsigned int flags) +{ + char syspath[MAX_OSPATH]; + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, puredir); + gameonly_gamedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags&~(com_homepathenabled?SPF_WRITABLE:0u)); + if (com_homepathenabled) + { + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, puredir); + gameonly_homedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags); + } + else + gameonly_homedir = NULL; } //if syspath, something like c:\quake\baseq2 @@ -3698,7 +3740,6 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) searchpath_t *next; int i; int orderkey; - char syspath[MAX_OSPATH]; COM_AssertMainThread("FS_ReloadPackFilesFlags"); COM_WorkerFullSync(); @@ -3715,6 +3756,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) com_searchpaths = NULL; com_purepaths = NULL; com_base_searchpaths = NULL; + gameonly_gamedir = gameonly_homedir = NULL; i = COM_CheckParm ("-basepack"); while (i && i < com_argc-1) @@ -3777,23 +3819,11 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) else if (*dir == '*') { //paths with a leading * are private, and not announced to clients that ask what the current gamedir is. - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir+1); - FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir+1); - FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|SPF_WRITABLE); - } + FS_AddGameDirectory(&oldpaths, dir+1, reloadflags, SPF_EXPLICIT|SPF_PRIVATE|SPF_WRITABLE); } else { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir); - FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - { - Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir); - FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); - } + FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); } } } @@ -3823,9 +3853,7 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags) } else { - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_gamepath, dir), reloadflags, SPF_EXPLICIT|(com_homepathenabled?0:SPF_WRITABLE)); - if (com_homepathenabled) - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homepath, dir), reloadflags, SPF_EXPLICIT|SPF_WRITABLE); + FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE); } } } @@ -6330,7 +6358,6 @@ static void COM_InitHomedir(ftemanifest_t *man) //but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead //we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't. //new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead - char *ev = getenv("FTEHOME"); if (ev && *ev) { diff --git a/engine/common/fs.h b/engine/common/fs.h index 70c4a3354..83d933646 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -49,9 +49,9 @@ struct searchpathfuncs_s qboolean (QDECL *PollChanges)(searchpathfuncs_t *handle); //returns true if there were changes qboolean (QDECL *FileStat)(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime); - qboolean (QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname); //returns true on success, false if source doesn't exist, or if dest does. + qboolean (QDECL *CreateFile)(searchpathfuncs_t *handle, flocation_t *loc, const char *filename); //like FindFile, but returns a usable loc even if the file does not exist yet (may also create requisite directories too) + qboolean (QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname); //returns true on success, false if source doesn't exist, or if dest does (cached locs may refer to either new or old name). qboolean (QDECL *RemoveFile)(searchpathfuncs_t *handle, const char *filename); //returns true on success, false if it wasn't found or is readonly. - qboolean (QDECL *MkDir)(searchpathfuncs_t *handle, const char *filename); //is this really needed? }; //searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc); //returns a handle to a new pak/path @@ -87,7 +87,7 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), #define SPF_TEMPORARY 4 //a map-specific path, purged at map change. #define SPF_EXPLICIT 8 //a root gamedir (bumps depth on gamedir depth checks). #define SPF_UNTRUSTED 16 //has been downloaded from somewhere. configs inside it should never be execed with local access rights. -#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. +#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. name is not networked. #define SPF_WRITABLE 64 //safe to write here. lots of weird rules etc. #define SPF_BASEPATH 128 //part of the basegames, and not the mod gamedir(s). qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags); diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index a9be3e132..22a20fb4d 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -271,6 +271,30 @@ static void QDECL FSSTDIO_BuildHash(searchpathfuncs_t *handle, int depth, void ( sp->AddFileHash = AddFileHash; Sys_EnumerateFiles(sp->rootpath, "*", FSSTDIO_RebuildFSHash, AddFileHash, handle); } + +static unsigned int QDECL FSSTDIO_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename) +{ + stdiopath_t *sp = (void*)handle; + char *ofs; + + loc->len = 0; + loc->offset = 0; + loc->fhandle = handle; + if ((unsigned int)snprintf(loc->rawname, sizeof(loc->rawname), "%s/%s", sp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; //too long... + + for (ofs = loc->rawname+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (loc->rawname); + *ofs = '/'; + } + } + + return FF_FOUND; +} static unsigned int QDECL FSSTDIO_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) { stdiopath_t *sp = (void*)handle; @@ -289,7 +313,8 @@ static unsigned int QDECL FSSTDIO_FLocate(searchpathfuncs_t *handle, flocation_t */ // check a file in the directory tree - snprintf (netpath, sizeof(netpath)-1, "%s/%s", sp->rootpath, filename); + if ((unsigned int)snprintf (netpath, sizeof(netpath), "%s/%s", sp->rootpath, filename) > sizeof(netpath)-1) + return FF_NOTFOUND; #ifdef ANDROID { @@ -378,6 +403,7 @@ searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs np->pub.OpenVFS = FSSTDIO_OpenVFS; np->pub.PollChanges = FSSTDIO_PollChanges; np->pub.FileStat = FSSTDIO_FileStat; + np->pub.CreateFile = FSSTDIO_CreateLoc; return &np->pub; } diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 34a2a42f0..1b4a8e30f 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -522,6 +522,30 @@ static void QDECL VFSW32_BuildHash(searchpathfuncs_t *handle, int hashdepth, voi wp->hashdepth = hashdepth; Sys_EnumerateFiles(wp->rootpath, "*", VFSW32_RebuildFSHash, AddFileHash, handle); } +static unsigned int QDECL VFSW32_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename) +{ + vfsw32path_t *wp = (void*)handle; + char *ofs; + + loc->len = 0; + loc->offset = 0; + loc->fhandle = handle; + loc->rawname[sizeof(loc->rawname)-1] = 0; + if ((unsigned int)snprintf (loc->rawname, sizeof(loc->rawname), "%s/%s", wp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; + for (ofs = loc->rawname+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; + Sys_mkdir (loc->rawname); + *ofs = '/'; + } + } + + return FF_FOUND; +} + #include static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult) { @@ -545,7 +569,8 @@ static unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t */ // check a file in the directory tree - snprintf (netpath, sizeof(netpath)-1, "%s/%s", wp->rootpath, filename); + if ((unsigned int)snprintf (netpath, sizeof(netpath), "%s/%s", wp->rootpath, filename) > sizeof(loc->rawname)-1) + return FF_NOTFOUND; if (!WinNT) { @@ -634,16 +659,6 @@ static qboolean QDECL VFSW32_RemoveFile(searchpathfuncs_t *handle, const char *f snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); return Sys_remove(syspath); } -static qboolean QDECL VFSW32_MkDir(searchpathfuncs_t *handle, const char *filename) -{ - vfsw32path_t *wp = (vfsw32path_t*)handle; - char syspath[MAX_OSPATH]; - if (fs_readonly) - return false; - snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename); - Sys_mkdir(syspath); - return true; -} searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix) { @@ -675,9 +690,10 @@ searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_ np->pub.FileStat = VFSW32_FileStat; +#undef CreateFile //stoopid windows.h + np->pub.CreateFile = VFSW32_CreateLoc; np->pub.RenameFile = VFSW32_RenameFile; np->pub.RemoveFile = VFSW32_RemoveFile; - np->pub.MkDir = VFSW32_MkDir; return &np->pub; } diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 04886554a..2b6d09cae 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -313,7 +313,8 @@ typedef struct { struct { - int cp[2]; + unsigned short cp[2]; + unsigned short fixedres[2]; } patch; struct { @@ -527,7 +528,7 @@ static int Patch_FlatnessTest( float maxflat2, const float *point0, const float Patch_GetFlatness =============== */ -static void Patch_GetFlatness( float maxflat, const float *points, int comp, const int *patch_cp, int *flat ) +static void Patch_GetFlatness( float maxflat, const float *points, int comp, const unsigned short *patch_cp, int *flat ) { int i, p, u, v; float maxflat2 = maxflat * maxflat; @@ -579,7 +580,7 @@ static void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const ve Patch_Evaluate =============== */ -static void Patch_Evaluate( const vec_t *p, const int *numcp, const int *tess, vec_t *dest, int comp ) +static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const int *tess, vec_t *dest, int comp ) { int num_patches[2], num_tess[2]; int index[3], dstpitch, i, u, v, x, y; @@ -850,7 +851,7 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver /* * CM_CreatePatch */ -static void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const int *patch_cp ) +static void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const unsigned short *patch_cp, const unsigned short *patch_subdiv) { int step[2], size[2], flat[2]; int i, j, k ,u, v; @@ -1088,7 +1089,7 @@ static qboolean CM_CreatePatchForFace (model_t *loadmodel, cminfo_t *prv, mleaf_ checkout[facenum] = prv->numpatches++; //gcc warns without this cast - CM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(prv->verts + face->firstvert), face->patch.cp ); + CM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(prv->verts + face->firstvert), face->patch.cp, face->patch.fixedres ); } leaf->contents |= patch->surface->c.value; leaf->numleafpatches++; @@ -2554,8 +2555,12 @@ static qboolean CModRBSP_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l) if (out->facetype == MST_PATCH) { - out->patch.cp[0] = LittleLong ( in->patchwidth ); - out->patch.cp[1] = LittleLong ( in->patchheight ); + unsigned int w = LittleLong ( in->patchwidth ); + unsigned int h = LittleLong ( in->patchheight ); + out->patch.cp[0] = w&0xffff; + out->patch.cp[1] = h&0xffff; + out->patch.fixedres[0]=w>>16; + out->patch.fixedres[1]=h>>16; } else { @@ -2658,11 +2663,12 @@ static index_t tempIndexesArray[MAX_ARRAY_VERTS*6]; static void GL_SizePatch(mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert, cminfo_t *prv) { - int patch_cp[2], step[2], size[2], flat[2]; + unsigned short patch_cp[2]; + int step[2], size[2], flat[2]; float subdivlevel; - patch_cp[0] = patchwidth; - patch_cp[1] = patchheight; + patch_cp[0] = patchwidth&0xffff; + patch_cp[1] = patchheight&0xffff; if (patch_cp[0] <= 0 || patch_cp[1] <= 0 ) { @@ -2692,13 +2698,14 @@ static void GL_SizePatch(mesh_t *mesh, int patchwidth, int patchheight, int numv static void GL_CreateMeshForPatch (model_t *mod, mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert) { cminfo_t *prv = (cminfo_t*)mod->meshinfo; - int numindexes, patch_cp[2], step[2], size[2], flat[2], i, u, v, p; + int numindexes, step[2], size[2], flat[2], i, u, v, p; + unsigned short patch_cp[2]; index_t *indexes; float subdivlevel; int sty; - patch_cp[0] = patchwidth; - patch_cp[1] = patchheight; + patch_cp[0] = patchwidth&0xffff; + patch_cp[1] = patchheight&0xffff; if (patch_cp[0] <= 0 || patch_cp[1] <= 0 ) { diff --git a/engine/common/net.h b/engine/common/net.h index e51960ccb..0ed4269db 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -209,9 +209,10 @@ typedef struct float nqreliable_resendtime;//force nqreliable_allowed, thereby forcing a resend of anything n qbyte nqunreliableonly; //nq can't cope with certain reliables some times. if 2, we have a reliable that result in a block (that should be sent). if 1, we are blocking. if 0, we can send reliables freely. if 3, then we just want to ignore clc_moves #endif + qboolean pext_fragmentation; //fte's packet fragmentation extension, to avoid issues with low mtus. struct netprim_s netprim; - int fragmentsize; - int dupe; + int mtu; //the path mtu, if known + int dupe; //how many times to dupe packets float last_received; // for timeouts diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 70a3e91b5..1887a7df6 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -692,7 +692,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (chan->message.overflowed) { chan->fatal_error = true; - Con_TPrintf ("%s:Outgoing message overflow\n" + Con_TPrintf ("%s: Outgoing message overflow\n" , NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address)); return 0; } @@ -716,7 +716,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) // write the packet header send.data = send_buf; - send.maxsize = MAX_QWMSGLEN + PACKET_HEADER; + send.maxsize = (chan->mtu?chan->mtu:MAX_QWMSGLEN) + PACKET_HEADER; send.cursize = 0; w1 = chan->outgoing_sequence | (send_reliable<<31); @@ -738,9 +738,9 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) } #endif - if (chan->fragmentsize) + if (chan->pext_fragmentation) { - //allow the max size to be bigger + //allow the max size to be bigger, sending everything available send.maxsize = MAX_OVERALLMSGLEN + PACKET_HEADER; MSG_WriteShort(&send, 0); } @@ -748,6 +748,17 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) // copy the reliable message to the packet first if (send_reliable) { + if (send.maxsize - send.cursize < chan->reliable_length) + { + if (!chan->fatal_error) + { + chan->fatal_error = true; + Con_TPrintf ("%s: Path MTU is lower than %u\n" + , NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address), chan->reliable_length); + } + chan->outgoing_sequence--; + return 0; + } SZ_Write (&send, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence; } @@ -765,7 +776,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) if (chan->compresstable) { //int oldsize = send.cursize; - Huff_CompressPacket(chan->compresstable, &send, 8 + ((chan->sock == NS_CLIENT)?2:0) + (chan->fragmentsize?2:0)); + Huff_CompressPacket(chan->compresstable, &send, 8 + ((chan->sock == NS_CLIENT)?2:0) + (chan->pext_fragmentation?2:0)); // Con_Printf("%i becomes %i\n", oldsize, send.cursize); // Huff_DecompressPacket(&send, (chan->sock == NS_CLIENT)?10:8); } @@ -779,20 +790,23 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) { int hsz = 10 + ((chan->sock == NS_CLIENT)?chan->qportsize:0); /*header size, if fragmentation is in use*/ - if ((!chan->fragmentsize))// || send.cursize < ((chan->fragmentsize - hsz)&~7)) - { + if ((!chan->pext_fragmentation))// || send.cursize < ((chan->mtu - hsz)&~7)) + { //vanilla sends for (i = -1; i < chan->dupe && e == NETERR_SENT; i++) e = NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); send.cursize += send.cursize * i; - if (e == NETERR_MTU && chan->fragmentsize > 560) + //ipv4 'guarentees' mtu sizes of at least 560ish. + //our reliable/backbuf messages are limited to 1024 bytes. + //this means that large reliables may be unsendable. + if (e == NETERR_MTU && chan->mtu > 560) { - Con_Printf("Reducing MSS to %i\n", chan->fragmentsize); - chan->fragmentsize -= 10; + Con_Printf("Reducing MSS to %i\n", chan->mtu); + chan->mtu -= 10; } } else - { + { //fte's fragmentaton protocol int offset = 0, no; qboolean more; @@ -801,7 +815,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) /*send the additional parts, adding new headers within the previous packet*/ do { - no = offset + chan->fragmentsize - hsz; + no = offset + chan->mtu - hsz; if (no < send.cursize-hsz) { no &= ~7; @@ -831,10 +845,10 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) for (i = -1; i < chan->dupe && e == NETERR_SENT; i++) { e = NET_SendPacket (chan->sock, (no - offset) + hsz, send.data + offset, &chan->remote_address); - if (e == NETERR_MTU && !offset && chan->fragmentsize > 560) + if (e == NETERR_MTU && !offset && chan->mtu > 560) { - chan->fragmentsize -= 16; - Con_Printf("Reducing MSS to %i\n", chan->fragmentsize); + chan->mtu -= 16; + Con_Printf("Reducing MSS to %i\n", chan->mtu); no = offset; more = true; break; @@ -917,7 +931,7 @@ qboolean Netchan_Process (netchan_t *chan) MSG_ReadShort (); #endif - if (chan->fragmentsize) + if (chan->pext_fragmentation) offset = (unsigned short)MSG_ReadShort(); else offset = 0; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index bb8ac2eca..afe469856 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -101,9 +101,9 @@ FTE_ALIGN(4) qbyte net_message_buffer[MAX_OVERALLMSGLEN]; #define HAVE_NATPMP #endif -#if defined(HAVE_SERVER) || defined(MASTERONLY) - #define HAVE_HTTPSV -#endif +//#if !defined(HAVE_SERVER) && !defined(MASTERONLY) +// #undef HAVE_HTTPSV +//#endif void NET_GetLocalAddress (int socket, netadr_t *out); //int TCP_OpenListenSocket (const char *localip, int port); @@ -127,6 +127,7 @@ cvar_t net_enable_qtv = CVARD("net_enable_qtv", "1", "Listens for qtv proxie #if defined(HAVE_SSL) cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "If enabled, binary data sent to a non-tls tcp port will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); #endif +#ifdef HAVE_HTTPSV #ifdef SV_MASTER cvar_t net_enable_http = CVARD("net_enable_http", "1", "If enabled, tcp ports will accept http clients, potentially serving large files which could distrupt gameplay."); #else @@ -135,6 +136,7 @@ cvar_t net_enable_http = CVARD("net_enable_http", "0", "If enabled, tcp port cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled, tcp ports will accept websocket game clients."); cvar_t net_enable_webrtcbroker = CVARD("net_enable_webrtcbroker", "0", "If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections."); #endif +#endif #if defined(HAVE_DTLS) && defined(HAVE_SERVER) static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue) { @@ -3693,10 +3695,10 @@ typedef struct ftenet_tcpconnect_stream_s { { TCPC_UNKNOWN, //waiting to see what they send us. TCPC_QIZMO, //'qizmo\n' handshake, followed by packets prefixed with a 16bit packet length. +#ifdef HAVE_HTTPSV TCPC_WEBSOCKETU, //utf-8 encoded data. TCPC_WEBSOCKETB, //binary encoded data (subprotocol = 'binary') TCPC_WEBSOCKETNQ, //raw nq msg buffers with no encapsulation or handshake -#ifdef HAVE_HTTPSV TCPC_HTTPCLIENT, //we're sending a file to this victim. TCPC_WEBRTC_CLIENT, //for brokering webrtc connections, doesn't carry any actual game data itself. TCPC_WEBRTC_HOST //for brokering webrtc connections, doesn't carry any actual game data itself. @@ -4718,8 +4720,8 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcpconnect_connection_t *con, ftenet return FTENET_TCPConnect_HTTPResponse(st, arg, acceptsgzip); } } - -#ifdef HAVE_SSL +#endif +#if defined(HAVE_SSL) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytestoread) { if (bytestoread > net_message.cursize) @@ -4730,7 +4732,6 @@ static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytes return bytestoread; } #endif -#endif void FTENET_TCPConnect_PrintStatus(ftenet_generic_connection_t *gcon) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; @@ -4749,12 +4750,12 @@ void FTENET_TCPConnect_PrintStatus(ftenet_generic_connection_t *gcon) case TCPC_QIZMO: Con_Printf("qizmo %s\n", adr); break; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: case TCPC_WEBSOCKETNQ: Con_Printf("websocket %s\n", adr); break; -#ifdef HAVE_HTTPSV case TCPC_HTTPCLIENT: Con_Printf("http %s\n", adr); break; @@ -5011,13 +5012,12 @@ closesvstream: net_from = st->remoteaddr; return true; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: case TCPC_WEBSOCKETNQ: -#ifdef HAVE_HTTPSV case TCPC_WEBRTC_HOST: case TCPC_WEBRTC_CLIENT: -#endif while (st->inlen >= 2) { unsigned short ctrl = ((unsigned char*)st->inbuffer)[0]<<8 | ((unsigned char*)st->inbuffer)[1]; @@ -5164,7 +5164,6 @@ closesvstream: Con_TPrintf ("Warning: Oversize packet from %s\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); goto closesvstream; } -#ifdef HAVE_HTTPSV #ifdef SUPPORT_RTC_ICE if (st->clienttype == TCPC_WEBRTC_CLIENT && !*st->webrtc.resource) { //this is a client that's corrected directly to us via webrtc. @@ -5203,7 +5202,6 @@ closesvstream: net_message.cursize = 0; } else -#endif #ifdef NQPROT if (st->clienttype == TCPC_WEBSOCKETNQ) { //hack in an 8-byte header @@ -5248,6 +5246,7 @@ closesvstream: } } break; +#endif } } @@ -5298,7 +5297,6 @@ closesvstream: neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to) { - neterr_t e; ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; ftenet_tcpconnect_stream_t *st; @@ -5333,6 +5331,7 @@ neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len } } break; +#ifdef HAVE_HTTPSV case TCPC_WEBSOCKETNQ: if (length < 8 || ((char*)data)[0] & 0x80) break; @@ -5344,10 +5343,13 @@ neterr_t FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len //fallthrough case TCPC_WEBSOCKETU: case TCPC_WEBSOCKETB: - e = FTENET_TCPConnect_WebSocket_Splurge(st, (st->clienttype==TCPC_WEBSOCKETU)?1:2, data, length); - if (e != NETERR_SENT) - return e; + { + neterr_t e = FTENET_TCPConnect_WebSocket_Splurge(st, (st->clienttype==TCPC_WEBSOCKETU)?1:2, data, length); + if (e != NETERR_SENT) + return e; + } break; +#endif default: break; } @@ -7877,10 +7879,12 @@ void NET_Init (void) #if defined(HAVE_SSL) Cvar_Register(&net_enable_tls, "networking"); #endif +#ifdef HAVE_HTTPSV Cvar_Register(&net_enable_http, "networking"); Cvar_Register(&net_enable_websockets, "networking"); Cvar_Register(&net_enable_webrtcbroker, "networking"); #endif +#endif diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 8a8e73926..cb75d8eb1 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -125,7 +125,7 @@ void Plug_RegisterBuiltin(char *name, Plug_Builtin_t bi, int flags) } //got an empty number. - Con_DPrintf("%s: %i\n", name, newnum); + Con_DLPrintf(2, "%s: %i\n", name, newnum); plugbuiltins[newnum].name = name; plugbuiltins[newnum].func = bi; plugbuiltins[newnum].flags = flags; diff --git a/engine/common/sys_linux_threads.c b/engine/common/sys_linux_threads.c index b0d9b9f61..931a768b3 100644 --- a/engine/common/sys_linux_threads.c +++ b/engine/common/sys_linux_threads.c @@ -364,7 +364,9 @@ pubsubserver_t *Sys_ForkServer(void) //make sure we're fully synced, so that workers can't mess up Cvar_Set(Cvar_FindVar("worker_count"), "0"); COM_WorkerFullSync(); +#ifdef WEBCLIENT DL_DeThread(); +#endif #ifdef SQL SQL_KillServers(); //FIXME: this is bad... #endif diff --git a/engine/common/zone.c b/engine/common/zone.c index 5275e2781..c9331b1d9 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -52,11 +52,11 @@ qbyte sentinalkey; #define TAGLESS 1 -int zmemtotal; -int zmemdelta; +size_t zmemtotal; +size_t zmemdelta; typedef struct memheader_s { - int size; + size_t size; int tag; } memheader_t; @@ -108,13 +108,13 @@ static void Z_DumpTree(void) } #endif -void *VARGS Z_TagMalloc(int size, int tag) +void *Z_TagMalloc(size_t size, int tag) { zone_t *zone; zone = (zone_t *)malloc(size + sizeof(zone_t)); if (!zone) - Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("Z_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); Q_memset(zone, 0, size + sizeof(zone_t)); zone->mh.tag = tag; zone->mh.size = size; @@ -184,7 +184,7 @@ void *Z_MallocNamed(int size, char *file, int line) return mem; } #else -void *ZF_Malloc(int size) +void *ZF_Malloc(size_t size) { #ifdef ANDROID void *ret = NULL; @@ -202,11 +202,11 @@ void *ZF_Malloc(int size) return calloc(size, 1); #endif } -void *Z_Malloc(int size) +void *Z_Malloc(size_t size) { void *mem = ZF_Malloc(size); if (!mem) - Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("Z_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); return mem; } @@ -423,7 +423,7 @@ void *BZF_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but return mem; } #else -void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc. +void *BZF_Malloc(size_t size) //BZ_Malloc but allowed to fail - like straight malloc. { void *mem; mem = malloc(size); @@ -446,11 +446,11 @@ void *BZ_MallocNamed(int size, const char *file, int line) //BZ_MallocNamed but return mem; } #else -void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensitive data structures. +void *BZ_Malloc(size_t size) //Doesn't clear. The expectation is a large file, rather than sensitive data structures. { void *mem = BZF_Malloc(size); if (!mem) - Sys_Error("BZ_Malloc: Failed on allocation of %i bytes", size); + Sys_Error("BZ_Malloc: Failed on allocation of %"PRIuSIZE" bytes", size); return mem; } @@ -472,17 +472,17 @@ void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line) return mem; } #else -void *BZF_Realloc(void *data, int newsize) +void *BZF_Realloc(void *data, size_t newsize) { return realloc(data, newsize); } -void *BZ_Realloc(void *data, int newsize) +void *BZ_Realloc(void *data, size_t newsize) { void *mem = BZF_Realloc(data, newsize); if (!mem) - Sys_Error("BZ_Realloc: Failed on reallocation of %i bytes", newsize); + Sys_Error("BZ_Realloc: Failed on reallocation of %"PRIuSIZE" bytes", newsize); return mem; } @@ -508,7 +508,7 @@ typedef struct zonegroupblock_s void *QDECL ZG_Malloc(zonegroup_t *ctx, int size){return ZG_MallocNamed(ctx, size, "ZG_Malloc", size);} void *ZG_MallocNamed(zonegroup_t *ctx, int size, char *file, int line) #else -void *QDECL ZG_Malloc(zonegroup_t *ctx, int size) +void *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size) #endif { zonegroupblock_t *newm; @@ -590,7 +590,7 @@ void Hunk_TempFree(void) //allocates without clearing previous temp. //safer than my hack that fuh moaned about... -void *Hunk_TempAllocMore (int size) +void *Hunk_TempAllocMore (size_t size) { void *buf; @@ -624,7 +624,7 @@ void *Hunk_TempAllocMore (int size) } -void *Hunk_TempAlloc (int size) +void *Hunk_TempAlloc (size_t size) { Hunk_TempFree(); @@ -657,8 +657,8 @@ void Cache_Flush(void) static void Hunk_Print_f (void) { - Con_Printf("Z Delta: %iKB\n", zmemdelta/1024); zmemdelta = 0; - Con_Printf("Z Total: %iKB\n", zmemtotal/1024); + Con_Printf("Z Delta: %"PRIuSIZE"KB\n", zmemdelta/1024); zmemdelta = 0; + Con_Printf("Z Total: %"PRIuSIZE"KB\n", zmemtotal/1024); //note: Zone memory isn't tracked reliably. we don't track the mem that is freed, so it'll just climb and climb //we don't track reallocs either. diff --git a/engine/common/zone.h b/engine/common/zone.h index f8868869c..4c8da9aad 100644 --- a/engine/common/zone.h +++ b/engine/common/zone.h @@ -87,12 +87,12 @@ void Memory_Init (void); void Memory_DeInit(void); void VARGS Z_Free (void *ptr); -void *Z_Malloc (int size); // returns 0 filled memory -void *ZF_Malloc (int size); // allowed to fail -void *Z_MallocNamed (int size, char *file, int line); // returns 0 filled memory -void *ZF_MallocNamed (int size, char *file, int line); // allowed to fail +void *Z_Malloc (size_t size); // returns 0 filled memory +void *ZF_Malloc (size_t size); // allowed to fail +void *Z_MallocNamed (size_t size, char *file, int line); // returns 0 filled memory +void *ZF_MallocNamed (size_t size, char *file, int line); // allowed to fail //#define Z_Malloc(x) Z_MallocNamed2(x, __FILE__, __LINE__ ) -void *VARGS Z_TagMalloc (int size, int tag); +void *Z_TagMalloc (size_t size, int tag); void VARGS Z_TagFree(void *ptr); void VARGS Z_FreeTags(int tag); qboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize); //returns false on error @@ -101,14 +101,14 @@ qboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelement //Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensitive data structures. //(this is a nicer name for malloc) -void *BZ_Malloc(int size); -void *BZF_Malloc(int size); -void *BZ_MallocNamed (int size, const char *file, int line); // returns 0 filled memory -void *BZF_MallocNamed (int size, const char *file, int line); // allowed to fail -void *BZ_Realloc(void *ptr, int size); -void *BZ_ReallocNamed(void *data, int newsize, const char *file, int line); -void *BZF_Realloc(void *data, int newsize); -void *BZF_ReallocNamed(void *data, int newsize, const char *file, int line); +void *BZ_Malloc(size_t size); +void *BZF_Malloc(size_t size); +void *BZ_MallocNamed (size_t size, const char *file, int line); // returns 0 filled memory +void *BZF_MallocNamed (size_t size, const char *file, int line); // allowed to fail +void *BZ_Realloc(void *ptr, size_t size); +void *BZ_ReallocNamed(void *data, size_t newsize, const char *file, int line); +void *BZF_Realloc(void *data, size_t newsize); +void *BZF_ReallocNamed(void *data, size_t newsize, const char *file, int line); void BZ_Free(void *ptr); //ctx should start off as void*ctx=NULL @@ -117,8 +117,8 @@ typedef struct zonegroup_s void *first; int bytes; } zonegroup_t; -void *QDECL ZG_Malloc(zonegroup_t *ctx, int size); -void *ZG_MallocNamed(zonegroup_t *ctx, int size, char *file, int line); +void *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size); +void *ZG_MallocNamed(zonegroup_t *ctx, size_t size, char *file, int line); void ZG_FreeGroup(zonegroup_t *ctx); #ifdef USE_MSVCRT_DEBUG @@ -141,8 +141,8 @@ void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, char *name); */ -void *Hunk_TempAlloc (int size); -void *Hunk_TempAllocMore (int size); //Don't clear old temp +void *Hunk_TempAlloc (size_t size); +void *Hunk_TempAllocMore (size_t size); //Don't clear old temp /* typedef struct cache_user_s diff --git a/engine/droid/fte.cfg b/engine/droid/fte.cfg index ca98a5860..8346f4e35 100644 --- a/engine/droid/fte.cfg +++ b/engine/droid/fte.cfg @@ -15,4 +15,5 @@ vid_conwidth "0" //make something up based upon aspect ratio vid_conheight "300" //not using autoscale as it can make the menu unusable. vid_conautoscale "0" // Text/Menu size. 2 is the default. 4 is bigger +scr_consize 0.4 //android's onscreen keyboard can take up over half the screen (and we don't know exactly where it is). exec touch.cfg diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 27227d9d1..6b58810f9 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -4725,6 +4725,20 @@ static void DrawMeshes(void) p = &shaderstate.curshader->passes[passno]; passno += p->numMergedPasses; + if (p->prog) + { + shaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo; + shaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr; + shaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT; + + shaderstate.pendingtexcoordparts[0] = 2; + shaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo; + shaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr; + + BE_RenderMeshProgram(shaderstate.curshader, p, p->prog); + continue; + } + emumode = 0; emumode = (p->shaderbits & SBITS_ATEST_BITS) >> SBITS_ATEST_SHIFT; diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index b386f4ce5..2b4a1abef 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -135,11 +135,11 @@ void GL_SetupFormats(void) if (gl_config_gles) { //pre-3 gles doesn't support sized formats, and only a limited number of them too - glfmtc(PTI_RGB8, GL_RGB, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb); - glfmtc(PTI_RGBA8, GL_RGBA, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8); - glfmt(PTI_L8A8, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); - glfmt(PTI_L8, GL_LUMINANCE, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); -// glfmt(PTI_RGBA8, GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); + glfmtc(PTI_RGB8, 0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, tc_rgb); + glfmtc(PTI_RGBA8, 0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, tc_rgba8); + glfmt(PTI_L8A8, 0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE); + glfmt(PTI_L8, 0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE); +// glfmt(PTI_A8, 0, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE); if (!gl_config.webgl_ie) { //these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit. @@ -754,7 +754,6 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips) { qglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, 0); qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, nummips-1); - qglTexParameteri(targ, GL_TEXTURE_LOD_BIAS, 0); } } } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index b664381e5..b6737c284 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -24,6 +24,10 @@ struct patchvert_s cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes."); cvar_t mod_terrain_defaulttexture = CVARD("mod_terrain_defaulttexture", "", "Newly created terrain tiles will use this texture. This should generally be updated by the terrain editor."); cvar_t mod_terrain_savever = CVARD("mod_terrain_savever", "", "Which terrain section version to write if terrain was edited."); +cvar_t mod_terrain_sundir = CVARD("mod_terrain_sundir", "0.4 0.7 2", "The direction of the sun (vector will be normalised)."); +cvar_t mod_terrain_ambient = CVARD("mod_terrain_ambient", "0.5", "Proportion of ambient light."); +cvar_t mod_terrain_shadows = CVARD("mod_terrain_shadows", "0", "Cast rays to determine whether parts of the terrain should be in shadow."); +cvar_t mod_terrain_shadow_dist = CVARD("mod_terrain_shadow_dist", "2048", "How far rays should be cast in order to look for occlusing geometry."); enum { @@ -81,7 +85,7 @@ void validatelinks2(link_t *firstnode, link_t *panic) #ifndef SERVERONLY -static void ted_dorelight(heightmap_t *hm); +static void ted_dorelight(model_t *m, heightmap_t *hm); static void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b); static qboolean Terr_Collect(heightmap_t *hm); #endif @@ -1933,6 +1937,18 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa if (!s || s->loadstate < TSLS_LOADING2) return; + { + int cx = s->sx/MAXSECTIONS; + int cy = s->sy/MAXSECTIONS; + hmcluster_t *c = hm->cluster[cx + cy*MAXCLUSTERS]; + int sx = s->sx & (MAXSECTIONS-1); + int sy = s->sy & (MAXSECTIONS-1); + + if (c->section[sx+sy*MAXSECTIONS] != s) + Sys_Error("Section %i,%i already destroyed...\n", s->sx, s->sy); + c->section[sx+sy*MAXSECTIONS] = NULL; + } + validatelinks(&hm->recycle); RemoveLink(&s->recycle); @@ -1966,10 +1982,16 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa { if (qglDeleteBuffersARB) { - qglDeleteBuffersARB(1, &s->vbo.coord.gl.vbo); - s->vbo.coord.gl.vbo = 0; - qglDeleteBuffersARB(1, &s->vbo.indicies.gl.vbo); - s->vbo.indicies.gl.vbo = 0; + if (s->vbo.coord.gl.vbo) + { + qglDeleteBuffersARB(1, &s->vbo.coord.gl.vbo); + s->vbo.coord.gl.vbo = 0; + } + if (s->vbo.indicies.gl.vbo) + { + qglDeleteBuffersARB(1, &s->vbo.indicies.gl.vbo); + s->vbo.indicies.gl.vbo = 0; + } } } else @@ -2123,8 +2145,6 @@ validatelinks(&hm->recycle); } else { - c->section[sx+sy*MAXSECTIONS] = NULL; - validatelinks(&hm->recycle); Terr_DestroySection(hm, s, lightmapreusable); validatelinks(&hm->recycle); @@ -2999,7 +3019,7 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) // hm->beinglazy = false; if (hm->relight) - ted_dorelight(hm); + ted_dorelight(m, hm); if (e->model == cl.worldmodel && hm->skyshader) { @@ -3310,9 +3330,10 @@ unsigned int Heightmap_NativeBoxContents(model_t *model, int hulloverride, frame return Heightmap_PointContentsHM(hm, mins[2], org); } -void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) +float Heightmap_Normal(heightmap_t *hm, vec2_t org, vec3_t norm) //returns the z { #if 0 + float z = 0; norm[0] = 0; norm[1] = 0; norm[2] = 1; @@ -3322,6 +3343,7 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) vec3_t d1, d2; const float wbias = CHUNKBIAS * hm->sectionsize; hmsection_t *s; + float z; norm[0] = 0; norm[1] = 0; @@ -3330,12 +3352,12 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) sx = (org[0]+wbias)/hm->sectionsize; sy = (org[1]+wbias)/hm->sectionsize; if (sx < hm->firstsegx || sy < hm->firstsegy) - return; + return hm->defaultgroundheight; if (sx >= hm->maxsegx || sy >= hm->maxsegy) - return; + return hm->defaultgroundheight; s = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD); if (!s) - return; + return hm->defaultgroundheight; x = (org[0]+wbias - (sx*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; y = (org[1]+wbias - (sy*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; @@ -3354,6 +3376,10 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) d2[0] = 0; d2[1] = (hm->sectionsize / SECTHEIGHTSIZE); d2[2] = (s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]); + + z = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(1-y) + + s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE]*(x+y-1) + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(1-x)); } else { //the 0,0 triangle @@ -3366,13 +3392,19 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) d2[0] = 0; d2[1] = (hm->sectionsize / SECTHEIGHTSIZE); d2[2] = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]); + + z = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(y) + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(x) + + s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]*(1-y-x)); } + VectorNormalize(d1); VectorNormalize(d2); CrossProduct(d1, d2, norm); VectorNormalize(norm); #endif + return z; } typedef struct { @@ -4244,11 +4276,11 @@ qboolean Heightmap_Trace_Test(struct model_s *model, int hulloverride, framestat qboolean ret = Heightmap_Trace(model, hulloverride, framestate, mataxis, start, end, mins, maxs, capsule, against, trace); if (!trace->startsolid) - { + { //FIXME: this code should not be needed. trace_t testtrace; Heightmap_Trace(model, hulloverride, framestate, mataxis, trace->endpos, trace->endpos, mins, maxs, capsule, against, &testtrace); if (testtrace.startsolid) - { + { //yup, we're bugged. Con_DPrintf("Trace became solid\n"); trace->fraction = 0; VectorCopy(start, trace->endpos); @@ -4370,32 +4402,37 @@ static unsigned char *QDECL Terr_GetLightmap(hmsection_t *s, int idx, qboolean e } return lightmap[s->lightmap]->lightmaps + ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap[s->lightmap]->pixbytes; } -static void ted_dorelight(heightmap_t *hm) +static void ted_dorelight(model_t *m, heightmap_t *hm) { unsigned char *lm = Terr_GetLightmap(hm->relight, 0, true); - int x, y; + int x, y, k; #define EXPAND 2 vec3_t surfnorms[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)]; + vec3_t surfpoint[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)]; // float scaletab[EXPAND*2*EXPAND*2]; - vec3_t ldir = {0.4, 0.7, 2}; + vec3_t ldir; hmsection_t *s = hm->relight; + float ambient, diffuse; + trace_t trace; s->flags &= ~TSF_RELIGHT; hm->relight = NULL; if (s->lightmap < 0) return; + ambient = 255*mod_terrain_ambient.value; + diffuse = 255-ambient; + for (y = -EXPAND; y < SECTTEXSIZE+EXPAND; y++) for (x = -EXPAND; x < SECTTEXSIZE+EXPAND; x++) { - vec3_t pos; - pos[0] = hm->relightmin[0] + (x*hm->sectionsize/(SECTTEXSIZE-1)); - pos[1] = hm->relightmin[1] + (y*hm->sectionsize/(SECTTEXSIZE-1)); - pos[2] = 0; - Heightmap_Normal(s->hmmod, pos, surfnorms[x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2)]); + k = x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2); + surfpoint[k][0] = hm->relightmin[0] + (x*hm->sectionsize/(SECTTEXSIZE-1)); + surfpoint[k][1] = hm->relightmin[1] + (y*hm->sectionsize/(SECTTEXSIZE-1)); + surfpoint[k][2] = Heightmap_Normal(s->hmmod, surfpoint[k], surfnorms[k])+0.1; } - VectorNormalize(ldir); + VectorNormalize2(mod_terrain_sundir.vec4, ldir); for (y = 0; y < SECTTEXSIZE; y++, lm += (HMLMSTRIDE-SECTTEXSIZE)*4) for (x = 0; x < SECTTEXSIZE; x++, lm += 4) @@ -4415,10 +4452,18 @@ static void ted_dorelight(heightmap_t *hm) d = DotProduct(ldir, norm); if (d < 0) d = 0; + else if (mod_terrain_shadows.ival) + { + float *point = surfpoint[x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2)]; + vec3_t sun; + VectorMA(point, mod_terrain_shadow_dist.value, ldir, sun); + if (m->funcs.NativeTrace(m, 0, NULL, NULL, point, sun, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID|FTECONTENTS_BODY, &trace)) + d = 0; + } // lm[0] = norm[0]*127 + 128; // lm[1] = norm[1]*127 + 128; // lm[2] = norm[2]*127 + 128; - lm[3] = 127 + d*128; + lm[3] = ambient + d*diffuse; } lightmap[s->lightmap]->modified = true; @@ -7102,11 +7147,11 @@ void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br) } hasrgba = (y < br->patch->ypoints*br->patch->xpoints); - VFS_PRINTF(file, "\n\tpatchDef%s\n\t{\n\t\t\"%s\"\n\t\t( %.9g %.9g %.9g %.9g %.9g )\n\t\t(\n", + VFS_PRINTF(file, "\n\tpatchDef%s\n\t{\n\t\t\"%s\"\n\t\t( %u %u %.9g %.9g %.9g )\n\t\t(\n", hasrgba?"WS":"2", br->patch->tex?br->patch->tex->shadername:"", - 0.0/*xoffset*/, - 0.0/*yoffset*/, + br->patch->xpoints/*width*/, + br->patch->ypoints/*height*/, 0.0/*rotation*/, 1.0/*xscale*/, 1.0/*yscale*/); @@ -7480,9 +7525,10 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) continue; } } - else if (inbrush && (!strcmp(token, "patchDef2") || !strcmp(token, "patchDefWS"))) + else if (inbrush && (!strcmp(token, "patchDef2") || !strcmp(token, "patchDef3") || !strcmp(token, "patchDefWS"))) { int x, y; + qboolean patchdef3 = !strcmp(token, "patchDef3"); //fancy alternative with rgba colours per control point qboolean parsergba = !strcmp(token, "patchDefWS"); //fancy alternative with rgba colours per control point if (numplanes || patch_tex) { @@ -7498,10 +7544,17 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - /*xoffset = atof(token);*/ + /*patch_w = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); - /*yoffset = atof(token);*/ + /*patch_h = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (patchdef3) + { + /*xsubdiv = atof(token);*/ + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + /*ysubdiv = atof(token);*/ + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + } /*rotation = atof(token);*/ entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); /*xscale = atof(token);*/ @@ -7532,7 +7585,12 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) patch_v[y][x].tc[1] = atof(token); if (parsergba) - { + { //the following four lines are stupid. + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;} + entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); patch_v[y][x].rgba[0] = atof(token); entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); @@ -7981,31 +8039,33 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) } #ifndef SERVERONLY +#if 0 //not yet ready struct ted_import_s { - int x, y; - int width; - int height; + size_t x, y; + size_t width; + size_t height; unsigned short *data; }; //static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps, -void ted_import_heights(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength) +static void ted_import_heights_r16(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength) { struct ted_import_s *ctx = vctx; unsigned int y = idx/SECTHEIGHTSIZE; unsigned int x = idx%SECTHEIGHTSIZE; x += s->sx*(SECTHEIGHTSIZE-1) - ctx->x; y += s->sy*(SECTHEIGHTSIZE-1) - ctx->y; - if (x < 0 || x >= ctx->width || y < 0 || y >= ctx->height) + if (x >= ctx->width || y >= ctx->height) return; s->flags |= TSF_NOTIFY|TSF_EDITED|TSF_DIRTY|TSF_RELIGHT; s->heights[idx] = ctx->data[x + y*ctx->width] * (8192.0/(1<<16)); } -void Mod_Terrain_Import_f(void) +static void Mod_Terrain_Import_f(void) { model_t *mod; struct ted_import_s ctx; const char *mapname = Cmd_Argv(1); + const char *filename; size_t fsize; heightmap_t *hm; vec3_t pos = {0}; @@ -8025,16 +8085,142 @@ void Mod_Terrain_Import_f(void) return; fsize = 0; - ctx.data = (void*)FS_LoadMallocFile("quake8km/height8km.r16", &fsize); + filename = va("maps/%s.r16", mapname); + ctx.data = (void*)FS_LoadMallocFile(filename, &fsize); + if (!ctx.data) + { + Con_Printf("Unable to read %s\n", filename); + return; + } ctx.width = ctx.height = sqrt(fsize/2); ctx.x = 0; ctx.y = 0; pos[0] += hm->sectionsize * CHUNKBIAS; pos[1] += hm->sectionsize * CHUNKBIAS; if (fsize == ctx.width*ctx.height*2) - ted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights, &ctx); + ted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights_r16, &ctx); FS_FreeFile(ctx.data); } +static void Mod_Terrain_Export_f(void) +{ + model_t *mod; + struct ted_import_s ctx; + char mapname[MAX_QPATH]; + const char *filename; + heightmap_t *hm; + size_t w, h; + size_t tx, ty; + size_t sx, sy; + unsigned int outtilex=0,outtiley=0; + qboolean populated; + if (Cmd_IsInsecure()) + { + Con_Printf("Please use this command via the console\n"); + return; + } + if (*Cmd_Argv(1)) + mod = NULL;//Mod_FindName(va("maps/%s", mapname)); + else + mod = cl.worldmodel; + if (!mod || mod->type == mod_dummy) + return; + hm = mod->terrain; + if (!hm) + return; + + COM_StripExtension(mod->name, mapname, sizeof(mapname)); + + ctx.x = hm->firstsegx * (SECTHEIGHTSIZE-1); + w = (hm->maxsegx-hm->firstsegx) * (SECTHEIGHTSIZE-1) + 1; + while(w) + { + ctx.width = w; + if (ctx.width > 2048+1) + ctx.width = 2048; + + outtiley = 0; + ctx.y = hm->firstsegy * (SECTHEIGHTSIZE-1); + h = (hm->maxsegy-hm->firstsegy) * (SECTHEIGHTSIZE-1) + 1; + while(h) + { + ctx.height = h; + if (ctx.height > 2048+1) + ctx.height = 2048; + + populated = false; + ctx.data = Z_Malloc(ctx.width*ctx.height*2); + for (sy = ctx.y/(SECTHEIGHTSIZE-1); sy < (ctx.y+ctx.height + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sy++) + for (sx = ctx.x/(SECTHEIGHTSIZE-1); sx < (ctx.x+ctx.width + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sx++) + { + hmsection_t *s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_ANYSTATE); + if (s->loadstate == TSLS_FAILED) + { //we're doing this weirdly so we can destroy sections as we go. + Terr_DestroySection(hm, s, true); + s = NULL; + } + if (s) + { + populated = true; + for (ty = 0; ty < SECTHEIGHTSIZE; ty++) + { + size_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y; + if (y >= ctx.height) + continue; + for (tx = 0; tx < SECTHEIGHTSIZE; tx++) + { + size_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x; + if (x >= ctx.width) + continue; + ctx.data[x + y*ctx.width] = s->heights[tx+y*SECTHEIGHTSIZE] / (8192.0/(1<<16)); + } + } + if (!(s->flags & TSF_EDITED)) + Terr_DestroySection(hm, s, true); + } + else + { + for (ty = 0; ty < SECTHEIGHTSIZE; ty++) + { + size_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y; + if (y >= ctx.height) + continue; + for (tx = 0; tx < SECTHEIGHTSIZE; tx++) + { + size_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x; + if (x >= ctx.width) + continue; + ctx.data[x + y*ctx.width] = hm->defaultgroundheight / (8192.0/(1<<16)); + } + } + } + } + + filename = va("%s/x%u_y%u.r16", mapname, outtilex, outtiley); + if (populated) + { + if (FS_WriteFile(filename, ctx.data, ctx.width*ctx.height*2, FS_GAMEONLY)) + { + char sysname[1024]; + FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); + Con_Printf("Wrote %s\n", sysname); + } + else + Con_Printf("Unable to write %s\n", filename); + } + else + Con_Printf("Skipping unpopulated %s\n", filename); + Z_Free(ctx.data); + + outtiley++; + ctx.y += ctx.height; + h -= ctx.height; + } + outtilex++; + ctx.x += ctx.width; + w -= ctx.width; + } +} +#endif void Mod_Terrain_Create_f(void) { @@ -8279,9 +8465,15 @@ void Terr_Init(void) Cmd_AddCommand("mod_terrain_save", Mod_Terrain_Save_f); Cmd_AddCommand("mod_terrain_reload", Mod_Terrain_Reload_f); #ifndef SERVERONLY - Cmd_AddCommandD("mod_terrain_import", Mod_Terrain_Import_f, "Import a raw heightmap"); +// Cmd_AddCommandD("mod_terrain_export", Mod_Terrain_Export_f, "Export a raw heightmap"); +// Cmd_AddCommandD("mod_terrain_import", Mod_Terrain_Import_f, "Import a raw heightmap"); Cmd_AddCommand("mod_terrain_create", Mod_Terrain_Create_f); Cmd_AddCommandD("mod_terrain_convert", Mod_Terrain_Convert_f, "mod_terrain_convert [mapname] [texkill]\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation."); + + Cvar_Register(&mod_terrain_sundir, "Terrain"); + Cvar_Register(&mod_terrain_ambient, "Terrain"); + Cvar_Register(&mod_terrain_shadows, "Terrain"); + Cvar_Register(&mod_terrain_shadow_dist, "Terrain"); #endif Mod_RegisterModelFormatText(NULL, "FTE Heightmap Map (hmp)", "terrain", Terr_LoadTerrainModel); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 3b87a5090..8e293388e 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -1256,6 +1256,7 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) size_t n, pn = 0; char defines[8192]; size_t offset; + qboolean fail = false; extern cvar_t gl_specular, gl_specular_power; @@ -1294,14 +1295,14 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) permutationdefines[pn++] = NULL; if (!sh_config.pCreateProgram(prog, pp, prog->shaderver, permutationdefines, prog->shadertext, prog->tess?prog->shadertext:NULL, prog->tess?prog->shadertext:NULL, prog->geom?prog->shadertext:NULL, prog->shadertext, prog->warned, NULL)) - prog->warned = true; + prog->warned = fail = true; //extra loop to validate the programs actually linked properly. //delaying it like this gives certain threaded drivers a chance to compile them all while we're messing around with other junk - if (sh_config.pValidateProgram && !sh_config.pValidateProgram(prog, pp, prog->warned, NULL)) - prog->warned = true; + if (!fail && sh_config.pValidateProgram && !sh_config.pValidateProgram(prog, pp, prog->warned, NULL)) + prog->warned = fail = true; - if (sh_config.pProgAutoFields) + if (!fail && sh_config.pProgAutoFields) { cvar_t *cvarrefs[64]; char *cvarnames[64+1]; @@ -1320,9 +1321,52 @@ struct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p) cvarnames[i] = NULL; //no more sh_config.pProgAutoFields(prog, pp, cvarrefs, cvarnames, cvartypes); } + if (fail) + { + Z_Free(pp); + return NULL; + } return pp; } +qboolean Shader_PermutationEnabled(unsigned int bit) +{ + if (bit == PERMUTATION_REFLECTCUBEMASK) + return gl_load24bit.ival; + if (bit == PERMUTATION_BUMPMAP) + return r_loadbumpmapping; + return true; +} +qboolean Com_PermuOrFloatArgument(const char *shadername, char *arg, size_t arglen, float def) +{ + extern cvar_t gl_specular; + size_t p; + //load-time-only permutations... + if (arglen == 8 && !strncmp("SPECULAR", arg, arglen) && gl_specular.value) + return true; + if ((arglen==5||arglen==6) && !strncmp("DELUXE", arg, arglen) && r_deluxemapping && Shader_PermutationEnabled(PERMUTATION_BUMPMAP)) + return true; + if (arglen == 13 && !strncmp("OFFSETMAPPING", arg, arglen) && r_glsl_offsetmapping.ival) + return true; + if (arglen == 13 && !strncmp("RELIEFMAPPING", arg, arglen) && r_glsl_offsetmapping.ival && r_glsl_offsetmapping_reliefmapping.ival) + return true; + + //real permutations + if (arglen == 5 && (!strncmp("UPPER", arg, arglen)||!strncmp("LOWER", arg, arglen)) && Shader_PermutationEnabled(PERMUTATION_BIT_UPPERLOWER)) + return true; + for (p = 0; p < countof(permutations); p++) + { + if (arglen == strlen(permutations[p].name) && !strncmp(permutations[p].name, arg, arglen)) + { + if (Shader_PermutationEnabled(permutations[p].bitmask)) + return true; + break; + } + } + + return Com_FloatArgument(shadername, arg, arglen, def) != 0; +} + static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *script, int qrtype, int ver, char *blobfilename) { #if defined(GLQUAKE) || defined(D3DQUAKE) @@ -1412,15 +1456,16 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip if (*token == '=' || *token == '!') { len = strlen(token); - if (*token == (Com_FloatArgument(name, token+1, len-1, 0)?'!':'=')) + if (*token == (Com_PermuOrFloatArgument(name, token+1, len-1, 0)?'!':'=')) ignore = true; continue; } else if (ignore) continue; -#ifdef HAVE_LEGACY +#if 1//def HAVE_LEGACY else if (!strncmp(token, "deluxmap", 8)) { //FIXME: remove this some time. + Con_DPrintf("Outdated texture name \"%s\" in program \"%s\"\n", token, name); token = va("deluxemap%s",token+8); } #endif @@ -1470,7 +1515,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip prog->numsamplers = i; } else - Con_Printf("Unknown texture name in %s\n", name); + Con_Printf("Unknown texture name \"%s\" in program \"%s\"\n", token, name); } } } @@ -1667,7 +1712,8 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip { if (!strncmp(permutations[p].name, script, end - script) && permutations[p].name[end-script] == '\0') { - nopermutation &= ~permutations[p].bitmask; + if (Shader_PermutationEnabled(permutations[p].bitmask)) + nopermutation &= ~permutations[p].bitmask; break; } } @@ -1738,10 +1784,10 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip nopermutation |= PERMUTATION_SKELETAL; //multiple lightmaps is kinda hacky. if any are set, all must be. -#define ALTLIGHTMAPSAMP 13 +#define ALTLIGHTMAPSAMP 14 if (prog->defaulttextures & ((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2)))) prog->defaulttextures |=((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2))); -#define ALTDELUXMAPSAMP 16 +#define ALTDELUXMAPSAMP 17 if (prog->defaulttextures & ((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2)))) prog->defaulttextures |=((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2))); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 93e668838..e613cb679 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -420,7 +420,7 @@ qboolean GL_CheckExtension(char *extname) for (i = 0; i < gl_num_extensions; i++) if (!strcmp(qglGetStringi(GL_EXTENSIONS, i), extname)) { - Con_DPrintf("Detected GL extension %s\n", extname); + Con_DPrintf("GL: Found %s\n", extname); return true; } } @@ -2280,6 +2280,9 @@ static GLhandleARB GLSlang_CreateShader (program_t *prog, const char *name, int "#ifndef USE_ARB_SHADOW\n" //fall back on regular samplers if we must "#define sampler2DShadow sampler2D\n" "#elif defined(GL_ES)\n" + "#if __VERSION__ < 300\n" + "#extension GL_EXT_shadow_samplers : require\n" + "#endif\n" "precision lowp sampler2DShadow;\n" //gah "#endif\n" #endif diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index a53d981d7..f3b49fcb4 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -2975,7 +2975,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND //must support skeletal and 2-way vertex blending or Bad Things Will Happen. //the vertex shader is responsible for calculating lighting values. -"#if gl_affinemodels==1 && __VERSION__ >= 130\n" +"#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES)\n" "#define affine noperspective\n" "#else\n" "#define affine\n" @@ -5599,11 +5599,17 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!permu REFLECTCUBEMASK\n" "!!cvarf r_glsl_offsetmapping_scale\n" "!!cvardf r_tessellation_level=5\n" -"!!samps !EIGHTBIT diffuse specular normalmap fullbright reflectmask reflectcube\n" +"!!samps diffuse\n" +"!!samps !EIGHTBIT =FULLBRIGHT fullbright\n" +"!!samps !EIGHTBIT =BUMP normalmap\n" +"!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube\n" //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. -"!!samps =EIGHTBIT paletted 1 specular diffuse\n" -"!!samps lightmap deluxemap\n" -"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3\n" +"!!samps =EIGHTBIT paletted 1\n" +"!!samps =SPECULAR specular\n" +"!!samps lightmap\n" +"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n" +"!!samps =DELUXE deluxmap\n" +"!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\n" "#if defined(ORM) || defined(SG)\n" "#define PBR\n" @@ -5960,7 +5966,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#else\n" //now we have our diffuse+specular terms, modulate by lightmap values. "col.rgb *= lightmaps.rgb;\n" - //add on the fullbright "#ifdef FULLBRIGHT\n" "col.rgb += texture2D(s_fullbright, tc).rgb;\n" @@ -10603,10 +10608,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND #endif #ifdef GLQUAKE {QR_OPENGL, 110, "terrain", +"!!ver 100 300\n" "!!permu FOG\n" -//t0-t3 are the diffusemaps, t4 is the blend factors -"!!samps 4\n" -"!!samps mix=4\n" +//RTLIGHT (+PCF,CUBE,SPOT,etc) +"!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps\n" +"!!samps mix=4 //how the ground is blended\n" "!!samps =PCF shadowmap\n" "!!samps =CUBE projectionmap\n" @@ -10703,10 +10709,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec4 r;\n" "vec4 m = texture2D(s_mix, lm);\n" -"r = texture2D(s_t0, tc)*m.r;\n" -"r += texture2D(s_t1, tc)*m.g;\n" -"r += texture2D(s_t2, tc)*m.b;\n" -"r += texture2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b));\n" +"r = texture2D(s_tr, tc)*m.r;\n" +"r += texture2D(s_tg, tc)*m.g;\n" +"r += texture2D(s_tb, tc)*m.b;\n" +"r += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b));\n" + +"r.rgb *= 1.0/r.a; //fancy maths, so low alpha values give other textures a greater focus\n" //vertex colours provide a scaler that applies even through rtlights. "r *= vc;\n" diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 5e50168cf..1884da3f8 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -23,7 +23,7 @@ void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, const char *name) return ((char *)mem)+sizeof(prmemb_t); } -void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name) +static void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name) { return PRHunkAlloc((progfuncs_t*)ppf, ammount, name); } @@ -48,7 +48,7 @@ void PRHunkFree(progfuncs_t *progfuncs, int mark) } /*if we ran out of memory, the vm can allocate a new block, but doing so requires fixing up all sorts of pointers*/ -void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen) +static void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen) { unsigned int i; edictrun_t *e; @@ -583,7 +583,7 @@ static void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, in -struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) +static struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; if (pnum < 0) @@ -598,7 +598,7 @@ struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum) return (struct globalvars_s *)pr_progstate[pnum].globals; } -struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) +static struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) { // progfuncs_t *progfuncs = (progfuncs_t*)ppf; if (((edictrun_t *)ed)->ereftype != ER_ENTITY) @@ -607,7 +607,7 @@ struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed) return (struct entvars_s *)edvars(ed); } -pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, int *builtinnum, char *funcname, size_t funcnamesize) +static pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, int *builtinnum, char *funcname, size_t funcnamesize) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; @@ -697,7 +697,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p return 0; } -void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx) +static void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int i; @@ -795,7 +795,7 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_ return NULL; } -char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) +static char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int i; @@ -816,7 +816,7 @@ char *PDECL PR_VarString (pubprogfuncs_t *ppf, int first) return out; } -int PDECL PR_QueryField (pubprogfuncs_t *ppf, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache) +static int PDECL PR_QueryField (pubprogfuncs_t *ppf, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; fdef_t *var; @@ -868,7 +868,7 @@ eval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *ppf, struct edict_s *ed, con return (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[cache->ofs32->ofs]); } -struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) +static struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; if ((unsigned)progs >= (unsigned)prinst.maxedicts) @@ -883,7 +883,7 @@ struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs) } return (struct edict_s *)PROG_TO_EDICT_PB(progfuncs.inst, progs); } -int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed) +static int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed) { // progfuncs_t *progfuncs = (progfuncs_t*)ppf; return EDICT_TO_PROG(progfuncs, ed); @@ -935,7 +935,7 @@ string_t PDECL PR_StringToProgs (pubprogfuncs_t *ppf, const char *str) return (string_t)((unsigned int)i | STRING_STATIC); } //if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated. -void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) +static void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static) { if (!str) *fld = 0; @@ -951,7 +951,7 @@ void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, stri } } -char *PDECL PR_RemoveProgsString (pubprogfuncs_t *ppf, string_t str) +static char *PDECL PR_RemoveProgsString (pubprogfuncs_t *ppf, string_t str) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; char *ret; @@ -1110,7 +1110,7 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals) } -string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) +static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; tempstr_t **ntable; @@ -1319,7 +1319,7 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs) prinst.numtempstrings = 0; prinst.nexttempstring = 0; } -pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) +static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; struct progstate_s *ps; @@ -1391,7 +1391,7 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf); static void PDECL RegisterBuiltin(pubprogfuncs_t *progfncs, const char *name, builtin_t func); -pubprogfuncs_t deffuncs = { +static pubprogfuncs_t deffuncs = { PROGSTRUCT_VERSION, PR_CloseProgs, PR_Configure, @@ -1510,11 +1510,11 @@ static void PDECL qclib_free(void *ptr) #endif //defs incase following structure is not passed. -struct edict_s *safesv_edicts; -int safesv_num_edicts; -double safetime=0; +static struct edict_s *safesv_edicts; +static int safesv_num_edicts; +static double safetime=0; -progexterns_t defexterns = { +static progexterns_t defexterns = { PROGSTRUCT_VERSION, NULL, //char *(*ReadFile) (char *fname, void *buffer, int len); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 20ab343ca..0e1b4deb6 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -10451,9 +10451,14 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"bprint", PF_bprint, 0, 23, 0, 0, D("void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, {"sprint", PF_sprint, 24, 0, 24, 0, D("void(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)", "NQ: Concatenates all string arguments, and prints the messsage on the named client's console")}, {"sprint", PF_sprint, 0, 24, 0, 0, D("void(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)", "QW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.")}, +#ifdef HAVE_LEGACY //these have subtly different behaviour, and are implemented using different internal builtins, which is a bit weird in the extensions file. documentation is documentation. {"dprint", PF_dprint, 25, 0, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, {"dprint", PF_print, 0, 25, 0, 0, D("void(string s, ...)", "QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message.")}, +#else + //going forward, we have print and dprint + {"dprint", PF_dprint, 25, 25, 25, 0, D("void(string s, ...)", "NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.")}, +#endif {"ftos", PF_ftos, 26, 26, 26, 0, D("string(float val)", "Returns a tempstring containing a representation of the given float. Precision depends upon engine.")}, {"vtos", PF_vtos, 27, 27, 27, 0, D("string(vector val)", "Returns a tempstring containing a representation of the given vector. Precision depends upon engine.")}, {"coredump", PF_coredump, 28, 28, 28, 0, D("void()", "Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging.")}, diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 776183e2a..dfc8f2386 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -565,7 +565,10 @@ void SV_Map_f (void) #ifdef Q3SERVER q3singleplayer = !strcmp(Cmd_Argv(0), "spmap"); #endif - flushparms = !strcmp(Cmd_Argv(0), "map") || !strcmp(Cmd_Argv(0), "spmap"); + if ((svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) && progstype == PROG_QW) + flushparms = !strcmp(Cmd_Argv(0), "spmap"); //quakeworld's map command preserves spawnparms. + else + flushparms = !strcmp(Cmd_Argv(0), "map") || !strcmp(Cmd_Argv(0), "spmap"); //[sp]map flushes in nq+h2+q2+etc #ifdef SAVEDGAMES newunit = flushparms || (!strcmp(Cmd_Argv(0), "changelevel") && !startspot); q2savetos0 = !strcmp(Cmd_Argv(0), "gamemap") && !isDedicated; //q2 @@ -1910,7 +1913,10 @@ static void SV_Status_f (void) #if defined(HAVE_SSL) extern cvar_t net_enable_tls; #endif - extern cvar_t net_enable_http, net_enable_webrtcbroker, net_enable_websockets, net_enable_qizmo, net_enable_qtv; + #ifdef HAVE_HTTPSV + extern cvar_t net_enable_http, net_enable_webrtcbroker, net_enable_websockets; + #endif + extern cvar_t net_enable_qizmo, net_enable_qtv; #endif #ifdef NQPROT extern cvar_t sv_listen_nq, sv_listen_dp; @@ -1990,12 +1996,14 @@ static void SV_Status_f (void) if (net_enable_tls.ival) Con_Printf(" TLS"); #endif +#ifdef HAVE_HTTPSV if (net_enable_http.ival) Con_Printf(" HTTP"); if (net_enable_webrtcbroker.ival) Con_Printf(" WebRTC"); if (net_enable_websockets.ival) Con_Printf(" WS"); +#endif if (net_enable_qizmo.ival) Con_Printf(" QZ"); if (net_enable_qtv.ival) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 80b8e6b82..051a39628 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -3140,13 +3140,27 @@ client_t *SVC_DirectConnect(void) else #endif newcl->netchan.compresstable = NULL; + newcl->netchan.pext_fragmentation = mtu?true:false; + //this is the upper bound of the mtu, if its too high we'll get EMSGSIZE and we'll reduce it. + //however, if it drops below newcl->netchan.message.maxsize then we'll start to see undeliverable reliables, which means dropped clients. + newcl->netchan.mtu = MAX_DATAGRAM; //vanilla qw clients are assumed to have an mtu of this size. if (mtu >= 64) { //if we support application fragmenting, then we can send massive reliables without too much issue - newcl->netchan.fragmentsize = mtu; + newcl->netchan.mtu = mtu; newcl->netchan.message.maxsize = sizeof(newcl->netchan.message_buf); } else //otherwise we can't fragment the packets, and the only way to honour the mtu is to send less data. yay for more round-trips. - newcl->netchan.message.maxsize = min(newcl->netchan.message.maxsize, max(net_mtu.ival, 512)); + { + mtu = atoi(Info_ValueForKey (userinfo[0], "mtu")); + if (mtu) + newcl->netchan.mtu = mtu; //locked mtu size, because not everyone has a working connection (we need icmp would-fragment responses for mtu detection) + else //if its not set then use some 'safe' fallback. + mtu = MAX_BACKBUFLEN; //MAX_BACKBUFLEN of 1200 is < ipv6 required segment size so should always work for reliables. + //enforce some boundaries + mtu = bound(512-8, mtu, sizeof(newcl->netchan.message_buf)); + newcl->netchan.message.maxsize = mtu; + } + Con_DLPrintf(2, "MTU size: %i - %i\n", newcl->netchan.message.maxsize, newcl->netchan.mtu); newcl->protocol = protocol; #ifdef NQPROT diff --git a/engine/server/sv_nchan.c b/engine/server/sv_nchan.c index 74a1ef937..84c2e3c2b 100644 --- a/engine/server/sv_nchan.c +++ b/engine/server/sv_nchan.c @@ -35,7 +35,7 @@ void ClientReliableCheckBlock(client_t *cl, int maxsize) memset(&cl->backbuf, 0, sizeof(cl->backbuf)); cl->backbuf.allowoverflow = true; cl->backbuf.data = cl->backbuf_data[0]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[0]); + cl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[0])); cl->backbuf_size[0] = 0; cl->num_backbuf++; } @@ -54,7 +54,7 @@ void ClientReliableCheckBlock(client_t *cl, int maxsize) memset(&cl->backbuf, 0, sizeof(cl->backbuf)); cl->backbuf.allowoverflow = true; cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; - cl->backbuf.maxsize = sizeof(cl->backbuf_data[cl->num_backbuf]); + cl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[cl->num_backbuf])); cl->backbuf_size[cl->num_backbuf] = 0; cl->num_backbuf++; } diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 89f4e8a57..98b038b68 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -2573,13 +2573,15 @@ qboolean SV_SendClientDatagram (client_t *client) client->edict->v->goalentity = 0; } - if (client->netchan.fragmentsize) + if (client->netchan.pext_fragmentation) { if (client->netchan.remote_address.type == NA_LOOPBACK) clientlimit = countof(buf); //biiiig... else - clientlimit = client->netchan.fragmentsize; //try not to overflow + clientlimit = client->netchan.mtu; //try not to overflow } + else if (client->netchan.mtu) + clientlimit = client->netchan.mtu; else if (client->protocol == SCP_NETQUAKE) clientlimit = MAX_NQDATAGRAM; //vanilla client is limited. else @@ -3411,7 +3413,7 @@ void SV_SendClientMessages (void) memset(&c->backbuf, 0, sizeof(c->backbuf)); c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; - c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]); + c->backbuf.maxsize = min(c->netchan.message.maxsize, sizeof(c->backbuf_data[c->num_backbuf-1])); } } } @@ -3526,6 +3528,9 @@ void SV_SendClientMessages (void) c->datagram.cursize = 0; } c->lastoutgoingphysicstime = sv.world.physicstime; + + if (c->netchan.fatal_error) + c->drop = true; } #ifdef MVD_RECORDING if (sv.mvdrecording) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index ebd6f802e..fa9672285 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -987,12 +987,12 @@ void SV_SendClientPrespawnInfo(client_t *client) return; } - //just because we CAN generate huge messages doesn't meant that we should. + //just because we CAN generate huge messages doesn't mean that we should. //try to keep packets within reasonable sizes so that we don't trigger insane burst+packetloss on map changes. maxsize = client->netchan.message.maxsize/2; - if (client->netchan.fragmentsize && maxsize > client->netchan.fragmentsize-200) + if (client->netchan.mtu && maxsize > client->netchan.mtu-200) { - maxsize = client->netchan.fragmentsize-200; + maxsize = client->netchan.mtu-200; if (maxsize < 500) maxsize = 500; } @@ -1933,7 +1933,6 @@ void SVQW_Spawn_f (void) // when that is completed, a begin command will be issued ClientReliableWrite_Begin (host_client, svc_stufftext, 8); ClientReliableWrite_String (host_client, "skins\n" ); - } /* @@ -7733,6 +7732,8 @@ void SV_ReadQCRequest(void) done: args[i] = 0; rname = MSG_ReadString(); + //We used to use Cmd_foo_args, but that conflicts with a zquake extension and would cause [e]zquake mods that use it to be remotely exploitable (mostly crashes from uninitialised args though). + //Instead, we've switched to some more weird prefix that's much less likly to conflict. if (i) fname = va("CSEv_%s_%s", rname, args); else if (strchr(rname, '_')) //this is awkward, as not forcing an underscore would allow people to mis-call things with lingering data (the alternative is to block underscores entirely). @@ -7749,7 +7750,10 @@ done: rname = va("Cmd_%s", rname); f = PR_FindFunction(svprogfuncs, rname, PR_ANY); if (f) - SV_ClientPrintf(host_client, PRINT_HIGH, "the name \"%s\" is deprecated\n", rname); + { + SV_ClientPrintf(host_client, PRINT_HIGH, "\"%s\" is no longer supported.\n", rname); + f = 0; + } } #endif if (host_client->drop) diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index c8724984b..9f04109b0 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -809,6 +809,10 @@ static void QDECL PFQ2_SetAreaPortalState(unsigned int p, qboolean s) CMQ2_SetAreaPortalState(sv.world.worldmodel, p, s); } +static void *VARGS ZQ2_TagMalloc(int size, int tag) +{ + return Z_TagMalloc(size, tag); +} qboolean SVQ2_InitGameProgs(void) { extern cvar_t maxclients; @@ -862,7 +866,7 @@ qboolean SVQ2_InitGameProgs(void) import.WriteDir = PFQ2_WriteDir; import.WriteAngle = PFQ2_WriteAngle; - import.TagMalloc = Z_TagMalloc; + import.TagMalloc = ZQ2_TagMalloc; import.TagFree = Z_TagFree; import.FreeTags = Z_FreeTags; diff --git a/engine/shaders/glsl/defaultskin.glsl b/engine/shaders/glsl/defaultskin.glsl index b671c2991..8a35d1931 100644 --- a/engine/shaders/glsl/defaultskin.glsl +++ b/engine/shaders/glsl/defaultskin.glsl @@ -30,7 +30,7 @@ //must support skeletal and 2-way vertex blending or Bad Things Will Happen. //the vertex shader is responsible for calculating lighting values. -#if gl_affinemodels==1 && __VERSION__ >= 130 +#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES) #define affine noperspective #else #define affine diff --git a/engine/shaders/glsl/defaultwall.glsl b/engine/shaders/glsl/defaultwall.glsl index f39135f0d..104a5e6e0 100644 --- a/engine/shaders/glsl/defaultwall.glsl +++ b/engine/shaders/glsl/defaultwall.glsl @@ -9,11 +9,17 @@ !!permu REFLECTCUBEMASK !!cvarf r_glsl_offsetmapping_scale !!cvardf r_tessellation_level=5 -!!samps !EIGHTBIT diffuse specular normalmap fullbright reflectmask reflectcube +!!samps diffuse +!!samps !EIGHTBIT =FULLBRIGHT fullbright +!!samps !EIGHTBIT =BUMP normalmap +!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. -!!samps =EIGHTBIT paletted 1 specular diffuse -!!samps lightmap deluxemap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 deluxemap deluxemap1 deluxemap2 deluxemap3 +!!samps =EIGHTBIT paletted 1 +!!samps =SPECULAR specular +!!samps lightmap +!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 +!!samps =DELUXE deluxmap +!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 #if defined(ORM) || defined(SG) #define PBR @@ -299,7 +305,7 @@ void main () vec3 deluxe = (texture2D(s_deluxemap, lm0).rgb-0.5); #ifdef BUMPMODELSPACE deluxe = normalize(deluxe*invsurface); -#else + #else deluxe = normalize(deluxe); lightmaps *= 2.0 / max(0.25, deluxe.z); //counter the darkening from deluxemaps #endif @@ -370,7 +376,6 @@ void main () #else //now we have our diffuse+specular terms, modulate by lightmap values. col.rgb *= lightmaps.rgb; - //add on the fullbright #ifdef FULLBRIGHT col.rgb += texture2D(s_fullbright, tc).rgb; diff --git a/engine/shaders/glsl/terrain.glsl b/engine/shaders/glsl/terrain.glsl index a79f88ee5..9b8bed58f 100644 --- a/engine/shaders/glsl/terrain.glsl +++ b/engine/shaders/glsl/terrain.glsl @@ -1,7 +1,8 @@ +!!ver 100 300 !!permu FOG -//t0-t3 are the diffusemaps, t4 is the blend factors -!!samps 4 -!!samps mix=4 +//RTLIGHT (+PCF,CUBE,SPOT,etc) +!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps +!!samps mix=4 //how the ground is blended !!samps =PCF shadowmap !!samps =CUBE projectionmap @@ -98,10 +99,12 @@ void main (void) vec4 r; vec4 m = texture2D(s_mix, lm); - r = texture2D(s_t0, tc)*m.r; - r += texture2D(s_t1, tc)*m.g; - r += texture2D(s_t2, tc)*m.b; - r += texture2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b)); + r = texture2D(s_tr, tc)*m.r; + r += texture2D(s_tg, tc)*m.g; + r += texture2D(s_tb, tc)*m.b; + r += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b)); + + r.rgb *= 1.0/r.a; //fancy maths, so low alpha values give other textures a greater focus //vertex colours provide a scaler that applies even through rtlights. r *= vc;