Obby (bugs)----------------------

why sometimes when i join the game i will spawn at the spawn point not my stage
or sometimes i die i will go back to spawn point
this is the saving script:
local players = game:GetService(“Players”)
local dataStoreService = game:GetService(“DataStoreService”)
local saveDataStore = dataStoreService:GetDataStore(“SaveData”)
local max = 300

local function savePlrData(plr)

local success,err = pcall(function()

	local saveData = {}

	for _,stat in pairs(plr.leaderstats:GetChildren()) do
		
		saveData[stat.Name] = stat.Value
	
	end
	
	saveDataStore:SetAsync(plr.UserId,saveData)

end)

if not success then return err end

end
players.PlayerAdded:Connect(function(plr)

local stats = Instance.new("Folder")
stats.Name = "leaderstats"
stats.Parent = plr

local stage = Instance.new("IntValue")
stage.Name = "Stage"
stage.Parent = stats
stage.Value = 1

local rebirths = Instance.new("IntValue")
rebirths.Name = "Rebirths"
rebirths.Parent =stats

local data = saveDataStore:GetAsync(plr.UserId)



if data then 
	
	print(data.Stage)
	
	for _,stat in pairs(stats:GetChildren()) do
		
		stat.Value = data[stat.Name]
	end
	
else
	
	print(plr.Name .. " has no data")
		
end

plr.CharacterAdded:Connect(function(char)
	
	local humanoid,hrp = char:WaitForChild("Humanoid"),char:WaitForChild("HumanoidRootPart")
	
	wait()
	
	if humanoid and hrp then
		
		if stage.Value ~= 0 then
			
			local part = workspace.ObbyStages:FindFirstChild(stage.Value)
			hrp.CFrame = part.CFrame + Vector3.new(0,1,0)
			
		end
	end
end)

end)

game.ReplicatedStorage.Rebirths.OnServerEvent:Connect(function(player)
local stage = player.leaderstats.Stage.Value
if tonumber(stage) >= max then
player.leaderstats.Stage.Value = 1
player.leaderstats.Rebirths.Value = player.leaderstats.Rebirths.Value + 1
game.ReplicatedStorage.Rebirths:FireClient(player)

end

end)

players.PlayerRemoving:Connect(function(plr)

local err = savePlrData(plr)

if err then print(err) end

end)

game:BindToClose(function()

for _,plr in pairs(players:GetPlayers()) do
	
	local err = savePlrData(plr)
	
	if err then print(err) end
	
end
wait(2)

end)

and this is the stage handler:
local obbyStages = workspace:WaitForChild(“ObbyStages”)

for _,stage in pairs(obbyStages:GetChildren()) do

stage.Touched:Connect(function(hit)
	
	local hum
	
	if hit.Parent:FindFirstChild("Humanoid") then
		
		hum = hit.Parent.Humanoid
		
	end
	
	if hit.Parent and hit.Parent.Parent:FindFirstChild("Humanoid") then
		
		hum = hit.Parent.Parent.Humanoid
		
	end
	
	if hum then
		
		local plr = game.Players:GetPlayerFromCharacter(hum.Parent)
		
		local plrStage = plr.leaderstats.Stage.Value
		
		if tonumber(stage.Name) == plrStage + 1 then
			
			plr.leaderstats.Stage.Value = plr.leaderstats.Stage.Value + 1
			
		elseif tonumber(stage.Name) > plrStage + 1 then
			
			hum.Health = 0
			
		end
	end
end)

end

this is the game:
https://www.roblox.com/games/4691373457/Extreme-Parkour-Stage-locator?refPageId=6b14f600-3085-4cfa-9641-f433405f3a60

1 Like

