New Player and Character Destroy Behavior

None of your examples throw any errors with the new behavior enabled.

Your BadgeService call is already broken today, badges cannot be awarded to players that are not in the game.

Please, we ask you to test your code and experiences with new behavior enabled and report real issues, no need to speculate.

2 Likes

Hi, thanks for sharing these code snippets. First though, I want to clear up a few things about what ‘Destroying’ means and does not mean.

Destroying an Instance, exclusively does the following:

  • Sets Instance Parent to nil
  • Disconnects all Connections to the Instance’s Events
  • Recursively destroys all children

Destroying an instance does not do the following:

  • Make the instance ‘vanish’ into the garbage collector while it still has active references
  • Throws an error when accessing a reference to the Instance
  • Remove Instance properties or make Instance properties throw an error when accessed

Looking at your code samples:

someTable[plr] = true -- throws error

This would not error, the player Instance reference is still active. someTable[plr] would be nil because you set it to nil in the PlayerRemoving callback.

local character = plr.Character -- throws error

This would not throw an error, it would return a reference to the player’s last Character model (which has been destroyed).

RemoteEvent:FireClient(plr, "DataLoaded") -- throws error

This would error, but this is expected behavior and not changed by the new player / character destroy behavior. The player is no longer in the experience so it is conceptually impossible to send a networked message to their client.

BadgeService:AwardBadge(plr.UserId, badgeId) -- throws error

Once again, this would error, but also is unrelated to the new behavior. You cannot award a badge to a player that is not in rthe experience. Accessing the UserId itself would not error (it would give you the user Id)

someTable[plr] = nil -- throws (but used to be necessary)

This would not error as the player Instance reference is still valid.

5 Likes

Thank you for for the kind response. I apologize for not testing the code before posting here, as it is clear now that I was incorrect, and especially because half of my “examples” would error for a perfectly logical and completely unrelated reason :man_facepalming:. Thank you as well for clarifying the ‘Destroying’ behavior, as I had previously misunderstood this to force garbage collection of an instance, which was the root cause of all my misconceptions. I feel blind for having not realized this before…

7 Likes

This is quite an interesting thing to see! Makes me wonder what else in my experiences may be causing potential memory leaks… :thinking:

I just recoded my ragdoll body system from using the same thing, to recloning to make it compatible with this change. I don’t mind, but it would probably be better to have a way to keep the model from being destroyed. And keep the model from parenting to nil when removed from the player as it’s character as well.

1 Like

how do i enable this

30CHARBYPASS

In the Properties of the Workspace.

Enabling this solved a huge memory leak we had on the server relating to animations and signals. :+1:

4 Likes

I imagine there are reasons as to why this Destroy call doesn’t replicate to clients but is inconsistent with the rest of Destroy calls on any other Instance. I expect that when an Instance is destroyed on the server this Destroy call replicates to client.


I have a leaderboard, once a player gets parented to nil the leaderboard slot gets destroyed and I still need to disconnect everything manually client side where leaks could also be the case if not handled well.

If Destroying were to be called I wouldn’t need to disconnect Attribute/Property events or any other connections made on its children.

How about making a property that specifies a delay before destroying the character after it’s removed and make it inf for existing experiences and 0 for newly created ones

1 Like

I’m seeing some very odd behavior relating to memory usage.

To my understanding, this setting is supposed to destroy characters when they’re removed. It also looks like this is supposed to be enabled in live servers.
Given both of these understandings, I should be able to expect identical behavior between destroying characters myself and letting Roblox do it with the new setting; this is not the case from my testing.


In our roleplay game, we parent a custom GUI to the player’s head for showing stats. In diagnosing a memory leak, we noticed that this GUI is not being cleaned up properly; over time, as people respawn, Gui memory usage will rise indefinitely. This holds true whether PlayerCharacterDestroyBehavior is enabled or not, and does not happen if we do not clone the GUI into the player’s head.

If I destroy the character myself on CharacterRemoving, it works fine and Gui never rises out of control.

The ONLY change in the game between these two videos is this snippet of code:

Player.CharacterRemoving:Connect(function(char)
	char:Destroy()
end)

PlayerCharacterDestroyBehavior was set to Enabled in both videos.


