Issues with Handling Player Character Updates and Team Changes

I’m working on a script for my Roblox game that handles player character updates when they switch teams. I’ve encountered some issues with re-entrancy and event handling that I could use help troubleshooting.

Here’s a summary of what I’m trying to achieve:

  • When a player joins a team or their character is updated, I want to replace their character model with a team-specific one.
  • The character should be cloned from a model stored in ReplicatedStorage based on the player’s team.
  • The script should preserve certain properties of the old character and ensure smooth transitions.

Here’s the code I’m using:

local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")

-- Store character models for each team
local TeamCharacters = {
    Team1 = RS:WaitForChild("Team1"),
    Team2 = RS:WaitForChild("Team2")
}

-- Function to handle team change
local function onTeamChange(player)
    -- Wait for the player's character to load
    if not player.Character or not player.Character:IsDescendantOf(game.Workspace) then
        player.CharacterAdded:Wait()
    end

    local oldCharacter = player.Character
    local oldBodyColors = oldCharacter:FindFirstChildOfClass("BodyColors")

    -- Get the appropriate character model based on the team
    local teamName = player.Team.Name
    local characterModel = TeamCharacters[teamName]

    -- Clone the character model
    local newCharacter = characterModel:Clone()
    newCharacter.Parent = game.Workspace

    -- Update body colors
    if oldBodyColors then
        oldBodyColors:Clone().Parent = newCharacter
    end

    -- Set the new character
    newCharacter.HumanoidRootPart.CFrame = player.Character.HumanoidRootPart.CFrame
    newCharacter.HumanoidRootPart.Anchored = false
    for _, child in ipairs(oldCharacter:GetChildren()) do
        if child:IsA("LocalScript") or child:IsA("Script") then
            if child.Name ~= "Animate" then
                child:Clone().Parent = newCharacter
            end
        end
    end

    newCharacter.Name = oldCharacter.Name
    player.Character = newCharacter

    -- Destroy the old character
    oldCharacter:Destroy()
end

-- Function to handle player added
local function onPlayerAdded(player)
    player:GetPropertyChangedSignal("Team"):Connect(function()
        onTeamChange(player)
    end)

    player.CharacterAdded:Connect(function(char)
        if not RS.OldCharacters:FindFirstChild(char.Name) then
            char.Archivable = true
            local clone = char:Clone()
            clone.Parent = RS.OldCharacters
        elseif player.Team == game:GetService("Teams"):FindFirstChild("Team1") or player.Team == game:GetService("Teams"):FindFirstChild("Team2") then
            onTeamChange(player)
        end
    end)
end

Players.PlayerAdded:Connect(onPlayerAdded)

-- Handle existing players
for _, player in ipairs(Players:GetPlayers()) do
    onPlayerAdded(player)
end

The Problem: Whenever a player joins a team or their character is updated, I get errors related to event re-entrancy, such as:

  • “Maximum event re-entrancy depth (80) exceeded for Instance.ChildRemoved”
  • Similar issues with Instance.ChildAdded, Instance.Changed, and Player.CharacterAdded

It works perfectly if i remove/comment out this snippet:

elseif player.Team == game:GetService("Teams"):FindFirstChild("Gnomes") or player.Team == game:GetService("Teams"):FindFirstChild("Knights") then
		onTeamChange(player)
end

But when this snippet is active, it causes all of the errors, and no animations to load.

1 Like

You’re overwriting the player’s character on team change, which is triggering player.CharacterAdded which then calls onTeamChange() again. You should be checking if the new character was just replaced, and not calling the function again if so.

1 Like

Thank you! This is the code that worked!:

-- Handle existing players
for _, player in ipairs(Players:GetPlayers()) do
	onPlayerAdded(player)
end


local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")

-- Store character models for each team
local TeamCharacters = {
	Team1 = RS:WaitForChild("Team1"),
	Team2 = RS:WaitForChild("Team2")
}

-- Table to store debouncing state for each player
local debounce = {}

-- Function to handle team change
local function onTeamChange(player)
	-- Check if the debounce is active for the player
	if debounce[player] then return end
	debounce[player] = true

	-- Wait for the player's character to load
	if not player.Character or not player.Character:IsDescendantOf(game.Workspace) then
		player.CharacterAdded:Wait()
	end

	local oldCharacter = player.Character
	local oldBodyColors = oldCharacter:FindFirstChildOfClass("BodyColors")

	-- Get the appropriate character model based on the team
	local teamName = player.Team.Name
	local characterModel = TeamCharacters[teamName]

	-- Clone the character model
	local newCharacter = characterModel:Clone()
	newCharacter.Parent = game.Workspace

	-- Update body colors
	if oldBodyColors then
		oldBodyColors:Clone().Parent = newCharacter
	end

	-- Set the new character
	newCharacter.HumanoidRootPart.CFrame = player.Character.HumanoidRootPart.CFrame
	newCharacter.HumanoidRootPart.Anchored = false
	for _, child in ipairs(oldCharacter:GetChildren()) do
		if child:IsA("LocalScript") or child:IsA("Script") then
			if child.Name ~= "Animate" then
				child:Clone().Parent = newCharacter
			end
		end
	end

	newCharacter.Name = oldCharacter.Name
	player.Character = newCharacter

	-- Destroy the old character
	oldCharacter:Destroy()

	-- Reset the debounce after a short delay
	task.delay(1, function()
		debounce[player] = false
	end)
end

-- Function to handle player added
local function onPlayerAdded(player)
	player:GetPropertyChangedSignal("Team"):Connect(function()
		onTeamChange(player)
	end)

	player.CharacterAdded:Connect(function(char)
		if not RS.OldCharacters:FindFirstChild(char.Name) then
			char.Archivable = true
			local clone = char:Clone()
			clone.Parent = RS.OldCharacters
		elseif player.Team == game:GetService("Teams"):FindFirstChild("Team1") or player.Team == game:GetService("Teams"):FindFirstChild("Team2") then
			onTeamChange(player)
		end
	end)
end

Players.PlayerAdded:Connect(onPlayerAdded)

-- Handle existing players
for _, player in ipairs(Players:GetPlayers()) do
	onPlayerAdded(player)
end
2 Likes

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