Detect if an autosave is loaded and advance world by 100 frames.

Autosaves are special. The are a byproduct of the level change process.
When loaded they aren't respawning the player at it's last position, the
player is relocated to func_playerstart. Since entities spawn at their
start position, the player may end up in the wrong spot.

One example is train.bsp -> base2.bsp. The platform spawns in upper
position, the player in lower position. The platform comes down and
crushes the player.

Most of these cases work by luck when the client isn't paused during
load, because the world advances a few frames before the player is
spawned. Implement a better fix: Detect if an autosave is loaded (name
is save0 or current) and treat it like a map change, advance the world
by 100 frames. We cant use the `autosave` boolean, because it's in the
game savefile.

Fix suggested by @BjossiAlfreds, closes #711.
This commit is contained in:
Yamagi 2021-05-29 17:16:42 +02:00
parent 8f6a085434
commit c9913eb538
4 changed files with 26 additions and 13 deletions

View file

@ -217,7 +217,7 @@ void Master_Heartbeat(void);
void Master_Packet(void);
void SV_InitGame(void);
void SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame);
void SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isautosave);
void SV_PrepWorldFrame(void);

View file

@ -156,7 +156,7 @@ SV_DemoMap_f(void)
return;
}
SV_Map(true, Cmd_Argv(1), false);
SV_Map(true, Cmd_Argv(1), false, false);
}
/*
@ -261,7 +261,7 @@ SV_GameMap_f(void)
/* start up the next map */
SV_Map(false, map, false);
SV_Map(false, map, false, false);
/* archive server state */
Q_strlcpy(svs.mapcmd, map, sizeof(svs.mapcmd));

View file

@ -123,7 +123,7 @@ SV_CreateBaseline(void)
}
void
SV_CheckForSavegame(void)
SV_CheckForSavegame(qboolean isautosave)
{
char name[MAX_OSPATH];
FILE *f;
@ -155,7 +155,7 @@ SV_CheckForSavegame(void)
/* get configstrings and areaportals */
SV_ReadLevelFile();
if (!sv.loadgame)
if (!sv.loadgame || (sv.loadgame && isautosave))
{
/* coming back to a level after being in a different
level, so run it for ten seconds */
@ -179,7 +179,7 @@ SV_CheckForSavegame(void)
*/
void
SV_SpawnServer(char *server, char *spawnpoint, server_state_t serverstate,
qboolean attractloop, qboolean loadgame)
qboolean attractloop, qboolean loadgame, qboolean isautosave)
{
int i;
unsigned checksum;
@ -295,7 +295,7 @@ SV_SpawnServer(char *server, char *spawnpoint, server_state_t serverstate,
SV_CreateBaseline();
/* check for a savegame */
SV_CheckForSavegame();
SV_CheckForSavegame(isautosave);
/* set serverinfo variable */
Cvar_FullSet("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET);
@ -439,7 +439,7 @@ SV_InitGame(void)
* map tram.cin+jail_e3
*/
void
SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame)
SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isautosave)
{
char level[MAX_QPATH];
char *ch;
@ -506,7 +506,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame)
SCR_BeginLoadingPlaque(); /* for local system */
#endif
SV_BroadcastCommand("changing\n");
SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame);
SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame, isautosave);
}
else if ((l > 4) && !strcmp(level + l - 4, ".dm2"))
{
@ -514,7 +514,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame)
SCR_BeginLoadingPlaque(); /* for local system */
#endif
SV_BroadcastCommand("changing\n");
SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame);
SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame, isautosave);
}
else if ((l > 4) && !strcmp(level + l - 4, ".pcx"))
{
@ -522,7 +522,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame)
SCR_BeginLoadingPlaque(); /* for local system */
#endif
SV_BroadcastCommand("changing\n");
SV_SpawnServer(level, spawnpoint, ss_pic, attractloop, loadgame);
SV_SpawnServer(level, spawnpoint, ss_pic, attractloop, loadgame, isautosave);
}
else
{
@ -531,7 +531,7 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame)
#endif
SV_BroadcastCommand("changing\n");
SV_SendClientMessages();
SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame);
SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame, isautosave);
Cbuf_CopyToDefer();
}

View file

@ -417,6 +417,7 @@ SV_Loadgame_f(void)
char name[MAX_OSPATH];
FILE *f;
char *dir;
qboolean isautosave;
if (Cmd_Argc() != 2)
{
@ -444,6 +445,18 @@ SV_Loadgame_f(void)
return;
}
Com_Printf("Savegame: %s\n", Cmd_Argv(1));
if (strcmp(Cmd_Argv(1), "save0") == 0 ||
strcmp(Cmd_Argv(1), "current") == 0)
{
isautosave = true;
}
else
{
isautosave = false;
}
fclose(f);
SV_CopySaveGame(Cmd_Argv(1), "current");
@ -452,7 +465,7 @@ SV_Loadgame_f(void)
/* go to the map */
sv.state = ss_dead; /* don't save current level when changing */
SV_Map(false, svs.mapcmd, true);
SV_Map(false, svs.mapcmd, true, isautosave);
}
void