From 65dba444eb51d310c360b2201ad152ccdb0fa038 Mon Sep 17 00:00:00 2001 From: itsrenderman <27890173+itsrenderman@users.noreply.github.com> Date: Tue, 26 May 2026 05:31:53 +0100 Subject: [PATCH 1/6] refactor: signal instead of busy-wait, debug call stack, immutable const bindings, string interpolation, linter fixes --- src/Overture.luau | 126 ++++++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/src/Overture.luau b/src/Overture.luau index 5616ded..80f8330 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -1,8 +1,8 @@ --!nonstrict --// Initialization -local RunService = game:GetService("RunService") -local CollectionService = game:GetService("CollectionService") +const RunService = game:GetService("RunService") +const CollectionService = game:GetService("CollectionService") --[=[ @class Overture @@ -17,9 +17,11 @@ local CollectionService = game:GetService("CollectionService") Remember, all library names in Overture must be unique! ::: ]=] -local Overture = {} -local LibraryThreadCache = {} -local Libraries: {[string]: ModuleScript} = {} +const Overture = {} +const LibrarySignalCache: { [string]: { BindableEvent } } = {} +const Libraries: { [string]: ModuleScript } = {} +const IsClient = RunService:IsClient() +local IsDebug = script:GetAttribute("Debug") == true --// Functions @@ -27,19 +29,24 @@ local Libraries: {[string]: ModuleScript} = {} @within Overture @ignore ]=] -local function Retrieve(InstanceName: string, InstanceClass: string, InstanceParent: Instance, ForceWait: boolean?): Instance +const function Retrieve( + InstanceName: string, + InstanceClass: string, + InstanceParent: Instance, + ForceWait: boolean? +): Instance if ForceWait and RunService:IsRunning() then return InstanceParent:WaitForChild(InstanceName) end - + local SearchInstance = InstanceParent:FindFirstChild(InstanceName) - + if not SearchInstance then SearchInstance = Instance.new(InstanceClass) SearchInstance.Name = InstanceName SearchInstance.Parent = InstanceParent end - + return SearchInstance end @@ -50,11 +57,11 @@ end @param Tag -- The CollectionService tag @param Function -- The function to call ]=] -local function BindToTag(Tag: string, Function: (Instance) -> ()): RBXScriptConnection +const function BindToTag(Tag: string, Function: (Instance) -> ()): RBXScriptConnection for _, Value in CollectionService:GetTagged(Tag) do task.spawn(Function, Value) end - + return CollectionService:GetInstanceAddedSignal(Tag):Connect(Function) end @@ -64,17 +71,16 @@ end @param Module -- The ModuleScript to require @param NamedImports -- When provided, returns the named variables instead of the entire ModuleScript - @return any? ]=] -local function RequireModule(Module: ModuleScript, NamedImports: {string}?) +const function RequireModule(Module: ModuleScript, NamedImports: { string }?): any? if NamedImports then - local Exports = require(Module) - local Imports = {} - + const Exports = require(Module) + const Imports = {} + for ImportIndex, ImportName in ipairs(NamedImports) do Imports[ImportIndex] = Exports[ImportName] end - + return unpack(Imports) else return require(Module) @@ -114,18 +120,35 @@ end @yields @param Index -- The name of the ModuleScript @param NamedImports -- When provided, returns the named variables instead of the entire ModuleScript - @return any? @error The library `Index` does not exist! -- Thrown on the server, if no ModuleScript exists with the given name. ]=] -function Overture:LoadLibrary(Index: string, NamedImports: {string}?) +function Overture:LoadLibrary(Index: string, NamedImports: { string }?): any? if Libraries[Index] then return RequireModule(Libraries[Index], NamedImports) else - assert(not RunService:IsServer(), "The library \"" .. Index .. "\" does not exist!") - - table.insert(LibraryThreadCache, {Thread = coroutine.running(), RequestedIndex = Index, RequestedAt = time()}) - return RequireModule(coroutine.yield(), NamedImports) + assert(IsClient, `The library "{Index}" does not exist!`) + + LibrarySignalCache[Index] = LibrarySignalCache[Index] or {} + const Signal = Instance.new("BindableEvent") + table.insert(LibrarySignalCache[Index], Signal) + + const CallStack = `{debug.traceback("Stack Begin")}Stack End` + local DebugThread: thread? + if IsDebug then + DebugThread = task.delay(5, function() + warn(`Infinite yield possible on 'Overture:LoadLibrary("{Index}")'`) + for _, line in CallStack:split("\n") do + print(`{line}`) + end + end) + end + + local Object = Signal.Event:Wait() + if DebugThread then + task.cancel(DebugThread) + end + return RequireModule(Object, NamedImports) end end @@ -145,12 +168,9 @@ end @yields @client @param ... any - @return any? ]=] -function Overture:LoadLibraryOnClient(...) - if RunService:IsClient() then - return self:LoadLibrary(...) - end +function Overture:LoadLibraryOnClient(...): any? + return if IsClient then self:LoadLibrary(...) else nil end --[=[ @@ -168,12 +188,9 @@ end @server @param ... any - @return any? ]=] -function Overture:LoadLibraryOnServer(...) - if RunService:IsServer() then - return self:LoadLibrary(...) - end +function Overture:LoadLibraryOnServer(...): any? + return if IsClient then nil else self:LoadLibrary(...) end --[=[ @@ -190,12 +207,12 @@ end @param Parent -- An optional override parent Instance. Useful for retrieving dependencies. ]=] function Overture:Get(InstanceClass: string, InstanceName: string, Parent: Instance?): Instance - local SetFolder = (Parent or Retrieve(InstanceClass, "Folder", script, RunService:IsClient())) - local Item = SetFolder:FindFirstChild(InstanceName) - + const SetFolder = (Parent or Retrieve(InstanceClass, "Folder", script, IsClient)) + const Item = SetFolder:FindFirstChild(InstanceName) + if Item then return Item - elseif RunService:IsServer() or not RunService:IsRunning() then + elseif not IsClient or not RunService:IsRunning() then return Retrieve(InstanceName, InstanceClass, SetFolder) else return SetFolder:WaitForChild(InstanceName) @@ -234,35 +251,26 @@ end @param Parent -- An optional override parent Instance. Useful for retrieving dependencies. ]=] function Overture:WaitFor(InstanceClass: string, InstanceName: string, Parent: Instance?): Instance - local IsClient = RunService:IsClient() - return (Parent or Retrieve(InstanceClass, "Folder", script, IsClient)):WaitForChild(InstanceName, if IsClient then math.huge else nil) + return (Parent or Retrieve(InstanceClass, "Folder", script, IsClient) :: any) --// silence linter + :WaitForChild(InstanceName, if IsClient then math.huge else nil) :: Instance end task.spawn(BindToTag, "oLibrary", function(Object) Libraries[Object.Name] = Object - - for _, Cached in LibraryThreadCache do - if Object.Name == Cached.RequestedIndex then - task.defer(Cached.Thread, Object) - task.delay(1, function() - table.remove(LibraryThreadCache, table.find(LibraryThreadCache, Cached)) - end) - end + + if not LibrarySignalCache[Object.Name] then + return end -end) -task.spawn(function() - while script:GetAttribute("Debug") do - task.wait(1) - - for _, Cached in LibraryThreadCache do - if Cached.WarningEmitted then continue end - if (time() - Cached.RequestedAt) > 5 then - warn(string.format([[Infinite yield possible on Overture:LoadLibrary("%s").]], Cached.RequestedIndex)) - Cached.WarningEmitted = true - end - end + for _, signal in LibrarySignalCache[Object.Name] do + signal:Fire(Object) end + + LibrarySignalCache[Object.Name] = nil +end) + +script:GetAttributeChangedSignal("Debug"):Connect(function() + IsDebug = script:GetAttribute("Debug") == true end) --// Triggers From 1a61eda23c673db9b593f81213eb38629fa1c4ba Mon Sep 17 00:00:00 2001 From: devSparkle Date: Fri, 19 Jun 2026 16:36:55 +0100 Subject: [PATCH 2/6] Linting --- src/Overture.luau | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Overture.luau b/src/Overture.luau index 80f8330..804a12e 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -18,10 +18,12 @@ const CollectionService = game:GetService("CollectionService") ::: ]=] const Overture = {} + const LibrarySignalCache: { [string]: { BindableEvent } } = {} const Libraries: { [string]: ModuleScript } = {} + const IsClient = RunService:IsClient() -local IsDebug = script:GetAttribute("Debug") == true +local IsDebug = (script:GetAttribute("Debug") == true) --// Functions @@ -38,15 +40,15 @@ const function Retrieve( if ForceWait and RunService:IsRunning() then return InstanceParent:WaitForChild(InstanceName) end - + local SearchInstance = InstanceParent:FindFirstChild(InstanceName) - + if not SearchInstance then SearchInstance = Instance.new(InstanceClass) SearchInstance.Name = InstanceName SearchInstance.Parent = InstanceParent end - + return SearchInstance end @@ -61,7 +63,7 @@ const function BindToTag(Tag: string, Function: (Instance) -> ()): RBXScriptConn for _, Value in CollectionService:GetTagged(Tag) do task.spawn(Function, Value) end - + return CollectionService:GetInstanceAddedSignal(Tag):Connect(Function) end @@ -76,11 +78,11 @@ const function RequireModule(Module: ModuleScript, NamedImports: { string }?): a if NamedImports then const Exports = require(Module) const Imports = {} - + for ImportIndex, ImportName in ipairs(NamedImports) do Imports[ImportIndex] = Exports[ImportName] end - + return unpack(Imports) else return require(Module) @@ -128,11 +130,11 @@ function Overture:LoadLibrary(Index: string, NamedImports: { string }?): any? return RequireModule(Libraries[Index], NamedImports) else assert(IsClient, `The library "{Index}" does not exist!`) - + LibrarySignalCache[Index] = LibrarySignalCache[Index] or {} const Signal = Instance.new("BindableEvent") table.insert(LibrarySignalCache[Index], Signal) - + const CallStack = `{debug.traceback("Stack Begin")}Stack End` local DebugThread: thread? if IsDebug then @@ -143,7 +145,7 @@ function Overture:LoadLibrary(Index: string, NamedImports: { string }?): any? end end) end - + local Object = Signal.Event:Wait() if DebugThread then task.cancel(DebugThread) @@ -209,7 +211,7 @@ end function Overture:Get(InstanceClass: string, InstanceName: string, Parent: Instance?): Instance const SetFolder = (Parent or Retrieve(InstanceClass, "Folder", script, IsClient)) const Item = SetFolder:FindFirstChild(InstanceName) - + if Item then return Item elseif not IsClient or not RunService:IsRunning() then @@ -257,22 +259,22 @@ end task.spawn(BindToTag, "oLibrary", function(Object) Libraries[Object.Name] = Object - + if not LibrarySignalCache[Object.Name] then return end - + for _, signal in LibrarySignalCache[Object.Name] do signal:Fire(Object) end - + LibrarySignalCache[Object.Name] = nil end) +--// Triggers + script:GetAttributeChangedSignal("Debug"):Connect(function() - IsDebug = script:GetAttribute("Debug") == true + IsDebug = (script:GetAttribute("Debug") == true) end) ---// Triggers - return Overture From 63621725e3dd6c5c919da0cf54a61b9951040736 Mon Sep 17 00:00:00 2001 From: devSparkle Date: Fri, 19 Jun 2026 16:41:24 +0100 Subject: [PATCH 3/6] Prevent error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `task.cancel` will throw an error if the given thread cannot be canceled, such as when the thread has already run its course — a scenario which would happen if a library took more than 5s to replicate. --- src/Overture.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Overture.luau b/src/Overture.luau index 804a12e..5907e0f 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -148,7 +148,7 @@ function Overture:LoadLibrary(Index: string, NamedImports: { string }?): any? local Object = Signal.Event:Wait() if DebugThread then - task.cancel(DebugThread) + pcall(task.cancel, DebugThread) end return RequireModule(Object, NamedImports) end From e9d19f5ef0c10ef592c43abe99fd207c064b0047 Mon Sep 17 00:00:00 2001 From: devSparkle Date: Fri, 19 Jun 2026 16:50:49 +0100 Subject: [PATCH 4/6] Lint --- src/Overture.luau | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Overture.luau b/src/Overture.luau index 5907e0f..0676442 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -19,12 +19,16 @@ const CollectionService = game:GetService("CollectionService") ]=] const Overture = {} -const LibrarySignalCache: { [string]: { BindableEvent } } = {} -const Libraries: { [string]: ModuleScript } = {} +const LibrarySignalCache: {[string]: {BindableEvent}} = {} +const Libraries: {[string]: ModuleScript} = {} const IsClient = RunService:IsClient() local IsDebug = (script:GetAttribute("Debug") == true) +--// Variables + +const YIELD_WARN_DELAY = 5 + --// Functions --[=[ @@ -125,31 +129,35 @@ end @error The library `Index` does not exist! -- Thrown on the server, if no ModuleScript exists with the given name. ]=] -function Overture:LoadLibrary(Index: string, NamedImports: { string }?): any? +function Overture:LoadLibrary(Index: string, NamedImports: {string}?): any? if Libraries[Index] then return RequireModule(Libraries[Index], NamedImports) else assert(IsClient, `The library "{Index}" does not exist!`) - LibrarySignalCache[Index] = LibrarySignalCache[Index] or {} + LibrarySignalCache[Index] = (LibrarySignalCache[Index] or {}) const Signal = Instance.new("BindableEvent") table.insert(LibrarySignalCache[Index], Signal) const CallStack = `{debug.traceback("Stack Begin")}Stack End` - local DebugThread: thread? - if IsDebug then - DebugThread = task.delay(5, function() - warn(`Infinite yield possible on 'Overture:LoadLibrary("{Index}")'`) - for _, line in CallStack:split("\n") do - print(`{line}`) - end - end) + local DebugThread: thread? do + if IsDebug then + DebugThread = task.delay(YIELD_WARN_DELAY, function() + warn(`Infinite yield possible on 'Overture:LoadLibrary("{Index}")'`) + + for _, line in CallStack:split("\n") do + print(`{line}`) + end + end) + end end local Object = Signal.Event:Wait() + if DebugThread then pcall(task.cancel, DebugThread) end + return RequireModule(Object, NamedImports) end end @@ -264,8 +272,8 @@ task.spawn(BindToTag, "oLibrary", function(Object) return end - for _, signal in LibrarySignalCache[Object.Name] do - signal:Fire(Object) + for _, Signal in LibrarySignalCache[Object.Name] do + Signal:Fire(Object) end LibrarySignalCache[Object.Name] = nil From 55eeb9156ad48a28f29a944495ef7ae1d2f58442 Mon Sep 17 00:00:00 2001 From: devSparkle Date: Fri, 19 Jun 2026 16:51:13 +0100 Subject: [PATCH 5/6] Minor performance improvement --- src/Overture.luau | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Overture.luau b/src/Overture.luau index 0676442..5d8a073 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -145,8 +145,8 @@ function Overture:LoadLibrary(Index: string, NamedImports: {string}?): any? DebugThread = task.delay(YIELD_WARN_DELAY, function() warn(`Infinite yield possible on 'Overture:LoadLibrary("{Index}")'`) - for _, line in CallStack:split("\n") do - print(`{line}`) + for _, Line in string.split(CallStack, "\n") do + print(Line) end end) end From 43c4213f74489ec6220e06464f3e71c64f1087a6 Mon Sep 17 00:00:00 2001 From: devSparkle Date: Fri, 19 Jun 2026 16:59:29 +0100 Subject: [PATCH 6/6] Migrate to LogService --- src/Overture.luau | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Overture.luau b/src/Overture.luau index 5d8a073..e96249d 100644 --- a/src/Overture.luau +++ b/src/Overture.luau @@ -1,6 +1,7 @@ --!nonstrict --// Initialization +const LogService = game:GetService("LogService") const RunService = game:GetService("RunService") const CollectionService = game:GetService("CollectionService") @@ -143,10 +144,12 @@ function Overture:LoadLibrary(Index: string, NamedImports: {string}?): any? local DebugThread: thread? do if IsDebug then DebugThread = task.delay(YIELD_WARN_DELAY, function() - warn(`Infinite yield possible on 'Overture:LoadLibrary("{Index}")'`) + LogService:Warn([[Infinite yield possible on 'Overture:LoadLibrary("{LibraryName}")']], { + LibraryName = Index + }) for _, Line in string.split(CallStack, "\n") do - print(Line) + LogService:Info(Line) end end) end