Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 37 additions & 24 deletions lua/cfw/classes/contraption_sv.lua
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -13,40 +21,45 @@ function CFW.createContraption()
created = CurTime(),
}

setmetatable(con, CFW.Classes.Contraption)
setmetatable(con, CLASS)

con:Init()

return con
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

Expand All @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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()
Expand All @@ -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
end
36 changes: 21 additions & 15 deletions lua/cfw/classes/family_sv.lua
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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 {}
Expand All @@ -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 ""
Expand Down Expand Up @@ -124,24 +130,24 @@ 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

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)
Expand All @@ -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
end
40 changes: 25 additions & 15 deletions lua/cfw/classes/link_sv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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)

Expand All @@ -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
Expand All @@ -104,4 +114,4 @@ do

return out
end
end
end
6 changes: 4 additions & 2 deletions lua/cfw/core/connectivity_sv.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local CFW = CFW

local function floodFill(source, sinkIndex)
local closed = {[source:EntIndex()] = true}
local closedCount = 0
Expand All @@ -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
Expand Down Expand Up @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions lua/weapons/gmod_tool/stools/cfw_tool.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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

Loading