Facepunch / garrysmod-issues

Garry's Mod issue tracker
144 stars 56 forks source link

Particle cause break lua stuff when particle limit reached up to 2048. #4609

Closed Headshotz closed 4 years ago

Headshotz commented 4 years ago

Details

After reached at 2048 particle limit can be cause its break the Clientside Model that become a NULL, I don't have idea why its does NULL. Also, its not clean the lua particle effects after map change when particles limit were full.

Steps to reproduce

You need 100 bots and force them to firing gun with custom lua effect that file I uploaded.

File: tracer_actinic.zip

robotboy655 commented 4 years ago

Maybe do not create a new emitter every frame?

robotboy655 commented 4 years ago

Would this be a duplicate of https://github.com/Facepunch/garrysmod-issues/issues/1968 ?

Meachamp commented 4 years ago

I don't think this is a duplicate. Usually when this happens there isn't a LinkedListOverflow, it's mostly just all new effects being created are NULL. I don't think that's supposed to happen - either create an effect entity with a valid table, or don't create anything at all.

Usually once this error occurs it quickly progresses to breaking all map rendering and then crashing the client.

MagicSwap commented 4 years ago

It also appears that whenever the issue occurs, a full GMod restart is needed to recover, map changes won't fix it and sometimes it becomes so bad that there's just a black screen on the next map, but players can still move and interact.

robotboy655 commented 4 years ago

I managed to get these console messages but nothing breaks? It's really hard to understand what the OP is saying, what actually breaks, where is 2048 number comes from, etc.

[ERROR] ParticleEmitter: Couldn't make emitter! Too many emitters!

ParticleEmitter limit is 4096. Hitting this doesn't cause any issues, as soon as there's a free slot, it all starts to work again.

