Speed up file loading on server join by splitting P_AddWadFile into stages

This commit is contained in:
X.organic 2022-09-05 15:04:49 +00:00 committed by Sal
parent 78cf02c221
commit 6fb59f87b7
6 changed files with 156 additions and 186 deletions

View File

@ -1117,6 +1117,7 @@ typedef enum
CL_DOWNLOADFILES,
CL_ASKJOIN,
CL_LOADFILES,
CL_SETUPFILES,
CL_WAITJOINRESPONSE,
#ifdef JOININGAME
CL_DOWNLOADSAVEGAME,
@ -1228,6 +1229,9 @@ static inline void CL_DrawConnectionStatus(void)
case CL_CONFIRMCONNECT:
cltext = "";
break;
case CL_SETUPFILES:
cltext = M_GetText("Configuring addons...");
break;
case CL_ASKJOIN:
case CL_WAITJOINRESPONSE:
if (serverisfull)
@ -2429,7 +2433,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
cl_mode = CL_LOADFILES;
break;
case CL_LOADFILES:
if (CL_LoadServerFiles())
if (CL_LoadServerFiles())
cl_mode = CL_SETUPFILES;
break;
case CL_SETUPFILES:
if (P_PartialAddGetStage() < 0 || P_MultiSetupWadFiles(false))
{
*asksent = 0; //This ensure the first join ask is right away
firstconnectattempttime = I_GetTime();
@ -2635,7 +2644,11 @@ static void CL_ConnectToServer(void)
#else
if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL))
#endif
{
if (P_PartialAddGetStage() >= 0)
P_MultiSetupWadFiles(true); // in case any partial adds were done
return;
}
if (server)
{

View File

@ -486,7 +486,7 @@ boolean CL_LoadServerFiles(void)
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename);
P_PartialAddWadFile(fileneeded[i].filename);
G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
return false;

View File

