Project Structure
A well-organized project keeps server, client, and shared code cleanly separated so Riptide can load them in the right order. Here is the recommended layout for a small-to-medium game.
Recommended Folder Layout
Section titled “Recommended Folder Layout”my-game/ ← your repository root│├── luau_packages/ ← managed by pesde (don't edit manually)│ └── Riptide/│├── src/│ ├── shared/ ← code shared by both server and client│ │ ├── CurrencyConfig.lua│ │ └── ItemDatabase.lua│ ││ ├── server/│ │ ├── main.server.lua ← SERVER entry point│ │ └── Services/ ← server-only modules│ │ ├── DataService.lua│ │ ├── Economy/│ │ │ └── CoinsService.lua│ │ └── MatchService.lua│ ││ └── client/│ ├── main.client.lua ← CLIENT entry point│ └── Controllers/ ← client-only modules│ ├── UIController.lua│ └── InputController.lua│└── default.project.json ← Rojo project fileInside Roblox Studio (after Rojo sync), this maps to:
ReplicatedStorage/├── Packages/ ← luau_packages (Riptide lives here)└── SharedModules/ ← src/shared
ServerScriptService/├── main ← src/server/main.server.lua (Script)└── Services/ ← src/server/Services/ (Folder)
StarterPlayer/StarterPlayerScripts/├── main ← src/client/main.client.lua (LocalScript)└── Controllers/ ← src/client/Controllers/ (Folder)Folder Purposes
Section titled “Folder Purposes”| Folder | Where it lives in Studio | Passed to Launch as | Purpose |
|---|---|---|---|
Services/ | ServerScriptService | ModulesFolder (server) | Server-only logic — data, economy, combat |
Controllers/ | StarterPlayerScripts | ModulesFolder (client) | Client-only logic — UI, input, camera |
SharedModules/ | ReplicatedStorage | SharedModulesFolder (both) | Code used by both sides — configs, utilities |
Components/ | ReplicatedStorage (optional) | ComponentsFolder (both) | CollectionService component classes |
Packages/ | ReplicatedStorage | — | External dependencies (require() directly) |
Entry Scripts
Section titled “Entry Scripts”Server — src/server/main.server.lua
Section titled “Server — src/server/main.server.lua”--!strictlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local ServerScriptService = game:GetService("ServerScriptService")
local Riptide = require(ReplicatedStorage.Packages.Riptide).Server
Riptide.Launch({ ModulesFolder = ServerScriptService.Services, SharedModulesFolder = ReplicatedStorage.SharedModules, -- optional ComponentsFolder = ReplicatedStorage.Components, -- optional})Client — src/client/main.client.lua
Section titled “Client — src/client/main.client.lua”--!strictlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local Players = game:GetService("Players")
local Riptide = require(ReplicatedStorage.Packages.Riptide).Client
Riptide.Launch({ ModulesFolder = Players.LocalPlayer.PlayerScripts.Controllers, SharedModulesFolder = ReplicatedStorage.SharedModules, -- optional ComponentsFolder = ReplicatedStorage.Components, -- optional})Multi-Folder Setup
Section titled “Multi-Folder Setup”Both ModulesFolder and SharedModulesFolder accept an array of folders, making it easy to split large codebases across multiple directories:
Riptide.Launch({ ModulesFolder = { ServerScriptService.CoreServices, ServerScriptService.GameplayServices, ServerScriptService.AdminServices, }, SharedModulesFolder = { ReplicatedStorage.SharedModules, ReplicatedStorage.LibModules, },})Modules from each folder are discovered in array order, then deduplicated.
Adding a Component Folder
Section titled “Adding a Component Folder”If you use ComponentService for CollectionService-based components, create a folder (e.g. Components/) in ReplicatedStorage and pass it via ComponentsFolder:
ReplicatedStorage/└── Components/ ├── Lava.lua ← kills players on touch └── SpeedPad.lua ← boosts player walk speedRiptide.Launch({ ModulesFolder = ServerScriptService.Services, ComponentsFolder = ReplicatedStorage.Components,})See the Component Service reference for details.
Next Steps
Section titled “Next Steps”- Module Lifecycle — understand how Riptide loads and starts your modules.
- Network — communicate between server and client.