diff --git a/docs/rh-log.txt b/docs/rh-log.txt
index 5a3351cf2e..02252b94da 100644
--- a/docs/rh-log.txt
+++ b/docs/rh-log.txt
@@ -1,3 +1,10 @@
+July 5, 2008
+- Added a check to G_DoSaveGame() to prevent saving when you're not actually
+  in a level.
+- Fixed: Serialized player data must always be loaded, even if it's simply to
+  be discarded, so that anything serialized after the players will load from
+  the correct position in the file when revisiting a hub map.
+
 July 5, 2008 (Changes by Graf Zahl)
 - Changed: AInventory::Tick now only calls the super method if the item is not
   owned. Having owned inventory items interact with the world is not supposed
@@ -16,7 +23,7 @@ July 5, 2008 (Changes by Graf Zahl)
 
 July 4, 2008
 - Fixed: Screenwipes now pause sounds, since there can be sounds playing
-  during it.
+  during them.
 - UI sounds are now omitted from savegames.
 - Fixed: Menu sounds had been restricted to one at a time again.
 - Moved the P_SerializeSounds() call to the end of G_SerializeLevel() so that
diff --git a/src/g_game.cpp b/src/g_game.cpp
index 938a289918..dab6b5f4c0 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -1989,6 +1989,13 @@ static void PutSavePic (FILE *file, int width, int height)
 
 void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description)
 {
+	// Do not even try, if we're not in a level. (Can happen after
+	// a demo finishes playback.)
+	if (lines == NULL || sectors == NULL)
+	{
+		return;
+	}
+
 	if (demoplayback)
 	{
 		filename = G_BuildSaveName ("demosave.zds", -1);
diff --git a/src/g_level.cpp b/src/g_level.cpp
index 657faff5ca..7befda5035 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -2860,10 +2860,7 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad)
 	FCanvasTextureInfo::Serialize (arc);
 	AM_SerializeMarkers(arc);
 
-	if (!hubLoad)
-	{
-		P_SerializePlayers (arc);
-	}
+	P_SerializePlayers (arc, hubLoad);
 	P_SerializeSounds (arc);
 }
 
diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp
index 682d6963f9..596b1367ea 100644
--- a/src/p_saveg.cpp
+++ b/src/p_saveg.cpp
@@ -48,14 +48,14 @@
 #include "r_interpolate.h"
 
 static void CopyPlayer (player_t *dst, player_t *src, const char *name);
-static void ReadOnePlayer (FArchive &arc);
-static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow);
+static void ReadOnePlayer (FArchive &arc, bool skipload);
+static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload);
 static void SpawnExtraPlayers ();
 
 //
 // P_ArchivePlayers
 //
-void P_SerializePlayers (FArchive &arc)
+void P_SerializePlayers (FArchive &arc, bool skipload)
 {
 	BYTE numPlayers, numPlayersNow;
 	int i;
@@ -92,20 +92,20 @@ void P_SerializePlayers (FArchive &arc)
 		// first player present, no matter what their name.
 		if (numPlayers == 1)
 		{
-			ReadOnePlayer (arc);
+			ReadOnePlayer (arc, skipload);
 		}
 		else
 		{
-			ReadMultiplePlayers (arc, numPlayers, numPlayersNow);
+			ReadMultiplePlayers (arc, numPlayers, numPlayersNow, skipload);
 		}
-		if (numPlayersNow > numPlayers)
+		if (!skipload && numPlayersNow > numPlayers)
 		{
 			SpawnExtraPlayers ();
 		}
 	}
 }
 
-static void ReadOnePlayer (FArchive &arc)
+static void ReadOnePlayer (FArchive &arc, bool skipload)
 {
 	int i;
 	char *name = NULL;
@@ -122,7 +122,10 @@ static void ReadOnePlayer (FArchive &arc)
 				didIt = true;
 				player_t playerTemp;
 				playerTemp.Serialize (arc);
-				CopyPlayer (&players[i], &playerTemp, name);
+				if (!skipload)
+				{
+					CopyPlayer (&players[i], &playerTemp, name);
+				}
 			}
 			else
 			{
@@ -137,7 +140,7 @@ static void ReadOnePlayer (FArchive &arc)
 	delete[] name;
 }
 
