Tool's new parent is delayed when using `humanoid:EquipTool()` (potential studio bug)

Code

bluesword.Parent = player.Backpack
hum:EquipTool(bluesword)
print("backpack", bluesword:GetFullName())

bluesword.Parent = workspace
hum:EquipTool(bluesword)
print("workspace", bluesword:GetFullName())

bluesword.Parent = player.Backpack
hum:EquipTool(bluesword)
print("backpack", bluesword:GetFullName())

bluesword.Parent = workspace
hum:EquipTool(bluesword)
print("workspace", bluesword:GetFullName())

bluesword.Parent = game.ReplicatedStorage
hum:EquipTool(bluesword)
print("repstorage", bluesword:GetFullName())

bluesword.Parent = game.ServerStorage
hum:EquipTool(bluesword)
print("serverstorage", bluesword:GetFullName())

bluesword.Parent = game.StarterGui
hum:EquipTool(bluesword)
print("startergui", bluesword:GetFullName())

bluesword.Parent = game.ReplicatedFirst
hum:EquipTool(bluesword)
print("repfirst", bluesword:GetFullName())

Output

  backpack Players.wrello.Backpack.bluesword
  workspace Workspace.wrello.bluesword -- Successfully reparented
  backpack Players.wrello.Backpack.bluesword
  workspace Workspace.wrello.bluesword -- Successfully reparented
  repstorage ReplicatedStorage.bluesword
  serverstorage ServerStorage.bluesword
  startergui StarterGui.bluesword
  repfirst ReplicatedFirst.bluesword

Why doesn’t the tool get parented to the character immediately with hum:EquipTool() when the tool doesn’t start in workspace?

How long is it taking? I don’t know what context you are testing this in but is it possible the scripts haven’t loaded?

equipped from backpack
0.019914799951948225 seconds to equip

equipped from workspace
0.000007999944500625134 seconds to equip

equipped from backpack
0.016071600024588406 seconds to equip

equipped from workspace
0.000008800067007541656 seconds to equip
tool_parent_bug.rbxl (56.2 KB)

Ah I see what you mean. It’s a large difference on that scale but it’s likely Roblox is just doing some background work, and in the grand scheme of things it shouldn’t affect much, no?

It is highly likely it is just Roblox based. They are probably doing some background work while function triggers.

It does because of this situation:

function doSomethingIfEquipped()
 if char:FindFirstChild("Tool") then
  print("Doing something!")
 end
end

hum:EquipTool(tool)
doSomethingIfEquipped() -- I expected this to work

What’s strange is that this does not have the same effect:

Code:

task.wait(3) -- Wait for player to load in

local tool = Instance.new("Tool")
local player = game.Players:GetPlayers()[1]
local hum = player.Character.Humanoid

local function customEquipTool(tool)
	hum:UnequipTools() -- Roblox API says that this is what normally happens first when you do `hum:EquipTool()`
	tool.Parent = player.Character
end

tool.Parent = player.Backpack
customEquipTool(tool)
print("from backpack")
local s = os.clock()
player.Character:WaitForChild(tool.Name)
print(os.clock()-s, "seconds to equip")
print()

tool.Parent = workspace
customEquipTool(tool)
print("from workspace")
local s = os.clock()
player.Character:WaitForChild(tool.Name)
print(os.clock()-s, "seconds to equip")
print()

tool.Parent = player.Backpack
customEquipTool(tool)
print("from backpack")
local s = os.clock()
player.Character:WaitForChild(tool.Name)
print(os.clock()-s, "seconds to equip")
print()

tool.Parent = workspace
customEquipTool(tool)
print("from workspace")
local s = os.clock()
player.Character:WaitForChild(tool.Name)
print(os.clock()-s, "seconds to equip")

Output:

  from backpack
  0.000008999952115118504 seconds to equip
  
  from workspace
  0.000004299916326999664 seconds to equip
  
  from backpack
  0.000005299923941493034 seconds to equip
  
  from workspace
  0.000002900022082030773 seconds to equip

So that’s what makes me think something funky is going on.

The reason this happens is because of how EquipTool calls are handled on the server when a Player and Backpack are involved.

If the Tool is in the Player’s Backpack, calling EquipTool from the server sends a message to the client to equip the Tool, to sort of mock the equip action being user-initiated.

I don’t remember what the reasoning was for needing to use EquipTool in place of just parenting the Tool directly to the character, but I assume there was some sort of buggy networking behavior at the time, and making the client take the action was the most stable way to work around this.

TL;DR - This is weird legacy shenanigans. You should avoid writing code that directly traverses through the children of an unknown object at runtime anyway. Doing so is unsafe and can lead to errors like this.

2 Likes

I believe if you call from the server it will have a bit of a ping (since servers are based); so opt for client based function calling.

And yes, Max replied before me :confused:

Couldn’t you just use :WaitForChild() with a short timeout instead?
Not perfect but better than nothing. That or yield until EquipTool is guaranteed to have run, somehow (If it doesn’t yield by default.)

The reason I need it is because my library attaches a motor6d between the tool & rig after equipped, and I expected that I could equip the tool and then run this function afterwards.

The fact that it works from workspace is even worse. It’s inconsistent. I was happy until I discovered that it didn’t work only when the tool got equipped after being unequipped (with the tool starting in workspace).

Is it reasonable to expect that hum:EquipTool() would parent the tool to the character synchronously? If so, I think this is bug-like behavior that needs to be addressed.

1 Like

Not necessarily an unreasonable assumption, but I concur there’s a better/safer approach. You should be passing a strong reference to both the Tool you’re going to be equipping, and the character it’ll be equipped onto, directly to whatever function is creating the Motor6D joint. That way it doesn’t matter what the Tool’s parent is in that immediate moment.

1 Like

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