diff --git a/gamemode/modules/base/cl_entityvars.lua b/gamemode/modules/base/cl_entityvars.lua index 65568ee73..65a609223 100644 --- a/gamemode/modules/base/cl_entityvars.lua +++ b/gamemode/modules/base/cl_entityvars.lua @@ -81,8 +81,31 @@ timer.Simple(0, fp{RunConsoleCommand, "_sendDarkRPvars"}) net.Receive("DarkRP_DarkRPVarDisconnect", function(len) local userID = net.ReadUInt(16) - timer.Simple(5, function() + local ply = Player(userID) + + -- If the player is already gone, then immediately clear the data and move on. + if not IsValid(ply) then DarkRP.ClientsideDarkRPVars[userID] = nil + return + end + -- Otherwise, we need to wait until the player is actually removed + -- clientside. The net message may come in _much_ earlier than the message + -- that the player disconnected and should therefore be removed. + local hook_name = "darkrp_remove_darkrp_var_" .. userID + + hook.Add("EntityRemoved", hook_name, function(ent) + -- NOTE: ent:UserID() will return -1 in this hook, so there is no use to + -- compare UserIDs. That also means that getting DarkRPVars in the + -- EntityRemoved hook is futile, as the lookup of -1 in + -- DarkRP.ClientsideDarkRPVars wil fail. + if ent ~= ply then return end + hook.Remove("EntityRemoved", hook_name) + + -- Placing this in a timer allows for the rest of the hook runners to + -- still use the DarkRPVars until the entity is _really_ gone. + timer.Simple(0, function() + DarkRP.ClientsideDarkRPVars[userID] = nil + end) end) end) diff --git a/gamemode/modules/base/sv_entityvars.lua b/gamemode/modules/base/sv_entityvars.lua index dd32168a4..c885ea07b 100644 --- a/gamemode/modules/base/sv_entityvars.lua +++ b/gamemode/modules/base/sv_entityvars.lua @@ -269,19 +269,18 @@ function meta:customEntityCount(entTable) return entities end -hook.Add("PlayerDisconnected", "DarkRP_VarRemoval", function(ply) - maxEntities[ply] = nil +-- We use EntityRemoved to clear players of tables, because it is always called +-- after the PlayerDisconnected hook. This is called _after_ the GAMEMODE +-- function, to make sure that all regular hooks can still use DarkRPVars until +-- the very end. See https://github.com/FPtje/DarkRP/pull/3270 +(GAMEMODE or GM).DarkRPPostEntityRemoved = function(_gm, ent) + if not ent:IsPlayer() then return end + + maxEntities[ent] = nil + DarkRP.ServerDarkRPVars[ent] = nil + DarkRP.ServerPrivateDarkRPVars[ent] = nil net.Start("DarkRP_DarkRPVarDisconnect") - net.WriteUInt(ply:UserID(), 16) + net.WriteUInt(ent:UserID(), 16) net.Broadcast() -end) - -hook.Add("EntityRemoved", "DarkRP_VarRemoval", function(ent) -- We use EntityRemoved to clear players of tables, because it is always called after the PlayerDisconnected hook - if ent:IsPlayer() then - timer.Simple(0, function() - DarkRP.ServerDarkRPVars[ent] = nil - DarkRP.ServerPrivateDarkRPVars[ent] = nil - end) - end -end) +end diff --git a/gamemode/modules/base/sv_gamemode_functions.lua b/gamemode/modules/base/sv_gamemode_functions.lua index 6ed97cdf1..0fdba5fc4 100644 --- a/gamemode/modules/base/sv_gamemode_functions.lua +++ b/gamemode/modules/base/sv_gamemode_functions.lua @@ -242,6 +242,12 @@ function GM:EntityRemoved(ent) local owner = ent.Getowning_ent and ent:Getowning_ent() or Player(ent.SID or 0) if ent.DarkRPItem and IsValid(owner) and not ent.IsPocketing then owner:removeCustomEntity(ent.DarkRPItem) end if ent.isKeysOwnable and ent:isKeysOwnable() then ent:removeDoorData() end + + -- Quick workaround for the fact that we don't have a hook ordering system + -- built into gmod. + if self.DarkRPPostEntityRemoved then + self.DarkRPPostEntityRemoved(self, ent) + end end function GM:ShowSpare1(ply)