A few things I want to clear up in this conversation:
Don’t assume HumanoidRootPart will always exist.
Technically, CharacterAdded will fire and guarantees all the Humanoid and it’s limbs will exist, but when referencing a character, there is a possibility the HumanoidRootPart will be gone (like falling off the map).
If the HumanoidRootPart is constantly not being found, answer these questions:
Is your character reference always up-to-date? - Player.Character is a property with a value of the current existing character. If the player respawns, the old character is no longer referenced.
Is Players.CharacterAutoLoads set to true and you are spawned in when the code runs? - If this is set to false, the player will not respawn, therefore not updating the Character property.
Is your HumanoidRootPart being renamed somehow? - Check the explorer window to see if HumanoidRootPart actually exists.
Are you using a custom character model? - You may not have a part called HumanoidRootPart.
You can technically have a top-level characters variable, but you would have to constantly update that table whenever the a player respawns. If you want to guarantee characters with HumanoidRootParts, consider this pattern instead:
local function getCharacters(withHumanoidRootPart: boolean?): {Model}
local characters: {Model} = {}
for _: number, player: Player in Players:GetPlayers() do
if player.Character ~= nil then
if withHumanoidRootPart == true and player.Character:FindFirstChild("HumanoidRootPart") == nil then
continue
end
table.insert(characters, player.Character)
end
end
return characters
end
-- teleport all characters with HumanoidRootParts
for _: number, character: Model in getCharacters(true) do
character.HumanoidRootPart.CFrame = CFrame.new(100, 100, 100)
end
It will work on server-side too, the server-script will be parented to each character. So the parent of that server-script will be the parent of that character. It’s basically local (sort of)
Within your if statement here is what you should do in different scenarios:
While loop:
while rootpart do
task.wait(5)
print(rootpart.Name)
end
It will only run the code while the root part exists.
If statement with delay:
local rootpart = character:WaitForChild(“HumanoidRootPart”, 10)
if not rootpart then return end
task.wait(0.5)
if not rootpart then return end
--do as you wish with the root part
All you need to keep in mind is that whenever you wanna do something with the root part it may or may not exist. On the line before you manipulate it, check if it exists. If not, exit the function if the rest of the function is related only to the root part.
Exit a function using return, or a while or for loop using break. If the for loop is iterating instances of which you must check more, use continue.
What if the function contains other important things that are unrelated to the root part? Then simply wrap all code relating to the root part in an if statement which checks for the root part’s existence, or start an entirely new local function dealing with the root part with our previously mentioned error-prevention methods within.
Thank you, thank seems to work! Since the loop will stop for 10 seconds if they cannot find the player, I am worried this could be exploited. Do you know if running each char in the pairs(characters) loop in parallel would prevent this issue, and if so, how?
I’m not sure I know exactly what you’re talking about, mostly because I’m not sure in what way the script will be trying to access the root part. You can adjust the 10 to a shorter time such as 1, or perhaps 2, if your worried about the code yielding for too long.
If even 1 second is too long, then yes, running the loops in parallel would be much better since one yield wouldn’t stop the entire for loop. I need to understand better in what way you’re accessing the root part, can you post that section of the code?
local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player)
player.CharacterRemoving:Connect(function()
local rootpart = player.Character:FindFirstChild("HumanoidRootPart")
if not rootpart then print("too late to check position") return end
local position = rootpart.CFrame
-- or rootpart.Position whichever you prefer
end)
end)
This completely new approach below means that your code will log the rootpart’s position every 2 seconds. Even if the rootpart is destroyed, its last position is stored in the player(not character since the character is constantly changing)
local rootpartcheck = coroutine.create(function()
while true do
task.wait(2)
for i, player in players:GetPlayers() do
local rootpos = player:FindFirstChild("rootpartpos")
if not rootpos then continue end
local character = player.Character
if not character then continue end
local rootpart = character:FindFirstChild("HumanoidRootPart")
if not rootpart then continue end
rootpos.Value = rootpart.CFrame
end
end
end)
players.PlayerAdded:Connect(function(player)
local rootpartpos = Instance.new("CFrameValue")
rootpartpos.Name = "rootpartpos"
rootpartpos.Parent = player
end)
If I’ve understood correctly your initial game design, you want to save the player’s last position every 2 seconds and when the player respawns, they teleport there.
I’m creating a chunk generation algorithm and therefore need the player’s position, but the problem is the script stops. My script loops through all characters whenever it cannot find the specified character’s humanoidrootpart because their character is destroyed the script halts since it uses waitforchild and therefore can be exploited (for example, the exploiter could be destroying and undestroying the humanoidrootpart so the script would never carry on)
I need a loop that every 2 seconds checks every player’s position and does not effect other people’s loop/cause errors/warns if that humanoidrootpart doesn’t exist that is server-side.
local function checkForHRPs()
for _, player in game.Players:GetPlayers() do
task.spawn(function()
if not player.Character then warn(string.format("%s does not have a character! Skipping loop for %s...", player.Name)) continue end
if not player.Character:FindFirstChild("HumanoidRootPart") then warn(string.format("%s does not have a HumanoidRootPart though has a character!", player.Name)) continue end
local HRP = player.Character.HumanoidRootPart
print(string.format("%s is healthy!\nX: %d\nY: %d\nZ: %d", player.Name, HRP.CFrame.X, HRP.CFrame.Y, HRP.CFrame.Z))
end)
end
end
while task.wait(2) do
checkForHRPs()
end
Ah, thank you!
For some reason the chunkFlush doesn’t seem to be working, can you spot an issue?:
while task.wait(chunkUpdateTime) do
for charNum, char in pairs(characters) do
coroutine.resume(chunkFlush, char)
end
end
local chunkFlush = coroutine.create(function(char)
local success, hrootPos = pcall(function()
return char:WaitForChild("HumanoidRootPart", 2).Position
end)
if success then
-- do thing
end
end)
local players = game:GetService("Players")
local rootpartcheck = coroutine.create(function()
while true do
task.wait(2)
for i, player in players:GetPlayers() do
local rootpos = player:FindFirstChild("rootpartpos")
if not rootpos then continue end
local character = player.Character
if not character then continue end
local rootpart = character:FindFirstChild("HumanoidRootPart")
if not rootpart then continue end
rootpos.Value = rootpart.CFrame
end
end
end)
coroutine.resume(rootpartcheck)
players.PlayerAdded:Connect(function(player)
local rootpartpos = Instance.new("CFrameValue")
rootpartpos.Name = "rootpartpos"
rootpartpos.Parent = player
end)
local function chunkFlush(player)
local rootpos = player:FindFirstChild("rootpartpos")
if rootpos then
local position = rootpos.Value
-- do as you wish with it's position
else
print("fail")
end
end
while task.wait(chunkUpdateTime) do
for charNum, char in pairs(characters) do
local player = players:GetPlayerFromCharacter(char)
chunkFlush(player)
end
end
Also keep in mind I’ve chosen to use CFrame, really the variable should be rootpartcframe not rootpartpos(a misnomer on my part). If you need it’s position you would just do rootpartcframe.Position