Workspace.RejectCharacterDeletions might be bypassed

Today, a patch update for my game released, with loads of bug fixes and the adjustment to the new Workspace.RejectCharacterDeletions with the removal of countless sanity checks. Well, the release didn’t go as planned, as the game loop script broke down due to someone not having a Humanoid, even instantly after a sanity check for their Character, which is quite unusual. The exploding system in my game also broke down once because someone didn’t have a HumanoidRootPart, which is again, quite unusual.

I need to know what’s going on, this can ruin my game.

Without a lot of context it is hard to really guess at what is going on, but it is unlikely that RejectcharacterDeletions is being bypassed. You’re probably just running into edge cases with how the engine creates characters, and how the engine can remove parts.

This can happen if a player glitches out of a map, falls, etc to the FallenPartsDestroyHeight. It can also happen sometimes under certain physics situations iirc, I think if stuff gets NaN velocities or something along those lines it can get auto deleted by the engine. (I don’t know what conditions that happens in or why but I’ve encountered it rarely a couple times in the past. It probably has to do with how FallenPartsDestroyHeight is implemented, if I had to make a guess)

It is possible (and potentially relatively likely depending on the specific code that is breaking) that your code simply ran before the player’s Humanoid was created. CharacterAdded will guarantee it, but player.Character is set before all the children are necessarily added to the player’s character, including the Humanoid.


I am not saying this is the correct way to do this in all cases or anything, but, there are a couple things that I personally do to make checks easier and more adaptable.

In general I personally use FindFirstChild/FindFirstChildWhichIsA where I can, since there are often a lot of cases where you can’t reliably know if descendants will exist, so it makes it less to think about and potentially change in the future, it just always works in all cases no matter what.

-- By chaining conditions like this you can grab a bunch of stuff at once without much effort and then check them later
local character = player.Character
local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
local rootPart = humanoid and humanoid.RootPart

if not humanoid then return end
-- By the way, this pattern of "if thing then return" or "if thing then continue" is called a guard clause, you're just cancelling the code when a certain condition is/isn't met.
-- It prevents you from having really tall nested if statements, which usually makes it easier to work with the code

You can also put your code into small helper functions e.g. like this to reduce how much you have to repeat this code. And then it becomes easier to copy and paste it across files you need it in as well.

local function getRootPart(player: Player)
	local character = player.Character
	local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
	local rootPart = humanoid and humanoid.RootPart

	return rootPart
end

-- Somewhere else
local rootPart = getRootPart(player)
if not rootPart then return end

Doing some of that in both server-side and client-side code for parts is also probably a good idea, so that code you write is always streaming compatible, in case you want to share it between both the client and server.

1 Like

Thanks, I’ll fix my code up right now.

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