-static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow)
+static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload)
 {
 	// For two or more players, read each player into a temporary array.
 	int i, j;
@@ -158,36 +161,19 @@ static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNo
 		playerUsed[i] = playeringame[i] ? 0 : 2;
 	}
 
-	// Now try to match players from the savegame with players present
-	// based on their names. If two players in the savegame have the
-	// same name, then they are assigned to players in the current game
-	// on a first-come, first-served basis.
-	for (i = 0; i < numPlayers; ++i)
+	if (!skipload)
 	{
-		for (j = 0; j < MAXPLAYERS; ++j)
-		{
-			if (playerUsed[j] == 0 && stricmp(players[j].userinfo.netname, nametemp[i]) == 0)
-			{ // Found a match, so copy our temp player to the real player
-				Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j);
-				CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
-				playerUsed[j] = 1;
-				tempPlayerUsed[i] = 1;
-				break;
-			}
-		}
-	}
-
-	// Any players that didn't have matching names are assigned to existing
-	// players on a first-come, first-served basis.
-	for (i = 0; i < numPlayers; ++i)
-	{
-		if (tempPlayerUsed[i] == 0)
+		// Now try to match players from the savegame with players present
+		// based on their names. If two players in the savegame have the
+		// same name, then they are assigned to players in the current game
+		// on a first-come, first-served basis.
+		for (i = 0; i < numPlayers; ++i)
 		{
 			for (j = 0; j < MAXPLAYERS; ++j)
 			{
-				if (playerUsed[j] == 0)
-				{
-					Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.netname);
+				if (playerUsed[j] == 0 && stricmp(players[j].userinfo.netname, nametemp[i]) == 0)
+				{ // Found a match, so copy our temp player to the real player
+					Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j);
 					CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
 					playerUsed[j] = 1;
 					tempPlayerUsed[i] = 1;
@@ -195,17 +181,37 @@ static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNo
 				}
 			}
 		}
-	}
 
-	// Make sure any extra players don't have actors spawned yet.
-	for (j = 0; j < MAXPLAYERS; ++j)
-	{
-		if (playerUsed[j] == 0)
+		// Any players that didn't have matching names are assigned to existing
+		// players on a first-come, first-served basis.
+		for (i = 0; i < numPlayers; ++i)
 		{
-			if (players[j].mo != NULL)
+			if (tempPlayerUsed[i] == 0)
 			{
-				players[j].mo->Destroy();
-				players[j].mo = NULL;
+				for (j = 0; j < MAXPLAYERS; ++j)
+				{
+					if (playerUsed[j] == 0)
+					{
+						Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.netname);
+						CopyPlayer (&players[j], &playertemp[i], nametemp[i]);
+						playerUsed[j] = 1;
+						tempPlayerUsed[i] = 1;
+						break;
+					}
+				}
+			}
+		}
+
+		// Make sure any extra players don't have actors spawned yet.
+		for (j = 0; j < MAXPLAYERS; ++j)
+		{
+			if (playerUsed[j] == 0)
+			{
+				if (players[j].mo != NULL)
+				{
+					players[j].mo->Destroy();
+					players[j].mo = NULL;
+				}
 			}
 		}
 	}
diff --git a/src/p_saveg.h b/src/p_saveg.h
index edb73e67ee..09f5f28e21 100644
--- a/src/p_saveg.h
+++ b/src/p_saveg.h
@@ -39,7 +39,7 @@
 // Persistent storage/archiving.
 // These are the load / save game routines.
 // Also see farchive.(h|cpp)
-void P_SerializePlayers (FArchive &arc);
+void P_SerializePlayers (FArchive &arc, bool fakeload);
 void P_SerializeWorld (FArchive &arc);
 void P_SerializeThinkers (FArchive &arc, bool);
 void P_SerializePolyobjs (FArchive &arc);