Trouble with saving the character's position on leave

I’m having an issue with saving the character’s position when the player leaves. I want to try to avoid a loop constantly saving the player’s position but if it’s the only way then so be it. Here’s my current code:


local function savePlayerPosition(plr)
	plr.CharacterRemoving:Connect(function(char)
		local hrp = char:FindFirstChild("HumanoidRootPart")
		local pos = {hrp.Position.X, hrp.Position.Y, hrp.Position.Z}
		print(pos) --doesn't print
		datastore:SetAsync(plr.UserId.."_lastpos", pos)
	end)
end

local function loadPlayerPosition(plr)
	plr.CharacterAdded:Connect(function(char)
		local pos = datastore:GetAsync(plr.UserId.."_lastpos")
		if pos == nil then return end --stops here cuz no pos
		local x, y, z = pos[1], pos[2], pos[3]
		print(x,y,z) 
		char.HumanoidRootPart.Position = Vector3.new(x,y,z)
	end)
end

Players.PlayerAdded:Connect(loadPlayerPosition)
Players.PlayerRemoving:Connect(savePlayerPosition)

Some other posts I looked at but didn’t seem to work:

Also tried using BindToClose with a task.wait(3) but it didn’t seem to change much. Let me know if my code is wrong or I’m missing something if that’s the case. Thanks

You didn’t really explain what the issue is, but looking at your code

This doesn’t print because CharacterRemoving is being connected as the player is leaving. What you can try doing, instead, is checking whether the player has a character or not as they are leaving, and consequentially saving the position of their character:

I also made sure to actually use the HRP variable properly since you were using FindFirstChild but not really doing anything with it. FindFirstChild is used to check whether an instance exists or not. You were using it but not checking if the HRP actually exists, so you could’ve just done char.HumanoidRootPart instead.

1 Like

Thanks for the response. I should’ve explained better though, what I want is for the player’s position to save when they leave the game so that I can move them to the position when they join back. It doesn’t seem like plr.CharacterRemoving:Connect(function() is running at all when I leave the game, (placed a print() statement right after). The saving seems to work fine.

Yep, I figured that’s what you were trying to do. My method basically gets rid of CharacterRemoving since it’s not needed. Since we know the player is getting removed, we can just check if they have a character as they are leaving, and then use that to save its position. Unless my code doesn’t work then, I didn’t test it so let me know if you try it

1 Like

Oh, my mistake. The code sample seems to work with PlayerRemoving but I’m still a bit iffy since I saw some people on the devforum say it occasionally doesn’t pickup on the character. I’ll do some testing, thanks

Actually yeah it doesn’t seem to print pos … Hmmm


local function savePlayerPosition(plr)
	print("char")
	local char = plr.Character
	if char == nil then return end
	local hrp = char:FindFirstChild("HumanoidRootPart")
	if hrp == nil then return end
	local pos = {hrp.Position.X, hrp.Position.Y, hrp.Position.Z}
	print(pos) --doesn't print
	datastore:SetAsync(plr.UserId.."_lastpos", pos)
end

Alright then it’s possible the character is treated like when it falls under position -500 and every instance in it is removed (besides the humanoid). So another solution I can think of is to connect the CharacterRemoving function under the PlayerAdded one. Even tho this would mean the position is saved with every respawn, it shouldn’t be a big deal tho.

1 Like

While it does work, I still am looking for some way to get the player’s position to save upon disconnect if it’s possible :smiling_face_with_tear:

This should work to achieve what you’re looking for:

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local dataStore = DataStoreService:GetDataStore("Example") -- Change the name to match the real name of your DataStore


local function savePlayerPosition(player: Player)
	if player.Character then
		local position = player.Character:GetPivot().Position -- Same as doing player.Character.HumanoidRootPart.Position
		local data = {position.X, position.Y, position.Z}
		print(data)

		local success, error = pcall(dataStore.SetAsync, dataStore, player.UserId.."_lastpos", data)
		if success then return end

		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName})\nError: {error}`)
	else
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName}), as they currently have no character`)
	end
end

game:BindToClose(function()
	local x, y = 0, 0

	local function onBindToClose(player: Player)
		savePlayerPosition(player)

		y += 1
	end

	for _, player in Players:GetPlayers() do
		x += 1

		task.spawn(onBindToClose, player)
	end

	repeat task.wait() until y == x
end)

Players.PlayerRemoving:Connect(savePlayerPosition)
1 Like

Forgive me if I’m wrong but won’t this only work if there is one player since it is using BindToClose. Again forgive me if I’m wrong :pray:

I’m connecting the savePlayerPosition function to Players.PlayerRemoving at the last line, so it will save the data for any player that leaves the game, not just the last one to leave. The BindToClose is there to save the positions of each player present in the case that the server randomly shuts down

1 Like

Seems like it cannot find the character still.
Unable to save position for Player 861475515 (kekprod), as they currently have no character

Correct me if I inserted your code incorrectly.

local function savePlayerPosition(player: Player)
	if player.Character then
		local position = player.Character:GetPivot().Position -- Same as doing player.Character.HumanoidRootPart.Position
		local data = {position.X, position.Y, position.Z}
		print(data)

		local success, error = pcall(datastore.SetAsync, datastore, player.UserId.."_lastpos", data)
		if success then return end
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName})\nError: {error}`)
	else
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName}), as they currently have no character`)
	end
