Multi-Place
Step-by-step setup guide for sharing code across multiple places.
A place is one Roblox place in a repo that builds several. It's a config that extends a shared one and adds a folder of its own to the root directories, so core code is shared across places.
Structure the Repository
Keep shared code in src, and each place's own code in places/<name>.
Add the Place
Starting fresh, run rogen init and pick Several places that share code; with folders already in places/, that's the default. It asks the usual questions for the shared config, then the place names, and writes one config per place.
To add a place later, run rogen init again in a terminal and pick A place. It asks for the place's name and folder (places/lobby by default). Nothing else is asked: language, Darklua, routes and packages come from the existing project. Without a terminal, or with --no-input, rogen init lobby takes those defaults and adds the place without asking, which is how a script or a coding agent adds one:
rogen init # in a terminal, then pick A place
rogen init lobby --no-input # without askingA place folder may not overlap the shared root directories, and init never edits an existing file. What it writes depends on the toolchain:
One config:
{
"$schema": "https://ldgerrits.github.io/rogen/schema/2/rogen.json",
"extends": "./default.rogen.json",
"rootDirs": ["src", "places/lobby"]
}Rogen reads both root directories and merges them into one tree. If two files land on the same instance, the directory listed last wins, so the place can override shared code.
Start Watching
Name the place to build it:
rogen watch lobby
rojo serve lobby.project.jsonThe config's name names its project file: lobby.rogen.json writes lobby.project.json. rogen list shows every config the repo can build.
With Darklua
Each place needs a source-rooted project file for Darklua and a synced one for Studio, so a place is two configs: lobby extends default and adds the place's root directory, and lobby-sync extends lobby and adds a sync directory.
{
"$schema": "https://ldgerrits.github.io/rogen/schema/2/rogen.json",
"extends": "./default.rogen.json",
"rootDirs": ["src", "places/lobby"]
}{
"$schema": "https://ldgerrits.github.io/rogen/schema/2/rogen.json",
"extends": "./lobby.rogen.json",
"syncDir": "dist/lobby"
}The common root of src and places/lobby is the repo root, so the synced paths are dist/lobby/src/… and dist/lobby/places/lobby/…. Run Darklua once per root directory to match, keep the place's sourcemap current for Darklua's convert_require, and serve the synced project:
darklua process src dist/lobby/src
darklua process places/lobby dist/lobby/places/lobby
rogen watch lobby lobby-sync
rojo sourcemap lobby.project.json --output sourcemap.json --watch
rojo serve lobby-sync.project.jsonluau-lsp regenerates sourcemap.json from default.project.json, which holds only the shared code, and would overwrite the place's. While you work on a place, point luau-lsp at it with "luau-lsp.sourcemap.rojoProjectFile": "lobby.project.json", or turn off luau-lsp.sourcemap.autogenerate.
A merged source config for every place wouldn't work: Darklua rewrites requires against the sourcemap, so the sourcemap must be the place's own tree, and places that override each other's files would collapse into one.
Shared routes, tags and template live in the config that every place extends, so a place only states what differs.