Roam
Roam is a systems bootstrapping tool for Roblox luau projects, designed to make it easy to initialize and start services in a topologically sorted manner without the need to manually order and start services.
Roam follows a design pattern similar to Knit, but is more lightweight. It removes all networking and replication functionality, and instead focuses on providing a simple methodology to easily initialize Services given to it.
Roam gathers a collection of specified services and initializes 'syncronously'. Once all services have been fully initialized, it then starts them 'asyncronously' by spawning their 'RoamStart' method in a new thread.
Roam is RunContext agnostic, meaning it can be used on both the server and client in the same manner. It makes no distinction between the two, and instead focuses on providing a simple interface for initializing and starting services. This means you could create a service and register it on both the server and client, and it will be initialized and started on both ends.
[CONTRACTS]
- Services must be created/registered before Roam is started.
- Services must be created/registered with a unique name.
-
Services with
RoamInit
andRoamStart
methods will have those methods called when Roam is started at the appropriate time. (Names are configurable) RequiredServices
boot in proper topological order if specified in the ServiceConfig.- Roam functions the same regardless of RunContext (Server/Client).
[EXAMPLE STARTUP]
local Roam = require(ReplicatedStorage.Roam)
-- Just iterates through all the children of the given parents
-- and requires any module scripts that match the given predicate
Roam.requireModules({
ReplicatedStorage.Shared;
ServerScriptService.Server;
})
-- Start Roam
Roam.start()
:andThenCall(print, "Roam started!")
:catch(warn)
Setting up Services
Services can be set up in a variety of ways. The most common way is to create a ModuleScript that returns a table with the methods you want to define, and then register it with Roam just prior to the final module's return.
See Roam.registerService for more information on setting up a new service.
Networking
Roam does not inherently have networking functionality. However, it can easily be added through the use of NetWire's .setupServiceNetworking funtion.
Types
ServiceConfig
interface
ServiceConfig {
Name:
string
--
Name of the Service. Must be unique. Used when accessing via .getService
RequiredServices:
{
Service
}
?
--
The Services that this Service depends on. Roam will ensure that these Services are initialized before this Service.
StartMethodName:
string?
--
Overrides default StartMethodName of "RoamStart"
InitMethodName:
string?
--
Overrides default InitMethodName of "RoamInit"
}
local myOtherService = require(ReplicatedStorage.MyOtherService)
-------------------------------------------------
local MyService = {}
function MyService:CustomStartMethod()
print("MyService started!")
end
-------------------------------------------------
Roam.registerService(MyService, {
Name = "MyService",
RequiredServices = {myOtherService},
StartMethodName = "CustomStartMethod",
})
return MyService
Deffering RequiredServices
Do NOT add services to the RequiredServices after you have created or registered the service. This will cause undefined behavior.
Properties
Debug
Roam.Debug:
boolean
Whether or not to print debug messages. Default is false.
Bootstrappers
A table of generic bootstrappers for Roam that you can use to quickly setup new projects.
local Roam = require(Packages.Roam)
Roam.Bootstrappers.Server(script)
:andThenCall(print, "Roam Server Bootstrapped!")
Functions
registerService
Registers a Service/Table with Roam to be Initialized and Started when Roam starts. Cannot be called after Roam has been started.
local MyRegisteredService = {}
function MyRegisteredService:RoamStart()
print("MyRegisteredService started!")
end
function MyRegisteredService:RoamInit()
print("MyRegisteredService initialized!")
end
----------------------------------------------------------------
local Roam = require(Packages.Roam)
Roam.registerService(MyRegisteredService, "MyRegisteredService")
return MyRegisteredService
start
Roam.
start
(
postInitPreStart:
(
(
)
→
(
Promise?
)
)
?
) →
Promise
Starts Roam. Should only be called once. Calling multiple times will result in a promise rejection.
Optional argument postInitPreStart
is a function that is called
after all services have been initialized, but before they are started.
Roam.start()
:andThenCall(print, "Roam started!")
:catch(warn)
caution
Be sure that all services have been created before
calling Start
. Services cannot be added later.
Bootstrapping
You can use the Roam.Bootstrappers table/methods to quickly bootstrap Roam in your project. This is reccomended as it will provide a consistent starting point for your projects.
onStart
Roam.
onStart
(
) →
Promise
Returns a promise that is resolved once Roam has started. This is useful
for any code that needs to tie into Roam services but is not the script
that called Start
.
Roam.onStart():andThen(function()
local MyService = Roam.Services.MyService
MyService:DoSomething()
end):catch(warn)
isReady
Roam.
isReady
(
) →
boolean
Returns whether or not Roam has been successfully started and is ready for external access.
requireModules
Roam.
requireModules
(
config:
{
DeepSearch:
boolean?
,
}
?
) →
{
Service
}
Requires all the modules that are children of the given parent. This is an easy way to quickly load all services that might be in a folder. Takes an optional predicate function to filter which modules are loaded. Services collected this way must not yield.
DeepSearch
-> whether it checks descendants or just childrenRequirePredicate
-> a predicate function that determines whether a module should be requiredIgnoreDescendantsPredicate
-> A Predicate for whether the Descendants of the Module should be Searched (Only matters if DeepSearch is true)
local pred = function(obj: ModuleScript): boolean
return obj.Name:match("Service$") ~= nil
end
Roam.requireModules(ReplicatedStorage.Shared, {
DeepSearch = true,
RequirePredicate = pred,
IgnoreDescendantsPredicate = function(obj: Instance): boolean
return obj.Name == "Ignore"
end,
})
getNameFromService
Roam.
getNameFromService
(
service:
Service
) →
string
Fetches the name of a registered Service.