Why does this not kill the player?

ok man just do this then

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		repeat
            character:WaitForChild("Humanoid").Health -= 50
        until character:WaitForChild("Humanoid").Health < 0
	end)
end)
game.Players.PlayerAdded:Connect(function(player)
player.CharacterAppearanceLoaded:Connect(function(character)
	    character:BreakJoints()
    end)
end)

What are you putting this in? A Local script or server script?

You made me check for the parent, and the parent seems to be “nil”, but I used this to check when the parent is changed to “Workspace” and then I set the Health to 0, and it STILL didn’t work:

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		if humanoid then
			humanoid.Parent.AncestryChanged:Connect(function(_, parent)
				print(parent)
				if parent == game.Workspace then
					humanoid.Health = 0
				end
			end)
		end
	end)
end)

Parent prints “Workspace” but it still does nothing. I also tried :BreakJoints() but it did nothing.

You may be right but would you be willing to explain why I need to wait a frame for this to happen? I’d rather not use any waits.

I’m no professional but I would assume that’s engine limitations or just hard coded in order to prevent something from happening, same thing with trying to use :Destroy() on the same frame an instance is created

2 Likes

This makes a lot of sense actually if I think about it now. Thank you for explaining this. Since the health is 0 when instanced, it does not activate the listeners until the next frame.
I will mark that as the solution.

1 Like

However, this raises the question of why this signal was able to detect the Workspace parenting change, but did not manage to kill the player? I guess I need to study frames more.

Edit:
Seems like Heartbeat wait will run after physics simulation. Maybe some internal signals run faster than that?

1 Like

I tried

instance = Instance.new("Part")
instance.Parent = workspace

instance:Destroy()

and the part did get destroyed

It may be correct that it is hard coded, but only for players.

3 Likes

Yes, as we tried this, you are able to destroy parts within the character before physics simulation without any consequences.

The solution is correct. The part gets instanced, but using heartbeat to wait for the physics simulation would be appropriate in this case.

So to my understanding, this is the order of what is happening:

  1. Character model gets instanced.
  2. Character parent is nil until all parts in the character have loaded in (not just Humanoid)
  3. Character gets put into Workspace. (AncestryChanged signal can detect this)
  4. Physics simulation (frame) has not ran yet since all of this happened on the same frame.
  5. Only after this physics simulation would it be possible to kill the player.

I don’t understand why

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		local instance = Instance.new("Part")
		instance.Parent = workspace
		instance.Size = Vector3.new(10,5,5)
		character:WaitForChild("Head").Size = Vector3.new(10,2,2)
	end)
end)

Changes the size of the part, but not the head, unless there is a wait.

Edit: It could be that roblox waits for some time on purpose after firing CharacterAppearanceLoaded before making the character properties possible to change.

Edit2: Never mind, I can change humanoid.WalkSpeed without wait

1 Like

Did some research into this and I think I know the reason?
Put this script within the function:

for i, v in ipairs(character:GetDescendants()) do
    print(i, v)
end

This prints a huge list of objects including some scripts that have the name “OriginalSize” under each part object. Notice that?

I think if you change the size before the scripts have been ran in Workspace, it will change back to the original size. I wonder what happens if you remove the “OriginalSize” script for the head, then change the Size and then wait for the physics simulation.

Roblox seems to get rid of those scripts after they are ran.

Try that.

EDIT:

I doubt it will work due to security reasons.

1 Like

Another Method would be:

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local Humanoid = character.Humanoid
		repeat wait() Humanoid.Health = 0
		until Humanoid.Health == 0
	end)
end)

Weird, last time I tried that it would output a warning message saying " Something unexpectedly tried to set the parent of X to NULL while trying to set the parent of X ", but by seeing this topic I assume this has been fixed a long time ago and I haven’t noticed this change.

Usually when the character is added, it doesn’t guarantee that the character has been parented to the workspace yet. You need to use the AncestryChanged event of the character model in order to wait for it to be parented.

Secondly, I believe wait is necessary as the Humanoid has not yet initialised properly. Setting the Humanoid state to “Dead” seems to work at this point, but it does not break joints (probably because the engine has not yet finished making the joints) so the player is still able to walk around until they respawn (although you could reduce the wait time between respawning by setting Players.RespawnTime to 0).