end

game:BindToClose(function()
	local x, y = 0, 0

	local function onBindToClose(player: Player)
		savePlayerPosition(player)
		y += 1
	end

	for _, player in Players:GetPlayers() do
		x += 1
		task.spawn(onBindToClose, player)
	end
	repeat task.wait() until y == x
end)

local function loadPlayerPosition(plr)
	plr.CharacterAdded:Connect(function()
		local char = plr.Character
		local pos = datastore:GetAsync(plr.UserId.."_lastpos")
		if pos == nil then return end --stops here cuz no pos
		if char == nil then return end
		local x, y, z = pos[1], pos[2], pos[3]
		print(x,y,z) 
		char.HumanoidRootPart.Position = Vector3.new(x,y,z)
	end)
end

Players.PlayerAdded:Connect(loadPlayerPosition)
Players.PlayerRemoving:Connect(savePlayerPosition)

1 Like

Try setting Workspace’s PlayerCharacterDestroyBehavior to Disabled (needs to be done in the Properties tab). It could be that the player’s character is being destroyed as soon as the player attempts to leave the game, which would explain why all the code so far is failing to work

1 Like

Doesn’t seem to work still after disabling it :smiling_face_with_tear:
Unable to save position for Player 861475515 (kekprod), as they currently have no character

1 Like

I was able to replicate the problem: It seems player.Character is always nil when used in the PlayerRemoving event

Doing this instead has fixed the issue for me:

local function savePlayerPosition(player: Player)
	if player.Character then
		local position = player.Character:GetPivot().Position -- Same as doing player.Character.HumanoidRootPart.Position
		local data = {position.X, position.Y, position.Z}
		print(data)

		local success, error = pcall(datastore.SetAsync, datastore, player.UserId.."_lastpos", data)
		if success then return end
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName})\nError: {error}`)
	else
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName}), as they currently have no character`)
	end
end

game:BindToClose(function()
	local x, y = 0, 0

	local function onBindToClose(player: Player)
		savePlayerPosition(player)
		y += 1
	end

	for _, player in Players:GetPlayers() do
		x += 1
		task.spawn(onBindToClose, player)
	end
	repeat task.wait() until y == x
end)

local function loadPlayerPosition(plr)
	plr.CharacterAdded:Connect(function()
		local char = plr.Character
		local pos = datastore:GetAsync(plr.UserId.."_lastpos")
		if pos == nil then return end --stops here cuz no pos
		if char == nil then return end
		local x, y, z = pos[1], pos[2], pos[3]
		print(x,y,z) 
		char.HumanoidRootPart.Position = Vector3.new(x,y,z)
	end)

	plr.CharacterRemoving:Connect(function()
		savePlayerPosition(plr)
	end) -- This does mean the position will also be saved when the player resets, though
end

Players.PlayerAdded:Connect(loadPlayerPosition)

Although it will also save the position when the player resets, but at least it does save it right before the player leaves the game


@kek_productions Sorry there was a bug, it should work now

1 Like

Thanks for the help. But it doesn’t seem to be working even still.
Unable to save position for Player 861475515 (kekprod), as they currently have no character on disconnect (studio playtest stop is how i’m disconnecting)
Works on reset though. (prints position, I think my loading code is probably poor)

1 Like

I did some testing, and this should work now:

local characters = {}

local function savePlayerPosition(player: Player)
	if characters[player] then
		local position = characters[player]:GetPivot().Position -- Same as doing player.Character.HumanoidRootPart.Position
		local data = {position.X, position.Y, position.Z}
		print(data)

		characters[player] = nil -- So that the table is cleaned up

		local success, error = pcall(datastore.SetAsync, datastore, player.UserId.."_lastpos", data)
		if success then return end
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName})\nError: {error}`)
	else
		warn(`Unable to save position for Player {player.UserId} ({player.DisplayName}), as they currently have no character saved in the characters table`)
	end
end

game:BindToClose(function()
	local x, y = 0, 0

	local function onBindToClose(player: Player)
		savePlayerPosition(player)
		y += 1
	end

	for _, player in Players:GetPlayers() do
		x += 1
		task.spawn(onBindToClose, player)
	end
	repeat task.wait() until y == x
end)

local function loadPlayerPosition(plr)
	plr.CharacterAdded:Connect(function(char)
		characters[plr] = char

		local success, pos = pcall(datastore.GetAsync, datastore, plr.UserId.."_lastpos")

		if success then
			if pos then
				char:MoveTo(Vector3.new(pos[1], pos[2], pos[3]))
			end
		else
			warn(`Failed to get data for Player {plr.UserId} ({plr.DisplayName})\nError: {pos}`)
		end
	end)
end

Players.PlayerAdded:Connect(loadPlayerPosition)
Players.PlayerRemoving:Connect(savePlayerPosition)

I’m storing the player’s character inside of a table that is updated each time the player spawns a new character, and when the player is leaving the game, I’m retrieving their character from that table instead of using player.Character

I also went ahead and cleaned up the function for when the data is being loaded

1 Like

Oh wow interesting, I’ll have a look tomorrow. Thank you for the great help

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.