Render queue too large, flushing! (This is super slow you shouldn't render so much stuff.)

Also seen this, but again it doesn't do much in terms of things breaking.

Couldn't make effect 'tracer_actinic' - Too many Lua Effects (2049)! Are you killing them properly? (x20)
WARNING: Broke possible Lua Effect infinite loop! Check your code!

Also seen this, which again doesn't cause any problems on its own for me, even when server is close to/at the edict limit, as soon as enough effects get free'd other effects can spawn again as normal.

This was tested in local MP on flatgrass. I didn't use 100 bots, I just used 100 bullet per shot weapon_base, as I couldn't make anything happen with a bunch of bots.

To me, your particle effect code is trash. Here's a slightly edited version that doesn't create a new emitter for every single particle, i.e. this is how you are actually supposed to use particle emitters:

function EFFECT:Init( data )
    self.Position = data:GetStart()
    self.WeaponEnt = data:GetEntity()
    self.Attachment = data:GetAttachment()

    self.StartPos = self:GetTracerShootPos( self.Position, self.WeaponEnt, self.Attachment )
    self.EndPos = data:GetOrigin()

    self.Life = 0

    self:SetRenderBoundsWS( self.StartPos, self.EndPos )

    self.Emitter =  ParticleEmitter(self.EndPos)
end

function EFFECT:Think( )
    self.Life = self.Life + FrameTime() * 10 -- Effect should dissipate before the next one.
    self.StartPos = self:GetTracerShootPos( self.Position, self.WeaponEnt, self.Attachment )

    return self.Life < 1
end

local beammat = Material("trails/smoke")
local glowmat = Material("sprites/light_glow02_add")
function EFFECT:Render()
    local texcoord = math.Rand( 0, 1 )

    local norm = (self.StartPos - self.EndPos) * self.Life
    local dir = (self.StartPos - self.EndPos):Angle()
    local dfwd = dir:Forward()
    local dup = dir:Up()
    local drgt = dir:Right()
    local nlen = norm:Length()

    local prevspinpos = self.StartPos
    local alpha = 1 - self.Life

    if ( self.Emitter ) then
        self.Emitter:SetNearClip(24, 32)

        local particle = self.Emitter:Add("effects/blueflare"..math.random(1, 1), self.EndPos - (self.EndPos - self.StartPos) * math.Rand(0, 1))
        local vel = VectorRand():GetNormal() * 25
        particle:SetDieTime(1)
        particle:SetColor(255,45,45)
        particle:SetColor(255,45,45)
        particle:SetStartAlpha(125)
        particle:SetEndAlpha(0)
        particle:SetStartSize(2)
        particle:SetEndSize(0)
        particle:SetVelocity(vel)
        particle:SetGravity(vel * -0.5)
        particle:SetRoll(math.random(360))
    end

    render.SetMaterial(beammat)
    for i = 0, nlen, 32 do
        if i > 704 then break end -- Limit the range on the spin beam effect...otherwise might be expensive.

        local set = i - CurTime() * 15

        local basebeampos = self.StartPos - dfwd * i
        local spinbeampos = basebeampos + dup * math.sin(set) * 6 + drgt * math.cos(set) * 6

        if i == 704 then spinbeampos = basebeampos end -- Tie the spin back into the main beam at the end of the spin beams

        render.DrawBeam(prevspinpos, spinbeampos, 3, texcoord + i, texcoord + nlen / 128 + i, Color(255, 0, 0, 255 * alpha))

        prevspinpos = spinbeampos
    end

    for i = 0, 4 do
        render.DrawBeam(self.StartPos, self.EndPos, 2.3, texcoord, texcoord + nlen / 128, Color(145, 45, 45, 200 * alpha))
    end
    render.DrawBeam(self.StartPos, self.EndPos, 8.5, texcoord, texcoord + nlen / 128, Color(255, 15, 15, 255 * alpha))

    render.SetMaterial(glowmat)
    render.DrawSprite(self.StartPos, 50, 50, Color(255, 0, 0, 200 * alpha))
    render.DrawSprite(self.EndPos, 50, 50, Color(255, 25, 25, 148 * alpha))
end
Meachamp commented 4 years ago

Wouldn't that new code actually cause a leak? I can't see finish being called from anywhere in there.

robotboy655 commented 4 years ago

You are not obligated to call it. It will be automatically GC'd after the effect entity gets deleted (i.e. no more references) and when all particles are dead.

You can test this yourself.

Edit: if you really want to, you can call Finish() just before returning in EFFECT:Think when self.Life >= 1

function EFFECT:Think( )
    self.Life = self.Life + FrameTime() * 10 -- Effect should dissipate before the next one.
    self.StartPos = self:GetTracerShootPos( self.Position, self.WeaponEnt, self.Attachment )

    local isAlive = self.Life < 1
    if ( !isAlive && self.Emitter ) then self.Emitter:Finish() end
    return isAlive
end
Meachamp commented 4 years ago

Interesting. The wiki says that you must call Finish when you're done with the emitter, but if that's not the case that's a lot better. I just remember all these issues with particle emitters from years ago.

mcNuggets1 commented 4 years ago

The sandbox gamemode itself can create this form of misbehaviour The tool tracer sometimes creates infinite particles, thus breaking all particles all together, because they are all null.

robotboy655 commented 4 years ago

Can you take like a few minutes and properly explain what the issue is? Actually make sense?

The sandbox toolgun tracer doesn't even use particles at all. https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/entities/effects/selection_ring.lua https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/entities/effects/selection_indicator.lua https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/base/entities/effects/tooltracer.lua

Having too many particles doesn't really cause any issues, they just will not get created until there's a slot, Lua will get a fake particle.

So what is the actual problem here? Is there even one?

mcNuggets1 commented 4 years ago

Im om vacation right now. I can get you a stack trace of the problem when im home. I actually have These in older files

Meachamp commented 4 years ago

I've done some further investigation. At least on my end, the issue was caused by a slow ClientsideModel leak. Since effects and ClientsideModels share NonNetworkedEntity slots, leaking clientside models will eventually cause the behavior. I would not consider this a new bug. Sorry about that.

robotboy655 commented 4 years ago

Thanks. So I am guessing the biggest issue here is that the created effect entity has self as NULL. Not much I can do about addons leaking ClientsideModels.

robotboy655 commented 4 years ago

And that should be fixed on Dev. I did not observe any crashes in my testing, Don't know if there's anything else to be done here, so I am going to close this. Still don't know where particles come into play here but whatever.

As far as I can tell this is largely an issue with your Lua code, but if you can find anything else that causes big issues when hitting the limit, feel free to share, but please with proper steps to reproduce, or at least a good explanation of what the issue is.