It seems like either PlayerCharacterDestroyBehavior is not working how we’d expect it to or it’s not enabled in live servers. I tried to isolate this in Studio but couldn’t get anything consistent. @WheretIB

4 Likes

hi, a possibly important feedback - even after setting PlayerCharacterDestroyBehavior to true, resetting a character still leaks memory on server side on an empty base plate.
The leaks occur on these areas:

  • animation
  • physicspart
  • instances

My biggest concern is with the animation category - a character’s animator/humanoid can load a bunch of animations with high memory load, and then die a bunch of times. Multiply those two together and you can see how fast the memory leak adds up.

This bug has been reported here months ago but hadn’t been addressed, but I thought you might want to know about it as it is highly relevant to this new PlayerCharacterDestroyBehavior feature.

1 Like

Automatic character destroy is performed only for the server.
This is because many servers run for days and see many more players and characters than a client will experience over the time of a shorter session.

Having said that, extending auto-call to characters on clients might be a good idea.

4 Likes

Yeah, I see absolutely no downsides to it. Let us know what your team decides.
For our game in particular, some people tend to stay in-game for upwards of 6 hours roleplaying. For these dedicated people, their game crashing mid-RP because of a mem leak is less than ideal.

The expected behavior at least to me was that it’d act the same as Destroy (which does propagate to clients)

2 Likes

This is a much-needed change, but it does break some behavior. I can think of several games that use corpses that despawn much later than the player respawns, and I believe the most seamless way to achieve the effect is to use method in the following pseudocode:

local Debris = game:GetService("Debris")
local Players = game:GetService("Players")

local player = players.Andy_Wirus --Server-sided
local character = player.Character

player.Character = nil
character.Parent = workspace
Debris:AddItem(character, 300)

task.wait(Players.RespawnTime)
player:LoadCharacter()

This code needs to reparent the character to work, because changing the Player.Character reparents the old character to nil with the old behavior, and calls Destroy on it with the new behavior. Cloning the old character causes visible replication and physics lag, which isn’t present with the reparenting. I believe this is because the reparenting happens in one replication cycle, so the reparenting doesn’t actually take place on the client-side at all, and only the Character property of the Player changes. The change also breaks what I believe to be the only clean way to achieve the effect seen in Roblox’s body swap potion, which I have written psuedocode for below:

local Players = game:GetService("Players")

local player1 = Players.Player1
local player2 = Players.Player2

local character1 = player1.Character
local character2 = player2.Character

player1.Character = character2
player2.Character = character1

character1.Parent = workspace
character2.Parent = workspace

There needs to be a bypass in the function that calls Destroy on the character to keep these scripts functional, and I believe that if it’s a use case interesting enough for Roblox to make a R$400 gear based on it, it’s not something that deserves to be affected by this change. I thought about requesting a new function to unlink the character from the Player without destroying it, but I don’t think such a niche function would land very well with the team. Instead, I’d like to propose that characters with nil Parents do not have Destroy called on them when Player.Character is changed. This way, I only have to add one line of code per character to keep the scripts functional:

local Debris = game:GetService("Debris")
local Players = game:GetService("Players")

local player = players.Andy_Wirus --Server-sided
local character = player.Character

character.Parent = nil --the new line of code
player.Character = nil
character.Parent = workspace
Debris:AddItem(character, 300)

task.wait(Players.RespawnTime)
player:LoadCharacter()

I do not believe this is a loophole that will cause any side effects in pre-existing games, and I hope this is a use case you will find as important as I do, @WheretIB.

1 Like

so disabling this option will not remove player and character left game

and this option will be locked in enabled and no longer possible to disable, right?

That was the original plan, but we are now considering only enabling it for Player instance and keep Character destruction a responsibility of the developer.

3 Likes

I fully agree with the new plan.
I am aiming to have AI take over the gameplay when a player leaves but their character remains in the game until either the character dies or the player rejoins. However, if the character is automatically destroyed, I will need to copy the character from just before destroy back into the workspace. This process may not be very seamless.

2 Likes

Is it intentional for the destroy callback to run before the playerremoving event in deferred signal behavior?

Not really a big issue but the signal is called Player “Removing”

1 Like

Would appreciate having character destruction be a permanent option that developers can switch on or off in StarterPlayer or Players for example. Less work for us as developers.

5 Likes