diff --git a/lua/cfw/classes/contraption_sv.lua b/lua/cfw/classes/contraption_sv.lua index fb9bfc4..f0159de 100644 --- a/lua/cfw/classes/contraption_sv.lua +++ b/lua/cfw/classes/contraption_sv.lua @@ -1,7 +1,15 @@ -- Contraptions are an object to refer to a collection of connected entities -CFW.Contraptions = {} -CFW.Classes.Contraption = {} +local CFW = CFW + +local Contraptions = {} +local EntityContraptions = setmetatable({}, {__mode = "k"}) + +CFW.Contraptions = Contraptions +CFW.EntityContraptions = EntityContraptions + +local CLASS = {} +CFW.Classes.Contraption = CLASS function CFW.createContraption() local con = { @@ -13,7 +21,7 @@ function CFW.createContraption() created = CurTime(), } - setmetatable(con, CFW.Classes.Contraption) + setmetatable(con, CLASS) con:Init() @@ -21,32 +29,37 @@ function CFW.createContraption() end do -- Contraption getters and setters - local ENT = FindMetaTable("Entity") - local Entity_GetTable = ENT.GetTable - - -- MARCH 4/16/2026 - -- We're going to deprecate this function and remove it sometime within the next 3-4 months probably. - -- Appropriate announcement will be given out to potential consumers of the API soonish, and then I'll make this ErrorNoHaltWithStack - -- for a week or so, then remove it entirely. Use CFW_GetContraption as its replacement which is properly namespaced. - -- This function has caused headaches in other codebases (wiremod's cam controllers for example) and should've always been namedspaced... + local ENT = FindMetaTable("Entity") + if file.Exists("cfg/cfw_legacy_mode.cfg", "GAME") then + CFW.LEGACY_MODE = true + end function ENT:CFW_GetContraption() - local SelfTbl = Entity_GetTable(self) - if not SelfTbl then return nil end - return SelfTbl._contraption + return EntityContraptions[self] end - ENT.GetContraption = ENT.CFW_GetContraption -end + if os.time() < 1791417621 or CFW.LEGACY_MODE then + local nextWarning -do -- Class def - local CLASS = CFW.Classes.Contraption + function ENT:GetContraption() + if not CFW.LEGACY_MODE then + local t = os.time() + if not nextWarning or t >= nextWarning then + nextWarning = t + 60 + ErrorNoHaltWithStack("GetContraption is deprecated, use CFW_GetContraption instead. GetContraption will be removed on October 8th 2026.") + end + end + return self:CFW_GetContraption() + end + end +end +do CLASS.__index = CLASS function CLASS:Init() - CFW.Contraptions[self] = true + Contraptions[self] = true hook.Run("cfw.contraption.created", self) end @@ -59,7 +72,7 @@ do -- Class def end function CLASS:Add(ent) - ent._contraption = self + EntityContraptions[ent] = self self.ents[ent] = true self.count = self.count + 1 @@ -71,7 +84,7 @@ do -- Class def end function CLASS:Sub(ent) - ent._contraption = nil + EntityContraptions[ent] = nil self.ents[ent] = nil self.count = self.count - 1 @@ -102,7 +115,7 @@ do -- Class def hook.Run("cfw.contraption.removed", self) end - CFW.Contraptions[self] = nil + Contraptions[self] = nil end function CLASS:Defuse() @@ -127,6 +140,6 @@ do -- Class def local Tracked = self.entsbyclass[ClassName] if not Tracked then return false end - return next(Tracked) ~= nil -- I don't *THINK* we would ever get NULL here... + return next(Tracked) ~= nil end -end \ No newline at end of file +end diff --git a/lua/cfw/classes/family_sv.lua b/lua/cfw/classes/family_sv.lua index 88917bf..496d136 100644 --- a/lua/cfw/classes/family_sv.lua +++ b/lua/cfw/classes/family_sv.lua @@ -1,7 +1,15 @@ -- Families are collections of parented entities, you know, parents... children... -CFW.Classes.Family = {} -CFW.Families = {} +local CFW = CFW + +local Families = {} +local EntityFamilies = setmetatable({}, {__mode = "k"}) + +CFW.Families = Families +CFW.EntityFamilies = EntityFamilies + +local CLASS = {} +CFW.Classes.Family = CLASS function CFW.Classes.Family.create(ancestor) local fam = { @@ -14,7 +22,7 @@ function CFW.Classes.Family.create(ancestor) created = CurTime(), } - setmetatable(fam, CFW.Classes.Family) + setmetatable(fam, CLASS) fam:Init() fam:Add(ancestor, true) @@ -23,12 +31,10 @@ function CFW.Classes.Family.create(ancestor) end do -- Class def - local CLASS = CFW.Classes.Family - CLASS.__index = CLASS function CLASS:Init() - CFW.Families[self] = true + Families[self] = true local con = self.ancestor:CFW_GetContraption() if con then con.families[self] = true end @@ -48,14 +54,14 @@ do -- Class def hook.Run("cfw.family.deleted", self) - CFW.Families[self] = nil + Families[self] = nil end function CLASS:Add(entity, isAncestor) self.count = self.count + 1 self.ents[entity] = true - entity._family = self + EntityFamilies[entity] = self local className = entity:GetClass() self.entsbyclass[className] = self.entsbyclass[className] or {} @@ -81,7 +87,7 @@ do -- Class def self.count = self.count - 1 self.ents[entity] = nil - entity._family = nil + EntityFamilies[entity] = nil local entValid = IsValid(entity) local className = entValid and entity:GetClass() or "" @@ -124,7 +130,7 @@ do -- Class def local Tracked = self.entsbyclass[ClassName] if not Tracked then return false end - return next(Tracked) ~= nil -- I don't *THINK* we would ever get NULL here... + return next(Tracked) ~= nil end end @@ -132,16 +138,16 @@ do local ENT = FindMetaTable("Entity") function ENT:GetFamily() - return self._family + return EntityFamilies[self] end function ENT:GetAncestor() - local Family = self._family + local Family = EntityFamilies[self] return Family and Family.ancestor or self end function ENT:SetFamily(newFamily) - local oldFamily = self._family + local oldFamily = EntityFamilies[self] if oldFamily then oldFamily:Sub(self) @@ -159,7 +165,7 @@ do end function ENT:GetFamilyChildren() - local Family = self._family + local Family = EntityFamilies[self] return Family and Family.children or self:GetChildren() end -end \ No newline at end of file +end diff --git a/lua/cfw/classes/link_sv.lua b/lua/cfw/classes/link_sv.lua index ac38675..2e6744c 100644 --- a/lua/cfw/classes/link_sv.lua +++ b/lua/cfw/classes/link_sv.lua @@ -2,7 +2,14 @@ -- These are used to keep track of the number of connections between two entities -- In graph theory these are edges -CFW.Classes.Link = {} +local CFW = CFW + +local EntityLinks = setmetatable({}, {__mode = "k"}) + +CFW.EntityLinks = EntityLinks + +local CLASS = {} +CFW.Classes.Link = CLASS function CFW.createLink(a, b) local indexA, indexB = a:EntIndex(), b:EntIndex() @@ -17,20 +24,20 @@ function CFW.createLink(a, b) created = CurTime(), } - a._links = a._links or {} - b._links = b._links or {} + local linksA = EntityLinks[a] or {} + EntityLinks[a] = linksA + local linksB = EntityLinks[b] or {} + EntityLinks[b] = linksB - a._links[indexB] = link - b._links[indexA] = link + linksA[indexB] = link + linksB[indexA] = link - setmetatable(link, CFW.Classes.Link) + setmetatable(link, CLASS) return link:Init() end do -- Class def - local CLASS = CFW.Classes.Link - CLASS.__index = CLASS -- why? function CLASS:Init() @@ -55,9 +62,10 @@ do -- Class def local indexA, indexB = self.indexA, self.indexB if IsValid(entA) then - entA._links[indexB] = nil + local linksA = EntityLinks[entA] + linksA[indexB] = nil - if not next(entA._links) then + if not next(linksA) then -- It's important that the entity is removed from the family first, then the contraption in that order entA:SetFamily(nil) entA:CFW_GetContraption():Sub(entA) @@ -69,9 +77,10 @@ do -- Class def end if IsValid(entB) then - entB._links[indexA] = nil + local linksB = EntityLinks[entB] + linksB[indexA] = nil - if not next(entB._links) then + if not next(linksB) then entB:SetFamily(nil) entB:CFW_GetContraption():Sub(entB) @@ -89,11 +98,12 @@ do local ENT = FindMetaTable("Entity") function ENT:GetCFWLink(other) -- Returns the link object between this and other - return self._links and self._links[other:EntIndex()] or nil + local links = EntityLinks[self] + return links and links[other:EntIndex()] or nil end function ENT:GetCFWLinks() -- Creates a shallow copy of the links table - local links = self._links + local links = EntityLinks[self] local out = {} if links then @@ -104,4 +114,4 @@ do return out end -end \ No newline at end of file +end diff --git a/lua/cfw/core/connectivity_sv.lua b/lua/cfw/core/connectivity_sv.lua index 353a3f4..a9248ac 100644 --- a/lua/cfw/core/connectivity_sv.lua +++ b/lua/cfw/core/connectivity_sv.lua @@ -1,3 +1,5 @@ +local CFW = CFW + local function floodFill(source, sinkIndex) local closed = {[source:EntIndex()] = true} local closedCount = 0 @@ -13,7 +15,7 @@ local function floodFill(source, sinkIndex) if entIndex == sinkIndex then return true, closed end - for neighborIndex in pairs(Entity(entIndex)._links) do -- neighborIndex, neighborLink + for neighborIndex in pairs(CFW.EntityLinks[Entity(entIndex)]) do -- neighborIndex, neighborLink if not closed[neighborIndex] then open[neighborIndex] = true end @@ -66,7 +68,7 @@ function CFW.disconnect(entA, indexB) if entA:EntIndex() == indexB then return end -- Should not happen normally, but ragdolls allow you to constrain to other bones on the same ragdoll, and it is the same entity -- Don't soft error because if _links isn't present then it's a deeper CFW issue, nothing without a _links table should be able to reach this point at all - local links = entA._links + local links = CFW.EntityLinks[entA] if not links then ErrorNoHaltWithStack("Contraption Framework Error: Entity had no links. This error generally indicates a deeper problem with CFW.") end local link = links[indexB] diff --git a/lua/weapons/gmod_tool/stools/cfw_tool.lua b/lua/weapons/gmod_tool/stools/cfw_tool.lua index 73745a4..b0334d0 100644 --- a/lua/weapons/gmod_tool/stools/cfw_tool.lua +++ b/lua/weapons/gmod_tool/stools/cfw_tool.lua @@ -41,15 +41,15 @@ end function TOOL:Think() local ent = self.Entity if not IsValid(ent) then return end - local selftbl = ent:GetTable() + local links = CFW and CFW.EntityLinks and CFW.EntityLinks[ent] - if not selftbl._links then return end + if not links then return end local tick = engine.TickInterval() + 0.05 local Rendered = {} - for _, link in pairs(selftbl._links) do + for _, link in pairs(links) do local entA, entB = link.entA, link.entB debugoverlay.Line(entA:GetPos(),entB:GetPos(),tick,link.color,true) @@ -61,8 +61,8 @@ function TOOL:Think() if not Rendered[entB] then debugoverlay.Text(entB:GetPos(),"B",tick,false) + Rendered[entB] = true end end end -