From 12e044364e34692d3c188245142cad4b4fbea3fb Mon Sep 17 00:00:00 2001 From: LengthenedGradient Date: Thu, 29 Aug 2024 17:00:45 -0400 Subject: [PATCH] Remove Link Store, Link Symmetry and Fix Issues - Removes LinkStore variables - Link information is now stored in the direction registered, but retrieval will try either so to keep the way we link things the same - Link functions will now be called with an additional `FromChip` parameter which specifies if the linkage comes from a chip. - Links will now support a `ChipDelay` property which will apply the linkage a set time after the initial call. This should be used with the `RegisterClassCheck` function. --- lua/acf/core/utilities/util_sv.lua | 105 +++++++++--------- lua/entities/acf_base_scalable/init.lua | 40 +++++-- lua/entities/acf_base_simple/init.lua | 41 +++++-- .../core/custom/acffunctions.lua | 2 +- lua/starfall/libs_sh/acffunctions.lua | 2 +- 5 files changed, 116 insertions(+), 74 deletions(-) diff --git a/lua/acf/core/utilities/util_sv.lua b/lua/acf/core/utilities/util_sv.lua index 8b3d09682..677fb0c72 100644 --- a/lua/acf/core/utilities/util_sv.lua +++ b/lua/acf/core/utilities/util_sv.lua @@ -415,81 +415,78 @@ do -- Entity linking } } ]]-- - local ClassLink = { Link = {}, Unlink = {} } - - --- Registers a link or unlink between two classes and how to handle them. + --- @class LinkFunction + --- @field Ent1 table The first entity in the link + --- @field Ent2 table The second entity in the link + --- @field FromChip boolean If the link is from a chip + --- @return boolean? Success Whether the link was successful + --- @return string? Message A message about the link status + + --- @class LinkData + --- @field Link LinkFunction? The function to handle linking + --- @field Unlink LinkFunction? The function to handle unlinking + --- @field Check LinkFunction? The function to check the link status + --- @field ChipDelay number? The delay associated with the link if done via chip + + --- @type table> + local ClassLink = { } + + --- Initializes a link in the ClassLink table if it doesn't already exist and returns the result. + --- The Link is initialized directionally (InitLink(Class1,Class2) != InitLink(Class2,Class1)) --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) - local function RegisterNewLink(Action, Class1, Class2, Function) - if not isfunction(Function) then return end - - local Target = ClassLink[Action] - local Data1 = Target[Class1] - - if not Data1 then - Target[Class1] = { - [Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end - } - else - Data1[Class2] = function(Ent1, Ent2) - return Function(Ent1, Ent2) - end - end - - if Class1 == Class2 then return end - - local Data2 = Target[Class2] - - if not Data2 then - Target[Class2] = { - [Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end - } - else - Data2[Class1] = function(Ent2, Ent1) - return Function(Ent1, Ent2) - end - end + --- @return LinkData? LinkData The returned link + function ACF.InitLink(Class1, Class2) + if not ClassLink[Class1] then ClassLink[Class1] = {} end + if not ClassLink[Class1][Class2] then ClassLink[Class1][Class2] = {} end + return ClassLink[Class1][Class2] end - --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. + --- Attempts to retrieve link information from Class 1 to Class2, otherwise tries Class 2 to Class1. If link exists in either direction, return nil. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message - function ACF.RegisterClassLink(Class1, Class2, Function) - RegisterNewLink("Link", Class1, Class2, Function) + --- @return LinkData? LinkData The returned link + --- @return boolean Reversed Whether you should reverse your entity arguments when calling with entities + function ACF.GetClassLink(Class1, Class2) + if ClassLink[Class1] ~= nil and ClassLink[Class1][Class2] ~= nil then return ClassLink[Class1][Class2], false end + if ClassLink[Class2] ~= nil and ClassLink[Class2][Class1] ~= nil then return ClassLink[Class2][Class1], true end + return nil, false end - --- Returns the callback defined previously by ACF.RegisterClassLink between Class1 and Class2. + --- Registers that two classes can be linked, as well as how to handle entities of their class being linked. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The linking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no linking functions - function ACF.GetClassLink(Class1, Class2) - if not ClassLink.Link[Class1] then return end - - return ClassLink.Link[Class1][Class2] + --- @param Function LinkFunction The linking function defined between an entity of Class1 and an entity of Class2; this should always return a boolean for link status and a string for link message + function ACF.RegisterClassLink(Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1,Class2) + LinkData.Link = Function end --- Registers that two classes can be unlinked, as well as how to handle entities of their class being unlinked. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @param Function fun(Entity1:table, Entity2:table) The unlinking function defined between an entity of Class1 and an entity of Class2 + --- @param Function LinkFunction The unlinking function defined between an entity of Class1 and an entity of Class2 function ACF.RegisterClassUnlink(Class1, Class2, Function) - RegisterNewLink("Unlink", Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.Unlink = Function end - --- Returns the callback defined previously by ACF.RegisterClassUnlink between Class1 and Class2. + --- Registers a validation check between two classes. --- @param Class1 string The first class in the link --- @param Class2 string The other class in the link - --- @return fun(Entity1:table, Entity2:table) | nil # The unlinking function defined between an entity of Class1 and an entity of Class2, or nil if Class1 has no unlinking functions - function ACF.GetClassUnlink(Class1, Class2) - if not ClassLink.Unlink[Class1] then return end + --- @param Function LinkFunction The checking function defined between an entity of Class1 and an entity of Class2 + function ACF.RegisterClassCheck(Class1, Class2, Function) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.Check = Function + end - return ClassLink.Unlink[Class1][Class2] + --- Registers a chip delay between two classes. + --- @param Class1 string The first class in the link + --- @param Class2 string The other class in the link + --- @param ChipDelay number If the link happens from the chip, then delay it by this amount + function ACF.RegisterClassDelay(Class1, Class2, ChipDelay) + local LinkData = ACF.InitLink(Class1, Class2) + LinkData.ChipDelay = ChipDelay end end diff --git a/lua/entities/acf_base_scalable/init.lua b/lua/entities/acf_base_scalable/init.lua index d8f778074..b7903db0e 100644 --- a/lua/entities/acf_base_scalable/init.lua +++ b/lua/entities/acf_base_scalable/init.lua @@ -62,17 +62,35 @@ do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) + if Check then + local result, message = Check(A, B) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(A, B) then Function(A, B) end + end) + else Function(A,B) end + end + return result, message + end + return Function(A, B) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,12 +101,16 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + local Function = LinkData.Unlink + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultUnlink then - return self:DefaultUnlink(Target) + return Function(A, B) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/acf_base_simple/init.lua b/lua/entities/acf_base_simple/init.lua index d8f778074..c1410621a 100644 --- a/lua/entities/acf_base_simple/init.lua +++ b/lua/entities/acf_base_simple/init.lua @@ -62,17 +62,35 @@ do -- Entity linking and unlinking -------------- local LinkText = "%s can't be linked to %s." local UnlinkText = "%s can't be unlinked from %s." - function ENT:Link(Target) + function ENT:Link(Target, FromChip) if not IsValid(Target) then return false, "Attempted to link an invalid entity." end if self == Target then return false, "Can't link an entity to itself." end local Class = Target:GetClass() - local Function = ACF.GetClassLink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Link + local Check = LinkData.Check + local ChipDelay = LinkData.ChipDelay + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultLink then - return self:DefaultLink(Target) + if Check then + local result, message = Check(A, B) + if result then + if FromChip and ChipDelay then + timer.Simple(ChipDelay,function() + if Check(A, B) then Function(A, B) end + end) + else Function(A,B) end + end + return result, message + end + return Function(A, B) end return false, LinkText:format(self.PluralName, Target.PluralName or Class) @@ -83,12 +101,17 @@ do -- Entity linking and unlinking -------------- if self == Target then return false, "Can't unlink an entity from itself." end local Class = Target:GetClass() - local Function = ACF.GetClassUnlink(self:GetClass(), Class) + local LinkData, Reversed = ACF.GetClassLink(self:GetClass(), Class) + + if LinkData == nil then return false, "Links between these two entities are impossible" end + + local Function = LinkData.Unlink + + local A, B = self, Target -- Default argument order + if Reversed then A, B = Target, self end -- If reversed, reverse argument order if Function then - return Function(self, Target) - elseif self.DefaultUnlink then - return self:DefaultUnlink(Target) + return Function(A, B) end return false, UnlinkText:format(self.PluralName, Target.PluralName or Class) diff --git a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua index 5534336d3..087188819 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/acffunctions.lua @@ -348,7 +348,7 @@ e2function number entity:acfLinkTo(entity Target, number Notify) return 0 end - local Sucess, Message = this:Link(Target) + local Sucess, Message = this:Link(Target,true) if Notify ~= 0 then ACF.SendNotify(self.player, Sucess, Message) diff --git a/lua/starfall/libs_sh/acffunctions.lua b/lua/starfall/libs_sh/acffunctions.lua index a6560b0b7..35ed14fa6 100644 --- a/lua/starfall/libs_sh/acffunctions.lua +++ b/lua/starfall/libs_sh/acffunctions.lua @@ -1105,7 +1105,7 @@ if SERVER then CheckPerms(instance, This, "entities.acf") CheckPerms(instance, Target, "entities.acf") - local Success, Message = This:Link(Target) + local Success, Message = This:Link(Target,true) if notify then ACF.SendNotify(instance.player, Success, Message)