diff --git a/src/d_player.h b/src/d_player.h
index 7c21358c8..fdcd29e6a 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -280,7 +280,8 @@ class player_t
 public:
 	player_t() = default;
 	~player_t();
-	player_t &operator= (const player_t &p);
+	player_t &operator= (const player_t &p) = delete;
+	void CopyFrom(player_t &src, bool copyPSP);
 
 	void Serialize(FSerializer &arc);
 	size_t PropagateMark();
diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp
index 61b9ec267..7421185ee 100644
--- a/src/p_saveg.cpp
+++ b/src/p_saveg.cpp
@@ -813,8 +813,7 @@ void FLevelLocals::CopyPlayer(player_t *dst, player_t *src, const char *name)
 	bool attackdown = dst->attackdown;
 	bool usedown = dst->usedown;
 
-
-	*dst = *src;		// To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
+	dst->CopyFrom(*src, true);	// To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above.
 
 	dst->cheats |= chasecam;
 
@@ -857,9 +856,6 @@ void FLevelLocals::CopyPlayer(player_t *dst, player_t *src, const char *name)
 		pspr = pspr->Next;
 	}
 
-	// Don't let the psprites be destroyed when src is destroyed.
-	src->psprites = nullptr;
-
 	// These 2 variables may not be overwritten.
 	dst->attackdown = attackdown;
 	dst->usedown = usedown;
diff --git a/src/p_user.cpp b/src/p_user.cpp
index 66d8d013f..cd0699829 100644
--- a/src/p_user.cpp
+++ b/src/p_user.cpp
@@ -259,7 +259,7 @@ player_t::~player_t()
 	DestroyPSprites();
 }
 
-player_t &player_t::operator=(const player_t &p)
+void player_t::CopyFrom(player_t &p, bool copyPSP)
 {
 	mo = p.mo;
 	playerstate = p.playerstate;
@@ -312,7 +312,6 @@ player_t &player_t::operator=(const player_t &p)
 	extralight = p.extralight;
 	fixedcolormap = p.fixedcolormap;
 	fixedlightlevel = p.fixedlightlevel;
-	psprites = p.psprites;
 	morphTics = p.morphTics;
 	MorphedPlayerClass = p.MorphedPlayerClass;
 	MorphStyle = p.MorphStyle;
@@ -346,7 +345,13 @@ player_t &player_t::operator=(const player_t &p)
 	ConversationFaceTalker = p.ConversationFaceTalker;
 	MUSINFOactor = p.MUSINFOactor;
 	MUSINFOtics = p.MUSINFOtics;
-	return *this;
+	if (copyPSP)
+	{
+		// This needs to transfer ownership completely.
+		psprites = p.psprites;
+		p.psprites = nullptr;
+	}
+	else psprites = nullptr;
 }
 
 size_t player_t::PropagateMark()
@@ -1369,7 +1374,7 @@ void P_PredictPlayer (player_t *player)
 	}
 
 	// Save original values for restoration later
-	PredictionPlayerBackup = *player;
+	PredictionPlayerBackup.CopyFrom(*player, false);
 
 	auto act = player->mo;
 	PredictionActor = player->mo;
@@ -1494,7 +1499,7 @@ void P_UnPredictPlayer ()
 		int inventorytics = player->inventorytics;
 		const bool settings_controller = player->settings_controller;
 
-		*player = PredictionPlayerBackup;
+		player->CopyFrom(PredictionPlayerBackup, false);
 
 		player->settings_controller = settings_controller;
 		// Restore the camera instead of using the backup's copy, because spynext/prev