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 Master_Packet(void);
void SV_InitGame(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); void SV_PrepWorldFrame(void);

View file

@ -156,7 +156,7 @@ SV_DemoMap_f(void)
return; 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 */ /* start up the next map */
SV_Map(false, map, false); SV_Map(false, map, false, false);
/* archive server state */ /* archive server state */
Q_strlcpy(svs.mapcmd, map, sizeof(svs.mapcmd)); Q_strlcpy(svs.mapcmd, map, sizeof(svs.mapcmd));

View file

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

View file

@ -417,6 +417,7 @@ SV_Loadgame_f(void)
char name[MAX_OSPATH]; char name[MAX_OSPATH];
FILE *f; FILE *f;
char *dir; char *dir;
qboolean isautosave;
if (Cmd_Argc() != 2) if (Cmd_Argc() != 2)
{ {
@ -444,6 +445,18 @@ SV_Loadgame_f(void)
return; 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); fclose(f);
SV_CopySaveGame(Cmd_Argv(1), "current"); SV_CopySaveGame(Cmd_Argv(1), "current");
@ -452,7 +465,7 @@ SV_Loadgame_f(void)
/* go to the map */ /* go to the map */
sv.state = ss_dead; /* don't save current level when changing */ 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 void