@ -2641,109 +2641,6 @@ static void readconditionset(MYFILE *f, UINT8 setnum)
Z_Free(s);
}
static void readtexture(MYFILE *f, const char *name)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word;
char *word2;
char *tmp;
INT32 i, j, value;
UINT16 width = 0, height = 0;
INT16 patchcount = 0;
texture_t *texture;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
value = searchvalue(s);
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " ");
if (word2)
strupr(word2);
else
break;
// Width of the texture.
if (fastcmp(word, "WIDTH"))
{
DEH_WriteUndoline(word, va("%d", width), UNDO_NONE);
width = SHORT((UINT16)value);
}
// Height of the texture.
else if (fastcmp(word, "HEIGHT"))
{
DEH_WriteUndoline(word, va("%d", height), UNDO_NONE);
height = SHORT((UINT16)value);
}
// Number of patches the texture has.
else if (fastcmp(word, "NUMPATCHES"))
{
DEH_WriteUndoline(word, va("%d", patchcount), UNDO_NONE);
patchcount = SHORT((UINT16)value);
}
else
deh_warning("readtexture: unknown word '%s'", word);
}
} while (!myfeof(f));
// Error checking.
if (!width)
I_Error("Texture %s has no width!\n", name);
if (!height)
I_Error("Texture %s has no height!\n", name);
if (!patchcount)
I_Error("Texture %s has no patches!\n", name);
// Allocate memory for the texture, and fill in information.
texture = Z_Calloc(sizeof(texture_t) + (sizeof(texpatch_t) * SHORT(patchcount)), PU_STATIC, NULL);
M_Memcpy(texture->name, name, sizeof(texture->name));
texture->width = width;
texture->height = height;
texture->patchcount = patchcount;
texture->holes = false;
// Fill out the texture patches, to allow them to be detected
// accurately by readpatch.
for (i = 0; i < patchcount; i++)
{
texture->patches[i].originx = 0;
texture->patches[i].originy = 0;
texture->patches[i].wad = UINT16_MAX;
texture->patches[i].lump = UINT16_MAX;
}
// Jump to the next empty texture entry.
i = 0;
while (textures[i])
i++;
// Fill the global texture buffer entries.
j = 1;
while (j << 1 <= texture->width)
j <<= 1;
textures[i] = texture;
texturewidthmask[i] = j - 1;
textureheight[i] = texture->height << FRACBITS;
// Clean up.
Z_Free(s);
}
static void readpatch(MYFILE *f, const char *name, UINT16 wad)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -3350,14 +3247,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
i = atoi(word2);
if (fastcmp(word, "TEXTURE"))
{
// Read texture from spec file.
readtexture(f, word2);
DEH_WriteUndoline(word, word2, UNDO_HEADER);
// This is not a major mod.
}
else if (fastcmp(word, "PATCH"))
if (fastcmp(word, "PATCH"))
{
// Read patch from spec file.
readpatch(f, word2, wad);

View File

@ -143,6 +143,12 @@ mapthing_t *playerstarts[MAXPLAYERS];
mapthing_t *bluectfstarts[MAXPLAYERS];
mapthing_t *redctfstarts[MAXPLAYERS];
// Global state for PartialAddWadFile/MultiSetupWadFiles
// Might be replacable with parameters, but non-trivial when the functions are called on separate tics
static SINT8 partadd_stage = -1;
static boolean partadd_replacescurrentmap = false;
static boolean partadd_important = false;
/** Logs an error about a map being corrupt, then terminate.
* This allows reporting highly technical errors for usefulness, without
* confusing a novice map designer who simply needs to run ZenNode.
@ -3438,23 +3444,39 @@ boolean P_RunSOC(const char *socfilename)
// replace sounds, musics, patches, textures, sprites and maps
//
boolean P_AddWadFile(const char *wadfilename)
{
UINT16 wadnum;
if ((wadnum = P_PartialAddWadFile(wadfilename)) == UINT16_MAX)
return false;
P_MultiSetupWadFiles(true);
return true;
}
//
// Add a WAD file and do the per-WAD setup stages.
// Call P_MultiSetupWadFiles as soon as possible after any number of these.
//
UINT16 P_PartialAddWadFile(const char *wadfilename)
{
size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
UINT16 numlumps, wadnum;
char *name;
lumpinfo_t *lumpinfo;
boolean texturechange = false;
boolean mapsadded = false;
boolean replacedcurrentmap = false;
lumpinfo_t *lumpinfo;
if ((numlumps = W_InitFile(wadfilename)) == INT16_MAX)
{
refreshdirmenu |= REFRESHDIR_NOTLOADED;
CONS_Printf(M_GetText("Errors occurred while loading %s; not added.\n"), wadfilename);
return false;
return UINT16_MAX;
}
else wadnum = (UINT16)(numwadfiles-1);
if (wadfiles[wadnum]->important)
partadd_important = true;
//
// search for sound replacements
//
@ -3488,14 +3510,6 @@ boolean P_AddWadFile(const char *wadfilename)
CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
digmreplaces++;
}
#if 0
//
// search for texturechange replacements
//
else if (!memcmp(name, "TEXTURE1", 8) || !memcmp(name, "TEXTURE2", 8)
|| !memcmp(name, "PNAMES", 6))
#endif
texturechange = true;
}
if (!devparm && sreplaces)
CONS_Printf(M_GetText("%s sounds replaced\n"), sizeu1(sreplaces));
@ -3504,28 +3518,12 @@ boolean P_AddWadFile(const char *wadfilename)
if (!devparm && digmreplaces)
CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
//
// search for sprite replacements
//
R_AddSpriteDefs(wadnum);
// Reload it all anyway, just in case they
// added some textures but didn't insert a
// TEXTURE1/PNAMES/etc. list.
if (texturechange) // initialized in the sound check
R_LoadTextures(); // numtexture changes
else
R_FlushTextureCache(); // just reload it from file
// Reload ANIMATED / ANIMDEFS
P_InitPicAnims();
// Flush and reload HUD graphics
ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
ST_ReloadSkinFaceGraphics();
// everything from MultiSetupWadFile until ST_Start was here originally
//
// look for skins
@ -3560,9 +3558,8 @@ boolean P_AddWadFile(const char *wadfilename)
mapheaderinfo[num-1]->menuflags |= LF2_EXISTSHACK;
}
//If you replaced the map you're on, end the level when done.
if (num == gamemap)
replacedcurrentmap = true;
partadd_replacescurrentmap = true;
CONS_Printf("%s\n", name);
mapsadded = true;
@ -3571,24 +3568,85 @@ boolean P_AddWadFile(const char *wadfilename)
if (!mapsadded)
CONS_Printf(M_GetText("No maps added\n"));
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
partadd_stage = 0;
return wadnum;
}
// Prevent savefile cheating
if (cursaveslot >= 0)
cursaveslot = -1;
// Only exists to make sure there's no way to overwrite partadd_stage externally
// unless you really push yourself.
SINT8 P_PartialAddGetStage(void) {
return partadd_stage;
}
if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
//
// Set up a series of partially added WAD files.
// Setup functions that iterate over every loaded WAD go here.
// If fullsetup false, only do one stage per call.
//
boolean P_MultiSetupWadFiles(boolean fullsetup)
{
if (partadd_stage < 0)
I_Error(M_GetText("Post-load addon setup attempted without loading any addons first"));
if (partadd_stage == 0)
{
CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
// Flush and reload HUD graphics
ST_UnloadGraphics();
HU_LoadGraphics();
ST_LoadGraphics();
ST_ReloadSkinFaceGraphics();
if (!partadd_important)
partadd_stage = -1; // everything done
else if (fullsetup)
++partadd_stage; // run next stage too
}
refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
if (partadd_stage == 1)
{
// Reload all textures, unconditionally for better or worse.
R_LoadTextures();
return true;
if (fullsetup)
++partadd_stage;
}
if (partadd_stage == 2)
{
// Reload ANIMATED / ANIMDEFS
P_InitPicAnims();
// reload status bar (warning should have valid player!)
if (gamestate == GS_LEVEL)
ST_Start();
// Prevent savefile cheating
if (cursaveslot >= 0)
cursaveslot = -1;
if (partadd_replacescurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
{
CONS_Printf(M_GetText("Current map %d replaced, ending the level to ensure consistency.\n"), gamemap);
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
partadd_stage = -1;
}
I_Assert(!fullsetup || partadd_stage < 0);
if (partadd_stage < 0)
{
partadd_important = false;
partadd_replacescurrentmap = false;
return true;
}
else
{
++partadd_stage;
return false;
}
}
#ifdef DELFILE

View File

@ -64,6 +64,26 @@ boolean P_AddWadFile(const char *wadfilename);
#ifdef DELFILE
boolean P_DelWadFile(void);
#endif
// WARNING: The following functions should be grouped as follows:
// any amount of PartialAdds followed by MultiSetups until returned true,
// as soon as possible.
UINT16 P_PartialAddWadFile(const char *wadfilename);
// Run a single stage of multisetup, or all of them if fullsetup set.
// fullsetup true: run everything
// otherwise
// stage 0: reload UI graphics (enough for unimportant WADs)
// stage 1: reload textures
// stage 2: reload animdefs and run post-setup actions
// returns true if setup finished on this call, false otherwise (always true on fullsetup)
// throws I_Error if called without any partial adds started as a safeguard
boolean P_MultiSetupWadFiles(boolean fullsetup);
// Get the current setup stage.
// if negative, no PartialAdds done since last MultiSetup
// if 0, partial adds done but MultiSetup not called yet
// if positive, setup's partway done
SINT8 P_PartialAddGetStage(void);
boolean P_RunSOC(const char *socfilename);
void P_WriteThings(lumpnum_t lump);
size_t P_PrecacheLevelFlats(void);

View File

@ -503,42 +503,31 @@ void R_LoadTextures(void)
}
patchlump = W_CacheLumpNumPwad((UINT16)w, texstart + j, PU_CACHE);
// Then, check the lump directly to see if it's a texture SOC,
// and if it is, load it using dehacked instead.
if (strstr((const char *)patchlump, "TEXTURE"))
{
CONS_Alert(CONS_WARNING, "%s is a Texture SOC.\n", W_CheckNameForNumPwad((UINT16)w,texstart+j));
Z_Unlock(patchlump);
DEH_LoadDehackedLumpPwad((UINT16)w, texstart + j);
}
else
{
//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
// Set texture properties.
M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name));
texture->width = SHORT(patchlump->width);
texture->height = SHORT(patchlump->height);
texture->patchcount = 1;
texture->holes = false;
// Set texture properties.
M_Memcpy(texture->name, W_CheckNameForNumPwad((UINT16)w, texstart + j), sizeof(texture->name));
texture->width = SHORT(patchlump->width);
texture->height = SHORT(patchlump->height);
texture->patchcount = 1;
texture->holes = false;
// Allocate information for the texture's patches.
patch = &texture->patches[0];
// Allocate information for the texture's patches.
patch = &texture->patches[0];
patch->originx = patch->originy = 0;
patch->wad = (UINT16)w;
patch->lump = texstart + j;
patch->originx = patch->originy = 0;
patch->wad = (UINT16)w;
patch->lump = texstart + j;
Z_Unlock(patchlump);
Z_Unlock(patchlump);
k = 1;
while (k << 1 <= texture->width)
k <<= 1;
k = 1;
while (k << 1 <= texture->width)
k <<= 1;
texturewidthmask[i] = k - 1;
textureheight[i] = texture->height << FRACBITS;
}
texturewidthmask[i] = k - 1;
textureheight[i] = texture->height << FRACBITS;
i++;
}
}