From 1fe478dfa1adcb1f4c35beb6fc730f83d42e57a9 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Mon, 20 Feb 2023 05:50:50 +0000 Subject: [PATCH] Add basic support for ktx's 'mapname#modifier' ent stuff. --- engine/client/merged.h | 2 ++ engine/gl/gl_heightmap.c | 1 + engine/gl/gl_model.c | 17 ++++++++++++++++ engine/server/pr_q1qvm.c | 24 +++++++++++++++++++++-- engine/server/sv_ccmds.c | 42 ++++++++++++++++++++++++++++++++++++++-- engine/server/sv_init.c | 35 +++++++++++++++++++++++++++++---- 6 files changed, 113 insertions(+), 8 deletions(-) diff --git a/engine/client/merged.h b/engine/client/merged.h index b62c0c147..257c8f766 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -184,6 +184,8 @@ void Mod_ParseEntities(struct model_s *mod); void Mod_LoadMapArchive(struct model_s *mod, void *archivedata, size_t archivesize); extern void Mod_ClearAll (void); extern void Mod_Purge (enum mod_purge_e type); +extern void Mod_SetModifier (const char *modifier); +extern char mod_modifier[]; extern qboolean Mod_PurgeModel (struct model_s *mod, enum mod_purge_e ptype); extern struct model_s *Mod_FindName (const char *name); //find without loading. needload should be set. extern struct model_s *Mod_ForName (const char *name, enum mlverbosity_e verbosity); //finds+loads diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index d14f9af14..6f6c64019 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -7930,6 +7930,7 @@ void Mod_Terrain_Save_f(void) { //warning: brushes are not saved unless its a .map COM_StripExtension(mod->name, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".ent", sizeof(fname)); FS_CreatePath(fname, FS_GAMEONLY); diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index e0734322c..867afb087 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -81,6 +81,7 @@ void Mod_LoadDoomSprite (model_t *mod); #define MAX_MOD_KNOWN 8192 model_t *mod_known; int mod_numknown; +char mod_modifier[MAX_QPATH]; //postfix for ent files extern cvar_t r_loadlits; #ifdef SPECULAR @@ -620,6 +621,17 @@ void Mod_Purge(enum mod_purge_e ptype) } } +void Mod_SetModifier(const char *modifier) +{ + if (!modifier || strlen(modifier) >= sizeof(mod_modifier)) modifier = ""; + if (strcmp(modifier, mod_modifier)) + { //if the modifier changed, force all models to reset. + COM_WorkerFullSync(); //sync all the workers, just in case. + strcpy(mod_modifier, modifier); + Mod_Purge(MP_RESET); //nuke it now + } +} + #ifndef SERVERONLY void Mod_FindCubemaps_f(void); void Mod_Realign_f(void); @@ -2252,11 +2264,13 @@ static void Mod_SaveEntFile_f(void) { Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, mod->name+5); COM_StripExtension(fname, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".ent", sizeof(fname)); } else { COM_StripExtension(mod->name, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".ent", sizeof(fname)); } @@ -2293,6 +2307,7 @@ qboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t e { Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, mod->name+5); COM_StripExtension(fname, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".ent", sizeof(fname)); ents = FS_LoadMallocFile(fname, &sz); } @@ -2300,12 +2315,14 @@ qboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t e if (mod_loadentfiles.value && !ents) { COM_StripExtension(mod->name, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".ent", sizeof(fname)); ents = FS_LoadMallocFile(fname, &sz); } if (mod_loadentfiles.value && !ents) { //tenebrae compat COM_StripExtension(mod->name, fname, sizeof(fname)); + Q_strncatz(fname, mod_modifier, sizeof(fname)); Q_strncatz(fname, ".edo", sizeof(fname)); ents = FS_LoadMallocFile(fname, &sz); } diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index d48be2ff1..8a608f3a0 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -1090,11 +1090,21 @@ static qintptr_t QVM_SetSpawnParams (void *offset, quintptr_t mask, const qintpt static qintptr_t QVM_ChangeLevel (void *offset, quintptr_t mask, const qintptr_t *arg) { const char *arg_mapname = VM_POINTER(arg[0]); -// const char *arg_entfilename = VM_POINTER(arg[1]); + const char *arg_entfilename = (qvm_api_version > 13)?(VM_POINTER(arg[1])):""; char newmap[MAX_QPATH]; if (sv.mapchangelocked) return 0; + + if (arg_entfilename && *arg_entfilename) + { + int nl = strlen(arg_mapname); + if (!strncmp(arg_mapname, arg_entfilename, nl) && arg_mapname[nl]=='#') + arg_mapname = arg_entfilename; + else + Con_Printf(CON_ERROR"%s: named ent file does not match map\n", "QVM_ChangeLevel"); + } + sv.mapchangelocked = true; COM_QuotedString(arg_mapname, newmap, sizeof(newmap), false); Cbuf_AddText (va("\nchangelevel %s\n", newmap), RESTRICT_LOCAL); @@ -1103,13 +1113,23 @@ static qintptr_t QVM_ChangeLevel (void *offset, quintptr_t mask, const qintptr_t static qintptr_t QVM_ChangeLevelHub (void *offset, quintptr_t mask, const qintptr_t *arg) { const char *arg_mapname = VM_POINTER(arg[0]); -// const char *arg_entfile = VM_POINTER(arg[1]); + const char *arg_entfilename = VM_POINTER(arg[1]); const char *arg_startspot = VM_POINTER(arg[2]); char newmap[MAX_QPATH]; char startspot[MAX_QPATH]; if (sv.mapchangelocked) return 0; + + if (arg_entfilename && *arg_entfilename) + { + int nl = strlen(arg_mapname); + if (!strncmp(arg_mapname, arg_entfilename, nl) && arg_mapname[nl]=='#') + arg_mapname = arg_entfilename; + else + Con_Printf(CON_ERROR"%s: named ent file does not match map\n", "QVM_ChangeLevelHub"); + } + sv.mapchangelocked = true; COM_QuotedString(arg_mapname, newmap, sizeof(newmap), false); COM_QuotedString(arg_startspot, startspot, sizeof(startspot), false); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 4fc732dab..596981282 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -471,6 +471,27 @@ static int QDECL CompleteMapList (const char *name, qofs_t flags, time_t mtime, ctx->cb(stripped, NULL, NULL, ctx); return true; } +static int QDECL CompleteMapListEnt (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) +{ + struct xcommandargcompletioncb_s *ctx = parm; + char stripped[64]; + char *modifier = strchr(name, '#'); + if (!modifier) //skip non-modifiers. + return true; + if (modifier-name+4 > sizeof(stripped)) //too long... + return true; + + //make sure we have its .bsp + memcpy(stripped, name, modifier-name); + strcpy(stripped+(modifier-name), ".bsp"); + if (!COM_FCheckExists(stripped)) + return true; + + COM_StripExtension(name+5, stripped, sizeof(stripped)); + ctx->cb(stripped, NULL, NULL, ctx); + return true; +} + static int QDECL CompleteMapListExt (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath) { struct xcommandargcompletioncb_s *ctx = parm; @@ -493,6 +514,8 @@ static void SV_Map_c(int argn, const char *partial, struct xcommandargcompletion COM_EnumerateFiles(va("maps/%s*.cm", partial), CompleteMapList, ctx); COM_EnumerateFiles(va("maps/%s*.hmp", partial), CompleteMapList, ctx); + COM_EnumerateFiles(va("maps/%s*.ent", partial), CompleteMapListEnt, ctx); + COM_EnumerateFiles(va("maps/%s*/*.bsp", partial), CompleteMapList, ctx); COM_EnumerateFiles(va("maps/%s*/*.bsp.gz", partial), CompleteMapListExt, ctx); COM_EnumerateFiles(va("maps/%s*/*.bsp.xz", partial), CompleteMapListExt, ctx); @@ -609,7 +632,7 @@ fte: 'map package:mapname' should download the specified map package and load up its maps. mvdsv: -basemap#modifier.ent files +'map foo bar' should load 'maps/bar.ent' instead of the regular ent file. this 'bar' will usually be something like 'foo#modified' ====================== */ @@ -851,10 +874,25 @@ void SV_Map_f (void) break; } if (!exts[i]) + { //try again. + char *mod = strchr(level, '#'); + if (mod) + { + *mod = 0; + for (i = 0; exts[i]; i++) + { + snprintf (expanded, sizeof(expanded), exts[i], level); + if (COM_FCheckExists (expanded)) + break; + } + *mod = '#'; + } + } + if (!exts[i]) { for (i = 0; exts[i]; i++) { - //doesn't exist, so try lowercase. Q3 does this. + //doesn't exist, so try lowercase. Q3 does this. really our fs_cache stuff should be handling this, but its possible its disabled. for (j = 0; j < sizeof(level) && level[j]; j++) { if (level[j] >= 'A' && level[j] <= 'Z') diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index b2bfd1aa6..46b558e71 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -1016,12 +1016,11 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, { //.map is commented out because quite frankly, they're a bit annoying when the engine loads the gpled start.map when really you wanted to just play the damn game intead of take it apart. //if you want to load a .map, just use 'map foo.map' instead. - char *exts[] = {"maps/%s", "maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", /*"maps/%s.map",*/ "maps/%s.bsp.gz", "maps/%s.bsp.xz", NULL}, *e; - int depth, bestdepth; + char *exts[] = {"%s", "maps/%s", "maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", /*"maps/%s.map",*/ "maps/%s.bsp.gz", "maps/%s.bsp.xz", NULL}, *e; + int depth, bestdepth = FDEPTH_MISSING; flocation_t loc; time_t filetime; - Q_snprintfz (sv.modelname, sizeof(sv.modelname), "%s", server); - bestdepth = COM_FDepthFile(sv.modelname, false); + char *mod = NULL; if (bestdepth == FDEPTH_MISSING) { //not an exact name, scan the maps subdir. for (i = 0; exts[i]; i++) @@ -1034,6 +1033,32 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, } } } + if (bestdepth == FDEPTH_MISSING) + { + mod = strchr(server, '#'); + if (mod) + { + *mod = 0; + bestdepth = COM_FDepthFile(server, false); + if (bestdepth != FDEPTH_MISSING) + Q_snprintfz (sv.modelname, sizeof(sv.modelname), "%s", server); + else + { //not an exact name, scan the maps subdir. + for (i = 0; exts[i]; i++) + { + depth = COM_FDepthFile(va(exts[i], server), false); + if (depth < bestdepth) + { + bestdepth = depth; + Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[i], server); + } + } + } + *mod = '#'; + if (bestdepth == FDEPTH_MISSING) + mod = NULL; + } + } if (!strncmp(sv.modelname, "maps/", 5)) Q_strncpyz (svs.name, sv.modelname+5, sizeof(svs.name)); @@ -1048,6 +1073,8 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, if (!strcmp(e, ".bsp")) *e = 0; + Mod_SetModifier(mod); + sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR); if (FS_FLocateFile(sv.modelname,FSLF_IFFOUND, &loc) && FS_GetLocMTime(&loc, &filetime))