Here’s the code I tested with. While this does kill the player, their joints do not break. The workaround I am using is to destroy their “RootPart,” which is what the client relies on in order to move the character around. Instead, they’ll just end up planking:

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player: Player)
	player.CharacterAdded:Connect(function(character)
		while not character.Parent do
			character.AncestryChanged:Wait()
		end
		
		local humanoid = character:WaitForChild("Humanoid")
		humanoid:ChangeState(Enum.HumanoidStateType.Dead)
		
		if humanoid.RootPart then
			humanoid.RootPart:Destroy()
		end
	end)
end)

Unfortunately none of that will work.

It is funny that you mention all of these points because I tried these earlier and was just about to write my analysis over what I found out.

You’ll be surprised of what I found.

First of all:
I tried waiting for the AncestyChanged signal and you’ll be surprised to know that even after the parent is set to “Workspace” from “nil”, you will still not be able to kill the player.

Second of all, I have tried to set the state of the Humanoid to Dead, but you’re missing something very important here. Setting the state of a Humanoid is not possible from the server side unless you set the network ownership of the HumanoidRootPart to “nil”.

This has a delay and therefore, the setting the state will not work. I have tried this:

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
		
		if humanoid then
			print(humanoid.Parent.Parent) -- nil, not yet in Workspace.
			humanoid.Parent.AncestryChanged:Connect(function(_, parent)
				print(parent) -- Before Physics Simulation, parent is now Workspace.
				if parent == game.Workspace then
					humanoidRootPart:SetNetworkOwner(nil) -- Gives server network ownership
					humanoid:ChangeState(Enum.HumanoidStateType.Dead)
					print(humanoid:GetState())
				end
			end)
		end
	end)
end)

Third of all. It seems to be literally impossible to kill the character before the first frame, before physics simulation has taken place.

I wish there was a way to look into the way Roblox handles checking for when the players health reaches down to 0 and kills the player. What is the actual script that handles that? You can set the Humanoid health to 0 after whatever internal Roblox event detects the value change, and I wonder if Roblox will ever change that to work instantly.

Edit: I mean yeah you said you’re not really killing the player but that is what I am hoping will happen.

Seems to work fine?

I think Roblox are slowly porting things over to Lua, so it’s hopefully only a matter of time before the Humanoid is more accessible.

1 Like

My bad, and thank you for making me realize something. You seem to be right in being able to change the state of the humanoid server side and I actually got the exact same result in the video earlier but when printing the GetState of the Humanoid it seemed to give the wrong state and thus didn’t fully understand if I was just doing something wrong since the BreakJoints and Death Gui flash was not occurring.

I wrote this and this seems to answer my question and replicate the death perfectly:

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		humanoid.Died:Connect(function()
			humanoid.Parent:BreakJoints()
		end)
		
		humanoid.Parent.AncestryChanged:Connect(function(_, parent)
			if parent == game.Workspace then
				humanoid:ChangeState(Enum.HumanoidStateType.Dead)
			end
		end)
	end)
end)

Well, if that is the case, then maybe the script that “kills” the player when they have 0 health runs after the script that changes humanoids health. It may be that the script didn’t notice that the health is 0 even after some time passes and it runs, because it only checks when the value changes.

This, of course, has some assumptions, but we will never know if that’s true unless we will be able to look inside the roblox humanoid script.

I don’t think it has something to do with physics simulation, because you are able to write a script like that yourself.

Edit: I found out that if you set humanoid.Health to 0 without wait, and then wait and set humanoid.Health to 0 again, nothing happens, so if you set it without wait the first time, you won’t be able to kill the player in that script, even with wait.

I also found out that if you have

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		if humanoid then
			print("HUMANOID LOADED")
			humanoid.Health = 0
			wait(1)
			humanoid.Health = 0
			wait(1)
			humanoid.Health = 0
			wait(1)
			humanoid.Health = 0
		end
	end)
end)

in one script, and

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		if humanoid then
			print("HUMANOID LOADED")
			wait(10)
			humanoid.Health = 0
		end
	end)
end)

in another, then the player will sometimes randomly spawnkill, and at other times, it won’t, and will only die after 10 seconds.

Edit2: Turns out you don’t actually need the second script for that to happen, the first script is enough to make the player randomly die/not die.

Edit3: You can change the first script to

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(character)
		local humanoid = character:WaitForChild("Humanoid")
		if humanoid then
			print("HUMANOID LOADED")
			humanoid.Health = 0
			while wait(1) do
				humanoid.Health = 0
			end
		end
	end)
end)

I would guess that happens because setting humanoid.Health to 0 before the script that checks if humanoid.Health is 0, runs, somehow causes an error in that checking script sometimes.

However, there was nothing in the output during the times when the player didn’t die.

1 Like