Hello Creators!
Today, we’re adding a new opt-in behavior that automatically destroys Player and Character Instances on the server when the player leaves or respawns.
We will be rolling this feature out in three stages:
1. Today: PlayerCharacterDestroyBehavior is set to Default, which behaves the same as Disabled, the new behavior is now opt-in.
2. Early 2024: Default behavior is updated to behave the same as Enabled, the new behavior is now opt-out.
3. Mid 2024: We will remove the PlayerCharacterDestroyBehavior property and it will no longer be possible to opt out.
Current Behavior
Currently, when a player disconnects from an experience, the parent of the Player Instance is set to nil without the Player Instance being destroyed. This is also the case for character Models. When the character despawns, the Model is not destroyed but rather parented to nil.
Problems With This Current Behavior
The problem with this behavior is that over the lifetime of an Experience server, players may leave, join, and respawn thousands of times. If any references to these player or character Instances are maintained in your code, such as references to connections that haven’t been disconnected, these Instances will not be garbage collected and a memory leak might occur causing your experience’s servers to crash.
Since connecting to player or character events is extremely common, there’s a high likelihood that your project is impacted by this issue.
If you are interested in learning more about this, we recommend you read @stravant excellent write up: PSA: Connections Can Memory Leak Instances.
New Opt-In Behavior
To help resolve this issue, we are introducing a new property to Workspace - PlayerCharacterDestroyBehavior. You can enable this property from the Explorer Window.
When this property is set to Enabled, the following behavior occurs:
- When a player leaves the experience, the
Destroy
method is called on thePlayer
Instance. This task is deferred and occurs after bound event connections are invoked, includingPlayerRemoving
. - When a player’s character is removed, the
Destroy
method is called on theModel
Instance. This task is deferred and occurs after bound event connections are invoked, includingCharacterRemoving
.
This means any event connections bound to Player
/Character
Instances and their descendants are disconnected when they are no longer required. This is an important mitigation that will prevent the accumulation of server side leaks in your experience.
Keep in mind that this Destroy
call is performed automatically only on the server and not on the client, but replication rules still apply.
What to Look Out For
Although we do not anticipate any impact to conventional script patterns, there are potential scenarios that are “broken” by this behavior.
Generally speaking, any code that expects to access the children of a player or character after they have been removed will be impacted. Additionally, any code that attempts to re-parent a destroyed child instance or character model will also fail as the Destroy method locks the Parent property of an Instance.
Note that only code that executes after the PlayerRemoving
/CharacterAdded
signals have finished being invoked is impacted, for example code that is delayed using task.wait
or task.delay
.
It is however still safe to call WaitForChild
on children that are present at the invoke time; such calls can also be replaced with FindFirstChild
.
Example:
-- Broken by PlayerCharacterDestroyBehavior = Enabled
Players.PlayerRemoving:Connect(function(player)
task.wait(1) -- Because we are deferring, this code will not work
local points = player.leaderstats.Points.Value
print(`player.Name left with ${points} points!`)
end)
-- Still works, even with PlayerCharacterDestroyBehavior = Enabled
Players.PlayerRemoving:Connect(function(player)
local points = player.leaderstats.Points.Value
print(`player.Name left with ${points} points!`)
end)
Please note, PlayerCharacterDestroyBehavior is currently opt-in, meaning the Default property behaves the same as Disabled.
Next Steps
We recommend creators take the following steps:
- Evaluate your code for any deferred operations on Player or Character Instances after the player has left or the character has respawned. If you have any of these operations, consider updating them.
- Set PlayerCharacterDestroyBehavior to Enabled and playtest your experience to see if you can identify any issues.
- If all looks good, publish your experience and let us know if you have any problems.
We encourage you to try out the new behavior to improve the performance of your experience. Please share any questions or feedback in the comments below, as we’re eager to hear about your experience. Thank you!