From 69d946681430743becef23bef0f36452001547b5 Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Sat, 26 Aug 2023 17:05:50 -0400 Subject: [PATCH] Cleanup ent list management with new class. Fixes: 1501 (#1502) * Cleanup ent list management with new class. Fixes: 1501 * Fixes --- lua/starfall/libs_sh/hologram.lua | 72 +++++++------------------- lua/starfall/libs_sv/constraint.lua | 80 ++++++++++++----------------- lua/starfall/libs_sv/navmesh.lua | 32 ++---------- lua/starfall/libs_sv/nextbot.lua | 35 +++++-------- lua/starfall/libs_sv/prop.lua | 50 ++++++------------ lua/starfall/sflib.lua | 42 +++++++++++++++ 6 files changed, 128 insertions(+), 183 deletions(-) diff --git a/lua/starfall/libs_sh/hologram.lua b/lua/starfall/libs_sh/hologram.lua index 76e228de8..734c044b6 100644 --- a/lua/starfall/libs_sh/hologram.lua +++ b/lua/starfall/libs_sh/hologram.lua @@ -6,10 +6,10 @@ registerprivilege("hologram.modify", "Modify holograms", "Allows the user to mod registerprivilege("hologram.create", "Create hologram", "Allows the user to create holograms", CLIENT and { client = {} } or nil) registerprivilege("hologram.setRenderProperty", "RenderProperty", "Allows the user to change the rendering of an entity", { entities = {} }) -local plyCount = SF.LimitObject("holograms", "holograms", 200, "The number of holograms allowed to spawn via Starfall scripts for a single player") +local entList = SF.EntManager("holograms", "holograms", 200, "The number of holograms allowed to spawn via Starfall scripts for a single player") local maxclips = CreateConVar("sf_holograms_maxclips", "8", { FCVAR_ARCHIVE, FCVAR_REPLICATED }, "The max number of clips per hologram entity") -SF.ResourceCounters.Holograms = {icon = "icon16/bricks.png", count = function(ply) return plyCount:get(ply).val end} +SF.ResourceCounters.Holograms = {icon = "icon16/bricks.png", count = function(ply) return entList:get(ply).val end} local entmeta = FindMetaTable("Entity") local cl_hologram_meta = { @@ -58,53 +58,19 @@ local vec_meta, vwrap, vunwrap = instance.Types.Vector, instance.Types.Vector.Wr local mtx_meta, mwrap, munwrap = instance.Types.VMatrix, instance.Types.VMatrix.Wrap, instance.Types.VMatrix.Unwrap local getent -local holograms = {} instance:AddHook("initialize", function() getent = instance.Types.Entity.GetEntity hologram_meta.__tostring = ent_meta.__tostring end) -local function hologramOnDestroy(holo) - holograms[holo] = nil - plyCount:free(instance.player, 1) -end - -local function removeHoloInternal(holo) - if holo:IsValid() then - holo:RemoveCallOnRemove("starfall_hologram_delete") - hologramOnDestroy(holo) - holo:Remove() - end -end - -local function removeHolo(holo) - if CLIENT and instance.data.render.isRendering then SF.Throw("Cannot remove while in rendering hook!", 3) end - if not holo:IsValid() or not holo.IsSFHologram then SF.Throw("Invalid hologram!", 3) end - return removeHoloInternal(holo) -end - -local function removeAllHolosInternal() - for holoent, _ in pairs(holograms) do - removeHoloInternal(holoent) - end -end - -local function removeAllHolos() - for holoent, _ in pairs(holograms) do - removeHolo(holoent) - end -end - instance:AddHook("deinitialize", function() - if SERVER then - removeAllHolosInternal() + if SERVER or not instance.data.render.isRendering then + entList:deinitialize(instance, true) else - if instance.data.render.isRendering then - -- Removing hologram in render hook = crash - timer.Simple(0, removeAllHolosInternal) - else - removeAllHolosInternal() - end + -- Removing hologram in render hook = crash + timer.Simple(0, function() + entList:deinitialize(instance, true) + end) end end) @@ -142,7 +108,7 @@ function hologram_library.create(pos, ang, model, scale) ang = aunwrap(ang) model = SF.CheckModel(model, ply) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) local holoent if SERVER then @@ -151,16 +117,14 @@ function hologram_library.create(pos, ang, model, scale) holoent:SetPos(SF.clampPos(pos)) holoent:SetAngles(ang) holoent:SetModel(model) - holoent:CallOnRemove("starfall_hologram_delete", hologramOnDestroy) holoent:Spawn() if CPPI then holoent:CPPISetOwner(ply == SF.Superuser and NULL or ply) end - holograms[holoent] = true if scale~=nil then holoent:SetScale(vunwrap(scale)) end - plyCount:free(ply, -1) + entList:register(instance, holoent) return wrap(holoent) end else @@ -172,12 +136,10 @@ function hologram_library.create(pos, ang, model, scale) holoent:SetAngles(ang) holoent:SetModel(model) holoent:SetRenderMode(RENDERGROUP_TRANSLUCENT) - holoent:CallOnRemove("starfall_hologram_delete", hologramOnDestroy) debug.setmetatable(holoent, cl_hologram_meta) holoent:Spawn() - holograms[holoent] = true if scale~=nil then holoent:SetScale(vunwrap(scale)) @@ -185,7 +147,7 @@ function hologram_library.create(pos, ang, model, scale) holoent:SetScale(Vector(1,1,1)) end - plyCount:free(ply, -1) + entList:register(instance, holoent) return wrap(holoent) end end @@ -195,14 +157,14 @@ end -- @return boolean True if user can spawn holograms, False if not. function hologram_library.canSpawn() if not SF.Permissions.hasAccess(instance, nil, "hologram.create") then return false end - return plyCount:check(instance.player) > 0 + return entList:check(instance.player) > 0 end --- Checks how many holograms can be spawned -- @return number Number of holograms able to be spawned function hologram_library.hologramsLeft() if not SF.Permissions.hasAccess(instance, nil, "hologram.create") then return 0 end - return plyCount:check(instance.player) + return entList:check(instance.player) end if SERVER then @@ -504,15 +466,19 @@ end --- Removes a hologram -- @shared function hologram_methods:remove() + if CLIENT and instance.data.render.isRendering then SF.Throw("Cannot remove while in rendering hook!", 2) end + local holo = getholo(self) + if not (holo:IsValid() and holo.IsSFHologram) then SF.Throw("Invalid hologram!", 2) end checkpermission(instance, holo, "hologram.create") - removeHolo(holo) + entList:remove(instance, holo) end --- Removes all holograms created by the calling chip -- @shared function hologram_library.removeAll() - removeAllHolos() + if CLIENT and instance.data.render.isRendering then SF.Throw("Cannot remove while in rendering hook!", 2) end + entList:clear(instance) end diff --git a/lua/starfall/libs_sv/constraint.lua b/lua/starfall/libs_sv/constraint.lua index 3bfbbf1a9..6c9002b18 100644 --- a/lua/starfall/libs_sv/constraint.lua +++ b/lua/starfall/libs_sv/constraint.lua @@ -14,13 +14,7 @@ registerprivilege("constraints.elastic", "Elastic", "Allows the user to elastic registerprivilege("constraints.nocollide", "Nocollide", "Allows the user to nocollide two entities", { entities = {} }) registerprivilege("constraints.any", "Any", "General constraint functions", { entities = {} }) -local plyCount = SF.LimitObject("constraints", "constraints", 600, "The number of constraints allowed to spawn via Starfall") - -local function constraintOnDestroy(ent, constraints, ply) - plyCount:free(ply, 1) - constraints[ent] = nil -end - +local entList = SF.EntManager("constraints", "constraints", 600, "The number of constraints allowed to spawn via Starfall") --- Library for creating and manipulating constraints. -- @name constraint @@ -38,24 +32,15 @@ SF.RegisterType("Constraint", true, false) return function(instance) local checkpermission = instance.player ~= SF.Superuser and SF.Permissions.check or function() end - local getent -local constraints = {} -local constraintsClean = true instance:AddHook("initialize", function() getent = instance.Types.Entity.GetEntity end) +local constraintsClean = true + instance:AddHook("deinitialize", function() - if constraintsClean then - for ent, _ in pairs(constraints) do - if (ent and ent:IsValid()) then - ent:RemoveCallOnRemove("starfall_constraint_delete") - constraintOnDestroy(ent, constraints, instance.player) - ent:Remove() - end - end - end + entList:deinitialize(instance, constraintsClean) end) local constraint_library = instance.Libraries.constraint @@ -86,7 +71,13 @@ end function constr_methods:remove() local ent = cunwrap(self) check_constr_perms(ent) - ent:Remove() + entList:remove(instance, ent) +end + +--- Removes all constraints created by the calling chip +-- @server +function constraint_library.removeAll() + entList:clear(instance) end --- Returns whether the constraint is valid or not @@ -112,13 +103,6 @@ local function checkConstraint(e, t) end end -local function register(ent, instance) - local ply = instance.player - ent:CallOnRemove("starfall_constraint_delete", constraintOnDestroy, constraints, ply) - plyCount:free(ply, -1) - constraints[ent] = true -end - --- Welds two entities -- @param Entity e1 The first entity -- @param Entity e2 The second entity @@ -129,7 +113,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.weld(e1, e2, bone1, bone2, force_lim, nocollide) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -148,7 +132,7 @@ function constraint_library.weld(e1, e2, bone1, bone2, force_lim, nocollide) local ent = constraint.Weld(ent1, ent2, bone1, bone2, force_lim, nocollide) if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -168,7 +152,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.axis(e1, e2, bone1, bone2, v1, v2, force_lim, torque_lim, friction, nocollide, laxis) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -194,7 +178,7 @@ function constraint_library.axis(e1, e2, bone1, bone2, v1, v2, force_lim, torque local ent = constraint.Axis(ent1, ent2, bone1, bone2, vec1, vec2, force_lim, torque_lim, friction, nocollide, axis) if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -211,7 +195,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.ballsocket(e1, e2, bone1, bone2, pos, force_lim, torque_lim, nocollide) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -233,7 +217,7 @@ function constraint_library.ballsocket(e1, e2, bone1, bone2, pos, force_lim, tor local ent = constraint.Ballsocket(ent1, ent2, bone1, bone2, vec1, force_lim, torque_lim, nocollide) if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -255,7 +239,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.ballsocketadv(e1, e2, bone1, bone2, v1, v2, force_lim, torque_lim, minv, maxv, frictionv, rotateonly, nocollide) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -282,7 +266,7 @@ function constraint_library.ballsocketadv(e1, e2, bone1, bone2, v1, v2, force_li local ent = constraint.AdvBallsocket(ent1, ent2, bone1, bone2, vec1, vec2, force_lim, torque_lim, mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, frictions.x, frictions.y, frictions.z, rotateonly, nocollide) if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -303,7 +287,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.elastic(index, e1, e2, bone1, bone2, v1, v2, const, damp, rdamp, width, stretch) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -333,7 +317,7 @@ function constraint_library.elastic(index, e1, e2, bone1, bone2, v1, v2, const, local ent = constraint.Elastic(ent1, ent2, bone1, bone2, vec1, vec2, const, damp, rdamp, "cable/cable2", math.Clamp(width, 0, 50), stretch) if ent then - register(ent, instance) + entList:register(instance, ent) e1.Elastics[index] = ent e2.Elastics[index] = ent @@ -359,7 +343,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.rope(index, e1, e2, bone1, bone2, v1, v2, length, addlength, force_lim, width, material, rigid, color) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -391,7 +375,7 @@ function constraint_library.rope(index, e1, e2, bone1, bone2, v1, v2, length, ad local ent = constraint.Rope(ent1, ent2, bone1, bone2, vec1, vec2, length, addlength, force_lim, math.Clamp(width, 0, 50), material, rigid, color) if ent then - register(ent, instance) + entList:register(instance, ent) e1.Ropes[index] = ent e2.Ropes[index] = ent @@ -411,7 +395,7 @@ end -- @server function constraint_library.slider(e1, e2, bone1, bone2, v1, v2, width) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -431,7 +415,7 @@ function constraint_library.slider(e1, e2, bone1, bone2, v1, v2, width) local ent = constraint.Slider(ent1, ent2, bone1, bone2, vec1, vec2, math.Clamp(width, 0, 50), "cable/cable2") if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -445,7 +429,7 @@ end -- @server function constraint_library.nocollide(e1, e2, bone1, bone2) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local ent1 = eunwrap(e1) local ent2 = eunwrap(e2) @@ -461,7 +445,7 @@ function constraint_library.nocollide(e1, e2, bone1, bone2) local ent = constraint.NoCollide(ent1, ent2, bone1, bone2) if ent then - register(ent, instance) + entList:register(instance, ent) return cwrap(ent) end end @@ -474,7 +458,7 @@ end -- @return Constraint The constraint entity -- @server function constraint_library.keepupright(e, ang, bone, lim) - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) e = eunwrap(e) ang = aunwrap(ang) @@ -487,9 +471,9 @@ function constraint_library.keepupright(e, ang, bone, lim) checkluatype(bone, TYPE_NUMBER) checkluatype(lim, TYPE_NUMBER) - local c = constraint.Keepupright(e, ang, bone, lim) - if c then - register(c, instance) + local ent = constraint.Keepupright(e, ang, bone, lim) + if ent then + entList:register(instance, ent) return cwrap(ent) end end @@ -602,7 +586,7 @@ end -- @server -- @return number Number of constraints able to be spawned function constraint_library.constraintsLeft() - return plyCount:check(instance.player) + return entList:check(instance.player) end end diff --git a/lua/starfall/libs_sv/navmesh.lua b/lua/starfall/libs_sv/navmesh.lua index d1d3beb59..0740d6725 100644 --- a/lua/starfall/libs_sv/navmesh.lua +++ b/lua/starfall/libs_sv/navmesh.lua @@ -28,7 +28,7 @@ SF.RegisterLibrary("navmesh") SF.RegisterType("NavArea", true, false, nil, "LockedNavArea") SF.RegisterType("LockedNavArea", true, false) -- NavArea that can't be modified. -local plyCount = SF.LimitObject("navareas", "navareas", 40, "The number of CNavAreas allowed to spawn via Starfall") +local entList = SF.EntManager("navareas", "navareas", 40, "The number of CNavAreas allowed to spawn via Starfall") return function(instance) local checkpermission = instance.player ~= SF.Superuser and SF.Permissions.check or function() end @@ -43,23 +43,6 @@ return function(instance) local vec_meta, vwrap, vunwrap = instance.Types.Vector, instance.Types.Vector.Wrap, instance.Types.Vector.Unwrap local cunwrap = instance.Types.Color.Unwrap - local navareas = {} - - local function register(navarea) - plyCount:free(instance.player, -1) - navareas[navarea] = true - end - - local function destroy(navarea) - plyCount:free(instance.player, 1) - - if navarea and navarea:IsValid() then - navarea:Remove() - end - - - navareas[navarea] = nil - end local getent instance:AddHook("initialize", function() @@ -67,9 +50,7 @@ return function(instance) end) instance:AddHook("deinitialize", function() - for navarea in pairs(navareas) do - destroy(navarea) - end + entList:deinitialize(instance, true) end) function lnavarea_meta:__tostring() @@ -165,11 +146,11 @@ return function(instance) -- @return NavArea? The new NavArea or nil if we failed for some reason function navmesh_library.createNavArea(corner, opposite_corner) checkpermission(instance, nil, "navarea.create") - plyCount:checkuse(instance.player, 1) + entList:checkuse(instance.player, 1) local area = navmesh.CreateNavArea( vunwrap(corner), vunwrap(opposite_corner) ) if area then - register(area) + entList:register(instance, area) return navwrap(area) end end @@ -659,7 +640,6 @@ return function(instance) return lnavunwrap(self):PlaceOnGround(corner) end - --- Removes a CNavArea from the Open List with the lowest cost to traverse to from the starting node, and returns it. -- Requires the `navarea.openlist` permission -- @return NavArea The CNavArea from the Open List with the lowest cost to traverse to from the starting node. @@ -669,12 +649,10 @@ return function(instance) return lnavwrap( lnavunwrap(self):PopOpenList() ) end - --- Removes the given NavArea. function navarea_methods:remove() local nav = navunwrap(self) - - destroy(nav) + entList:remove(instance, nav) local sensitive2sf, sf2sensitive = navarea_meta.sensitive2sf, navarea_meta.sf2sensitive sensitive2sf[nav] = nil diff --git a/lua/starfall/libs_sv/nextbot.lua b/lua/starfall/libs_sv/nextbot.lua index fcbbc7cba..0877b885e 100644 --- a/lua/starfall/libs_sv/nextbot.lua +++ b/lua/starfall/libs_sv/nextbot.lua @@ -17,6 +17,7 @@ SF.RegisterType("NextBot", false, true, debug.getregistry().NextBot, "Entity") SF.RegisterLibrary("nextbot") registerprivilege("nextbot.create", "Create nextbot", "Allows the user to create nextbots.") +registerprivilege("nextbot.remove", "Remove a nextbot", "Allows the user to remove a nextbot.", {entites = {}}) registerprivilege("nextbot.setGotoPos", "Set nextbot goto pos", "Allows the user to set a vector pos for the nextbot to try and go to.", {entites = {}}) registerprivilege("nextbot.setApproachPos", "Nextbot approach goal", "Allows the user to make a nextbot approach a specified Vector.", {entites = {}}) registerprivilege("nextbot.removeApproachPos", "Nextbot approach goal", "Allows the user to remove the approach pos from a nextbot.", {entites = {}}) @@ -57,34 +58,19 @@ registerprivilege("nextbot.setClimbAllowed", "Nextbot allow climb", "Allows the registerprivilege("nextbot.setAvoidAllowed", "Nextbot allow avoid", "Allows the user to set whether the nextbot can try to avoid obstacles.", {entities = {}}) registerprivilege("nextbot.setJumpGapsAllowed", "Nextbot allow jump gaps", "Allows the user to set whether the nextbot can jump gaps.", {entities = {}}) -local nbCount = SF.LimitObject("nextbots", "nextbots", 30, "The number of props allowed to spawn via Starfall") +local entList = SF.EntManager("nextbots", "nextbots", 30, "The number of props allowed to spawn via Starfall") return function(instance) local checkpermission = instance.player ~= SF.Superuser and SF.Permissions.check or function() end -local nextbots = {} - local nextbot_library, nb_meta, nb_methods = instance.Libraries.nextbot, instance.Types.NextBot, instance.Types.NextBot.Methods local vec_meta, vwrap, vunwrap = instance.Types.Vector, instance.Types.Vector.Wrap, instance.Types.Vector.Unwrap local navarea_methods, navarea_meta, navwrap, navunwrap = instance.Types.NavArea.Methods, instance.Types.NavArea, instance.Types.NavArea.Wrap, instance.Types.NavArea.Unwrap local nbwrap, nbunwrap = instance.Types.NextBot.Wrap, instance.Types.NextBot.Unwrap -local function nextbotOnDestroy(ent) - local ply = instance.player - nbCount:free(ply, 1) - nextbots[ent] = nil -end - -local function register(ent) - ent:CallOnRemove("starfall_nextbot_delete", nextbotOnDestroy) - nbCount:free(instance.player, -1) - nextbots[ent] = true -end instance:AddHook("deinitialize", function() - for nextbot in pairs(nextbots) do - nextbot:Remove() - end + entList:deinitialize(instance, true) end) function nb_meta:__tostring() @@ -103,7 +89,7 @@ function nextbot_library.create(pos, mdl) local ply = instance.player mdl = SF.CheckModel(mdl, ply) - nbCount:checkuse(ply, 1) + entList:checkuse(ply, 1) local nb = ents.Create("starfall_cnextbot") register(nb, instance) @@ -112,19 +98,26 @@ function nextbot_library.create(pos, mdl) nb.chip = instance.entity nb:Spawn() nb:SetCreator(ply) - nextbots[nb] = true + entList:register(instance, nb) if CPPI then nb:CPPISetOwner(ply == SF.Superuser and NULL or ply) end return nbwrap(nb) end - + +--- Removes the given nextbot. +function nextbot_library:remove() + local nb = nbunwrap(self) + checkpermission(instance, nb, "nextbot.remove") + entList:remove(instance, nb) +end + --- Checks if a user can spawn anymore nextbots. -- @server -- @return boolean True if user can spawn nextbots, False if not. function nextbot_library.canSpawn() if not SF.Permissions.hasAccess(instance, nil, "nextbot.create") then return false end - return nbCount:check(instance.player) > 0 + return entList:check(instance.player) > 0 end --- Makes the nextbot try to go to a specified position without using navmesh pathfinding (in a straight line). diff --git a/lua/starfall/libs_sv/prop.lua b/lua/starfall/libs_sv/prop.lua index 8aa52e32a..f572197aa 100755 --- a/lua/starfall/libs_sv/prop.lua +++ b/lua/starfall/libs_sv/prop.lua @@ -8,7 +8,7 @@ registerprivilege("prop.createRagdoll", "Create a ragdoll", "Allows the user to registerprivilege("prop.createCustom", "Create custom prop", "Allows the user to create custom props") -local plyCount = SF.LimitObject("props", "props", -1, "The number of props allowed to spawn via Starfall") +local entList = SF.EntManager("props", "props", -1, "The number of props allowed to spawn via Starfall") local plyPropBurst = SF.BurstObject("props", "props", 4, 4, "Rate props can be spawned per second.", "Number of props that can be spawned in a short time.") local maxCustomSize = CreateConVar("sf_props_custom_maxsize", "2048", FCVAR_ARCHIVE, "The max hull size of a custom prop") @@ -28,37 +28,19 @@ SF.RegisterLibrary("prop") return function(instance) local checkpermission = instance.player ~= SF.Superuser and SF.Permissions.check or function() end -local props = {} local propClean = true local propUndo = false instance:AddHook("deinitialize", function() - if propClean then - for prop, _ in pairs(props) do - prop:Remove() - end - end + entList:deinitialize(instance, propClean) end) - local props_library = instance.Libraries.prop local owrap, ounwrap = instance.WrapObject, instance.UnwrapObject local ent_meta, ewrap, eunwrap = instance.Types.Entity, instance.Types.Entity.Wrap, instance.Types.Entity.Unwrap local ang_meta, awrap, aunwrap = instance.Types.Angle, instance.Types.Angle.Wrap, instance.Types.Angle.Unwrap local vec_meta, vwrap, vunwrap = instance.Types.Vector, instance.Types.Vector.Wrap, instance.Types.Vector.Unwrap -local function propOnDestroy(ent) - local ply = instance.player - plyCount:free(ply, 1) - props[ent] = nil -end - -local function register(ent) - ent:CallOnRemove("starfall_prop_delete", propOnDestroy) - plyCount:free(instance.player, -1) - props[ent] = true -end - --- Creates a prop -- @server -- @param Vector pos Initial entity position @@ -79,15 +61,15 @@ function props_library.create(pos, ang, model, frozen) model = SF.CheckModel(model, ply, true) plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) if ply ~= SF.Superuser and gamemode.Call("PlayerSpawnProp", ply, model)==false then SF.Throw("Another hook prevented the prop from spawning", 2) end local propent = ents.Create("prop_physics") - register(propent, instance) propent:SetPos(pos) propent:SetAngles(ang) propent:SetModel(model) propent:Spawn() + entList:register(instance, propent) if not propent:GetModel() then propent:Remove() SF.Throw("Invalid model", 2) end @@ -129,13 +111,13 @@ function props_library.createRagdoll(model, frozen) if not model then SF.Throw("Invalid model", 2) end plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) if ply ~= SF.Superuser and gamemode.Call("PlayerSpawnRagdoll", ply, model)==false then SF.Throw("Another hook prevented the ragdoll from spawning", 2) end local ent = ents.Create("prop_ragdoll") - register(ent, instance) ent:SetModel(model) ent:Spawn() + entList:register(instance, ent) if not ent:GetModel() then ent:Remove() SF.Throw("Invalid model", 2) end @@ -181,7 +163,7 @@ function props_library.createCustom(pos, ang, vertices, frozen) local ply = instance.player plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) if instance.player ~= SF.Superuser and gamemode.Call("PlayerSpawnProp", ply, "starfall_prop")==false then SF.Throw("Another hook prevented the prop from spawning", 2) end local uwVertices = {} @@ -222,12 +204,12 @@ function props_library.createCustom(pos, ang, vertices, frozen) plyVertexCount:free(-totalVertices) local propent = ents.Create("starfall_prop") - register(propent, instance) propent.streamdata = streamdata propent:SetPos(pos) propent:SetAngles(ang) propent.Mesh = uwVertices propent:Spawn() + entList:register(instance, propent) local physobj = propent:GetPhysicsObject() if not physobj:IsValid() then @@ -286,14 +268,14 @@ function props_library.createComponent(pos, ang, class, model, frozen) if not ply:CheckLimit("starfall_components") then SF.Throw("Limit of components reached!", 2) end plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) local comp = ents.Create(class) - register(comp, instance) comp:SetPos(pos) comp:SetAngles(ang) comp:SetModel(model) comp:Spawn() + entList:register(instance, comp) local mdl = comp:GetModel() if not mdl or mdl == "models/error.mdl" then @@ -373,7 +355,7 @@ function props_library.createSeat(pos, ang, model, frozen) model = SF.CheckModel(model, ply, true) plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) local class = "prop_vehicle_prisoner_pod" @@ -388,7 +370,7 @@ function props_library.createSeat(pos, ang, model, frozen) prop:SetKeyValue( "limitview", 0 ) prop:Activate() - register(prop, instance) + entList:register(instance, prop) local phys = prop:GetPhysicsObject() if phys:IsValid() then @@ -431,7 +413,7 @@ function props_library.createSent(pos, ang, class, frozen, data) local ply = instance.player plyPropBurst:use(ply, 1) - plyCount:checkuse(ply, 1) + entList:checkuse(ply, 1) local swep = list.GetForEdit("Weapon")[class] local sent = list.GetForEdit("SpawnableEntities")[class] @@ -650,7 +632,7 @@ function props_library.createSent(pos, ang, class, frozen, data) end if entity and entity:IsValid() then - register(entity, instance) + entList:register(instance, entity) if CPPI then entity:CPPISetOwner(ply == SF.Superuser and NULL or ply) end @@ -684,7 +666,7 @@ end -- @return boolean True if user can spawn props, False if not. function props_library.canSpawn() if not SF.Permissions.hasAccess(instance, nil, "prop.create") then return false end - return plyCount:check(instance.player) > 0 and plyPropBurst:check(instance.player) >= 1 + return entList:check(instance.player) > 0 and plyPropBurst:check(instance.player) >= 1 end --- Checks how many props can be spawned @@ -692,7 +674,7 @@ end -- @return number Number of props able to be spawned function props_library.propsLeft() if not SF.Permissions.hasAccess(instance, nil, "prop.create") then return 0 end - return math.min(plyCount:check(instance.player), plyPropBurst:check(instance.player)) + return math.min(entList:check(instance.player), plyPropBurst:check(instance.player)) end --- Returns how many props per second the user can spawn diff --git a/lua/starfall/sflib.lua b/lua/starfall/sflib.lua index 82fdb24d8..659f88a80 100644 --- a/lua/starfall/sflib.lua +++ b/lua/starfall/sflib.lua @@ -286,6 +286,48 @@ SF.LimitObject = { } setmetatable(SF.LimitObject, SF.LimitObject) +--- Returns a class that handles entities spawned by an instance +SF.EntManager = { + __index = { + register = function(self, instance, ent) + ent:CallOnRemove("starfall_entity_onremove", self.onremove, self, instance) + self.entsByInstance[instance][ent] = true + self:free(instance.player, -1) + end, + remove = function(self, instance, ent) + if ent:IsValid() then + -- The die function is called the next frame after 'Remove' which is too slow so call it ourself + local diefunc = ent.OnDieFunctions.starfall_entity_onremove + diefunc.Function(ent, unpack(diefunc.Args)) + ent:RemoveCallOnRemove("starfall_entity_onremove") + ent:Remove() + end + end, + onremove = function(ent, self, instance) + self.entsByInstance[instance][ent] = nil + self:free(instance.player, 1) + end, + clear = function(self, instance) + for ent in pairs(self.entsByInstance[instance]) do + self:remove(instance, ent) + end + end, + deinitialize = function(self, instance, shouldclear) + if shouldclear then + self:clear(instance) + end + self.entsByInstance[instance] = nil + end + }, + __call = function(p, ...) + local t = SF.LimitObject(...) + t.entsByInstance = setmetatable({},{__index = function(t,k) local r = {} t[k]=r return r end}) + return setmetatable(t, p) + end +} +setmetatable(SF.EntManager, SF.EntManager) +setmetatable(SF.EntManager.__index, SF.LimitObject) + --- Returns a class that can limit per player and recycle a indestructable resource SF.ResourceHandler = { __index = {