So I guess trying to avoid circular dependancies?
I want to be able to have 2 modules access eachother. Is this even possible?
If Module A requires Module B which requires Module A, both scripts will kind of yield since they are waiting for each other to finish
Any tips on getting around this, or on the proper way to setup modules so you dont run into this problem would be helpful
Why do they need to be able to access each other? The issue with making a general post like this is that it’s hard to work around a problem without knowing what it is that you’re actually looking for.
well an example is that I have a camera module and character module and they need to be able to access eachother. Character module needs to access camera module to get the cameras yaw to rotate the character, camera module needs to access character module to position the camera where the rootpart is
Why can’t you pass the information one module needs as a parameter to a function? For example, you could just pass the root as an argument to whatever function is updating camera position, and whatever script is requiring the two modules can pass it as needed.
Alternatively, you could use BindableFunctions to allow one module to communicate with the other (e.g. ask what the root part is) without needing to require the other. This is a slightly more awkward solution, but it should still work fine.
So, you can actually do this as long as you can guarantee that one module is required before the other:
-- Module1.lua
local Module2 = require(Path_To_Module2)
local Module1 = {}
function Module1.Print()
print("Module1")
end
return Module1
-- Module2.lua
local Module1
local Module2 = {}
function Module2.Print()
Module1.Print()
end
function Module2.RequireModule1()
Module1 = require(Path_To_Module1)
end
return Module2
-- Script.lua
local Module2 = require(Path_To_Module2)
Module2.RequireModule1()
Module2.Print()
I’ll leave the reason why this works as an exercise to the reader
A common solution is to make a single module that requires all others and puts their results into a table which is shared with all of the modules. This can be done by having each module return a callback which takes in the shared table or a global variable.
Anyways, this sort of cyclic dependency and tight state relationships is common in object oriented programming. If you search in my posts, you’ll find a wealth of anti-OOP material. Unfortunately the powers that be have pushed OOP beyond where it should have stopped. Do yourself a favor and learn to live without it: it yields little value beyond empty promises and ignorant friends.
I use a system in which a primary module loads all modules in my game first. Once it does that it calls an Init function for each module if it exists. Inside of this Init function you can then require the modules you need.
This avoids all circular dependency problems and allows you to specify load order if need be.
Im just wondering why you need to do this since its being done in a local script the character can easily be indexed from the player? so the modules dont need to require each other
Here’s what I typically do when organizing my code base. I’ll only be talking about the client in this post for the sake of brevity but the same can be done for the server.
I have a single local script called “ClientBootstrapper” which requires a list of distinctive service modules (things like a TradingService, AmbientEffectsService, UIService etc). These services can require more modules (such as modules that control each screen on the UI) and these modules I add as children of the service that’s requiring them for organization.
Each service has a .new constructor that initializes only the basic state of the service and a dependency safe initialization method that initializes the state of the service that depends on other services to be initialized first. This fixes the problem of circular dependency because all of the services are simply references in a table passed into the dependency safe initialization method. No need for requiring since each service is only required once. All utility modules and wrapper classes are stored in ReplicatedStorage.
Each service module manages a specific section of the game’s logic. They all have a constructor and an :Update method that runs once every Stepped (handled by the bootstrapper). For example, my TradingService module caches inbound trade requests, outbound trade requests and trade session data locally. My trading screen handler module organized under the UIService somewhere listens to events that the TradeService fires and displays the updated data on the UI.