I posted here - hopefully this’ll be enough. They know how to get ahold of me
does the weak table method work?
local weakCharacters = setmetatable({}, { __mode = "v" }) -- Create a weak table
for _, player in pairs(game.Players:GetPlayers()) do
player.CharacterAdded:Connect(function()
-- Keep a weak reference to the old character since it's weak, shouldnt prevent the garbage collector from cleaning it up?
table.insert(weakCharacters, character)
end)
end
game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
table.insert(weakCharacters, character)
end)
end)
while true do
for _, player in pairs(game.Players:GetPlayers()) do
player:LoadCharacter()
end
wait(0.2)
end
It probably doesnt work, because I’m assuming that player:LoadCharacter()
doesn’t hold onto other strong references of the old character internally. Worth a shot.
It might.
It’s worth mentioning again that the second example doesn’t use LoadCharacter, it just allows spawning via a regular spawnpoint.
I have seen this bug reported a few months ago, can’t find the post.
I thought it would be fixed by now, but eh?
Yeah I think I’ve seen this get “Fixed” before, too.
local Players = game:GetService("Players")
local function cleanupCharacter(player)
if player.Character then
-- Cleanup logic; we know it works
player.Character:Destroy()
player.Character = nil
end
end
local function overrideLoadCharacter(player)
-- Ensure we're not doing this more than once per player
if player:IsA('Player') and not player:FindFirstChild("LoadCharacterOverridden") then
local originalLoadCharacter = player.LoadCharacter
player.LoadCharacter = function(...)
cleanupCharacter(player)
return originalLoadCharacter(player, ...)
end
-- Add a tag
local tag = Instance.new("BoolValue")
tag.Name = "LoadCharacterOverridden"
tag.Parent = player
end
end
-- Immediately override for existing players
for _, player in pairs(Players:GetPlayers()) do
overrideLoadCharacter(player)
end
-- Setup to override for any players that join in the future
Players.PlayerAdded:Connect(overrideLoadCharacter)
Make sure no other script in your game is trying to call LoadCharacter before this script, no idea what determines the order in which scripts run in serverscriptservice, assuming it’s sorted by name prefix the script with $
This arleady is a solid solution.
No, I was replying to your weak table question. Just informing that roblox instances do not get garbage collected, even in weak tables.
Though you’re right, checking for a character and destroying before calling LoadCharacter
is a very great solution.
Understandable Pointed out that it’s an outdated comment.
Good to know Roblox instances do not get garbage collected, but I heavily doubted from the start that weak tables would work, considering I don’t know the internal code haha
Bonus bug!
I think there might be something broken with the way player.Character:Destroy() works.
player.Character:Destroy()
player.Character = nil
does NOT behave the same way as just calling
player.Character:Destroy()
Full example:
CharacterAutoLoads = false
while(true) do
for key,player in game.Players:GetPlayers() do
if (player.Character) then
player.Character:Destroy()
--player.Character = nil --Comment this line out
end
player:LoadCharacter()
end
wait(0.2)
end
The following code causes the following console error:
which does not happen if you set Character = nil explicitly.
I assume that’s the core movement scripts likely thinking that the character still exists, but it doesn’t. I don’t particularly think that’s a bug, as it’s only throwing a warning. There should be some sort of method though to destroy the character without that workaround.
It is weird that this is still happening after being reported and “fixed” many times. This is a major issue for games that make the player die a lot and do not use custom respawning systems
Would you have to call
player.Character:Destroy()
player.Character = nil
when the player leaves?
This isn’t an issue as long as scripts aren’t holding references to the character after it respawns.
local Character = Player.Character
Player:LoadCharacter()
-- ...Leak!
local Character = Player.Character
Player.CharacterRemoving:Connect(function()
Character = nil
end)
Player.CharacterAdded:Connect(function(newCharacter)
Character = newCharacter
end)
Player:LoadCharacter()
-- ...No leak!
Instance:Destroy()
is a handy way to remove existing connections and references so that garbage collection can occur, but garbage collection still won’t occur if care isn’t taken to manually discard of old references held in scripts.
References to Instances are never weak, even in weak tables!
That’s what the bug report is about. Opening @MrChickenRocket 's repro file, this is the only script in the baseplate:
It does not hold any references to the old character; merely calling ::LoadCharacter
.
Oh no! Sorry, I misunderstood the bug report. I thought the OP was reporting that Instance:Destroy()
isn’t implicitly called on old characters. If there’s still a leak when there’s no scripts holding references to the old character, that’s a whole other kettle of fish - and it’s quite disappointing that some people mention this happening months ago yet we still don’t have a fix.
Not a bug. Player.Character = nil
unassigns the character from the player. Character:Destroy()
just parents it to nil
.
Actually, it is. From what I gather, the player’s character stays in memory even after the player logs out. You have to call Model:Destroy()
on the character so it can be garbage collected. Unless you’re using a custom spawn/respawn system, the Roblox default system will leak memory. Just calling Player:LoadCharacter()
without called destroy first is what causes it.
So it seems that Roblox’s default system is missing a step. The problem is below the LUA interpreter so there’s not much we can do about it except explicitly call Destroy()
on the character to try and keep the memory leak to a minimum.
Yes, I understand that, but I was referring to the message I was replying to.