When posting code on the forum, please put
```
above and below the code segment so it would look like this

local players = game:GetService(“Players”)
local dataStoreService = game:GetService(“DataStoreService”)
local saveDataStore = dataStoreService:GetDataStore(“SaveData”)
local max = 300

and not like this

local players = game:GetService(“Players”)
local dataStoreService = game:GetService(“DataStoreService”)
local saveDataStore = dataStoreService:GetDataStore(“SaveData”)
local max = 300

Also your post title is confusing. Why is there a line? This may confuse other users when they see your post. Please remember to make posts professional.


Putting that aside, if you want to make your development a bit easier, I’d advise using module scripts. There’s nothing bad about going for a modular structure if it makes your development process easier.

Data Store Script

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

local SaveDataStore = DataStoreService:GetDataStore("SaveData")

local function SaveData(Player)
	local PlayerData = {
		[1] = Player.leaderstats.Stage.Value,
		[2] = Player.leaderstats.Rebirths.Value
	}
	
	local Success, Result = pcall(function()
		SaveDataStore:SetAsync(Player.UserId, PlayerData)
	end)
	
	if Success then
		print(Player.Name.."'s data saved.")
	else
		error(Result)
	end
end

local function LoadData(Player)
	local Success, Result = pcall(function()
		return SaveDataStore:GetAsync(Player.UserId)
	end)
	
	if Success then
		if Result then
			Player.leaderstats.Stage.Value = Result[1]
			Player.leaderstats.Rebirths.Value = Result[2]
		else
			print(Player.Name.." has no data.")
		end
	else
		error(Result)
	end
end

local function CreateLeaderstats(Player)
	local LeaderstatsFolder = Instance.new("Folder")
	LeaderstatsFolder.Name = "leaderstats"
	
	local StageIntValue = Instance.new("IntValue")
	StageIntValue.Name = "Stage"
	StageIntValue.Value = 1
	StageIntValue.Parent = LeaderstatsFolder
	
	local RebirthIntValue = Instance.new("IntValue")
	RebirthIntValue.Name = "Rebirths"
	RebirthIntValue.Parent = LeaderstatsFolder
	
	LeaderstatsFolder.Parent = Player
	
	LoadData(Player)
end

Players.PlayerAdded:Connect(CreateLeaderstats)
Players.PlayerRemoving:Connect(SaveData)


Stage Handler

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

local ObbyStages = Workspace:WaitForChild("ObbyStages"):GetChildren()

for i, v in ipairs(ObbyStages) do
	v.Touched:Connect(function(Hit)
		local Humanoid = Hit.Parent.Humanoid
		local Player = Players:GetPlayerFromCharacter(Humanoid)
		
		local PlayerStage = Player.leaderstats.Stage
		
		if tonumber(v.Name) == PlayerStage.Value + 1 then
			PlayerStage.Value = PlayerStage.Value + 1
		elseif tonumber(v.Name) > PlayerStage.Value + 1 then
			Humanoid.Health = 0
		end
	end)
end

Rebirths Handler

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RebirthRemote = ReplicatedStorage:WaitForChild("Rebirth")
local MaxStages = 300

local function OnServerEvent(Player)
	local PlayerStage = Player.leaderstats.Stage
	local PlayerRebirths = Player.leaderstats.Rebirths
	
	if PlayerStage >= MaxStages then
		PlayerStage.Value = 1
		PlayerRebirths.Value = PlayerRebirths.Value + 1
	end
end

RebirthRemote.OnServerEvent:Connect(OnServerEvent)

I left out the Bind to Close part because you’d have to learn somehow. Please note that these are all individual scripts (not exactly the modular structure I was talking about but it would do), I also cleaned the code a bit and probably fixed some stuff, however most of it is a direct copy and paste.

I also added a few pcalls so you could implement your own retry system.

If there are any questions, please do not hesitate to ask.

Edit:
Also you didn’t provide any information on how you exactly place a player on their saved stage so I wouldn’t be able to help on that.

so i should use module script with all these handler?

These scripts aren’t setup to be used in module scripts. Use normal scripts.

the stage handler have something wrong
[ - ServerScriptService.ServerHandler.obby handler:11: attempt to index local ‘Player’ (a nil value)]

Woops, that’s supposed to be

local Player = Players:GetPlayerFromCharacter(Humanoid.Parent)

Edit:
I actually missed something in that script, change it to this

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

local ObbyStages = Workspace:WaitForChild("ObbyStages"):GetChildren()

for i, v in ipairs(ObbyStages) do
	v.Touched:Connect(function(Hit)
		if Hit.Parent:FindFirstChild("Humanoid") then
			local Humanoid = Hit.Parent.Humanoid
			local Player = Players:GetPlayerFromCharacter(Humanoid.Parent)
			
			local PlayerStage = Player.leaderstats.Stage
			
			if tonumber(v.Name) == PlayerStage.Value + 1 then
				PlayerStage.Value = PlayerStage.Value + 1
			elseif tonumber(v.Name) > PlayerStage.Value + 1 then
				Humanoid.Health = 0
			end
		end
	end)
end

Also, you could add a debounce to prevent spamming the hit event.

1 Like

when i touched the stage pad nothings happend
and no error

What do you mean nothing happens? I tested it myself and the code works fine. When I touch the next stage pad, my stage stats goes up.

oh ok now but everytime respawn i spawn at the spawn point

What do you use under ObbyStages? A part or a spawn point?

i use a part as a Obbystages and there is a spawn point at the lobby .everytime i respawn i spawn at the lobby

I made a lot of changes, so I’ll just give it to you via file.

Server Obby Handler.rbxm (3.0 KB)

Drag the file into your studio window with your place open. It will give you a folder with the scripts. Move the folder into Server Script Service.

If you have any questions, or if anything seems to not work, don’t hesitate to ask.

1 Like

You should explain why code doesn’t work and how you changed it rather than just giving code to copy-paste. If nobody explains why what he did is wrong, it’ll be much harder for him to find the mistakes he made and improve.

3 Likes

how am i suppose to know and understand if ur just saying

it worked and it must have a delay when teleport back to the stage?

the rebirths system kinda not working
[ServerScriptService.Server Handler.Rebirth Handler:10: attempt to compare number with userdata]

My Code vs Your Code - An Explanation


Data Handler

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

local SpawnHandler = require(script.Parent:WaitForChild("Spawn Handler"))

It’s best to define services and modules at the top of the script for organization

local SaveDataStore = DataStoreService:GetDataStore("SaveData")

Other important stuff is defined after that.

local function SaveData(Player)
	local PlayerData = {
		[1] = Player.leaderstats.Stage.Value,
		[2] = Player.leaderstats.Rebirths.Value
	}
	
	local Success, Result = pcall(function()
		return SaveDataStore:SetAsync(Player.UserId, PlayerData)
	end)
	
	if Success then
		print(Player.Name.."'s data saved.")
	else
		error(Result)
	end
end

Based on your first data script, you didn’t create an explicit data table, this may cause problems as your code is dependent on other sources. The pcall is used to run certain functions that yield or may pause the thread. This also gives you an opportunity to add a rety system if the data stores are down.

local function LoadData(Player)
	local Success, Result = pcall(function()
		return SaveDataStore:GetAsync(Player.UserId)
	end)
	
	if Success then
		if Result then
			Player.leaderstats.Stage.Value = Result[1]
			Player.leaderstats.Rebirths.Value = Result[2]
			
			if Player.Character then
				wait(1)
				SpawnHandler:RespawnPlayer(Player)
			else
				Player.CharacterAdded:Wait()
				wait(1)
				SpawnHandler:RespawnPlayer(Player)
			end
		else
			print(Player.Name.." has no data.")
		end
	else
		error(Result)
	end
end

The pcall logic is same as the the SaveData function above. Since we previously defined the PlayerData table, the player stats can be assigned without having a for loop. Then we check if the player’s character has spawned before teleporting them to their stage. The delay is neccessary because the character is still being assembled when the event (CharacterAdded) is fired.

local function CreateLeaderstats(Player)
	local LeaderstatsFolder = Instance.new("Folder")
	LeaderstatsFolder.Name = "leaderstats"
	
	local StageIntValue = Instance.new("IntValue")
	StageIntValue.Name = "Stage"
	StageIntValue.Value = 1
	StageIntValue.Parent = LeaderstatsFolder
	
	local RebirthIntValue = Instance.new("IntValue")
	RebirthIntValue.Name = "Rebirths"
	RebirthIntValue.Parent = LeaderstatsFolder
	
	LeaderstatsFolder.Parent = Player
	
	LoadData(Player)
end

This part is separated for readability.

Stage Handler

Explanation
for i, v in ipairs(ObbyStages) do
	v.Touched:Connect(function(Hit)
		if Hit.Parent:FindFirstChild("Humanoid") then --checks if the object that touched is a character
			local Humanoid = Hit.Parent.Humanoid
			local Player = Players:GetPlayerFromCharacter(Humanoid.Parent)
			
			local PlayerStage = Player.leaderstats.Stage
			
			if tonumber(v.Name) == PlayerStage.Value + 1 then
				PlayerStage.Value = PlayerStage.Value + 1
			elseif tonumber(v.Name) > PlayerStage.Value + 1 then
				Humanoid.Health = 0
			end
		end
	end)
end

We first run a for loop that will iterate through all the ObbyStage parts and connect a touched event to the function that will check if the player skipped a stage.

Spawn Handler

Explanation
local SpawnHandler = {}

function SpawnHandler:RespawnPlayer(Player)
	local TargetStage = Player.leaderstats.Stage.Value
	Player.Character:MoveTo(ObbyStages[TargetStage].Position)
end

return SpawnHandler

This is a module script that is used when a player will be respawned on their stage (since you used parts instead of spawn points). It is important that you add a wait before calling the Respawn function otherwise the character will not teleport to their stage. Since this is a module script under server script service, this can be accessed by any server sided script.

Death Handler

Explanation
local SpawnHandler = require(script.Parent:WaitForChild("Spawn Handler"))

local Connections = {}

function OnDied(Player)
	print(Player.Name.." died")
	Connections[Player.Name]:Disconnect()
	local Character = Player.CharacterAdded:Wait()
	if Character then
		print("Respawning")
		wait(1)
		SpawnHandler:RespawnPlayer(Player)
		print("Respawned")
		Connections[Player.Name] = Character.Humanoid.Died:Connect(function()
			OnDied(Player)
		end)
	end
end

local function OnPlayerAdded(Player)
	local Character = Player.Character or Player.CharacterAdded:Wait()
	if Character then
		Connections[Player.Name] = Character.Humanoid.Died:Connect(function()
			OnDied(Player)
		end)
	end
	print("Created new connection")
end

local function OnPlayerRemoving(Player)
	if Connections[Player.Name] then
		Connections[Player.Name] = nil
	end
	print("Removing connection")
end

This is probably my greatest mess today. This will create a connection for each player that joined. It will place all their connections in a table for organization. When a player dies, a new character is created, so we have to disconnect the old connection and reconnect it. There’s probably a better way to do this by the looks of it snkjdhssdka.

Rebirth Handler

The rebirth handler is pretty much your code, I just cleaned it up I guess.

3 Likes

Change that line to this

if PlayerStage.Value >= MaxStages then
1 Like

Dude thank you very very much for helping me and waste you lots of time . Im glad that you helped me
and your scripting skill rlly advance .<3

1 Like

WOW thx for explaining too <3 :smiley: :smiley: :smiley: