### Install via Wally Source: https://roexpress.dev/docs/pages/installation.html Add the dependency to your wally.toml file and run the install command. ```toml [dependencies] RoExpress = "unofficialrobloxtutor/roexpress@2.5.0" ``` ```bash wally install ``` -------------------------------- ### Full Installer Script Source: https://roexpress.dev/docs/pages/installation.html The source code for the installation script that fetches modules and sets up the RemoteEvents. ```lua local HttpService = game:GetService("HttpService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local BRANCH = "main" local BASE = "https://raw.githubusercontent.com/unofficialrobloxtutor/RoExpress/" .. BRANCH .. "/src/" local function fetch(path) local ok, r = pcall(HttpService.GetAsync, HttpService, BASE .. path) if not ok then error("[RoExpress] Failed to fetch " .. path) end return r end local function ms(name, source, parent) local m = Instance.new("ModuleScript") m.Name, m.Source, m.Parent = name, source, parent return m end local existing = ReplicatedStorage:FindFirstChild("RoExpress") if existing then existing:Destroy() end print("[RoExpress] Downloading from GitHub (" .. BRANCH .. ")...") local root = ms("RoExpress", fetch("init.luau"), ReplicatedStorage) -- Pre-bake the RemoteEvents so they exist before any script requires the package local re = Instance.new("RemoteEvent") re.Name, re.Parent = "RoExpressRemote", root local ure = Instance.new("UnreliableRemoteEvent") ure.Name, ure.Parent = "RoExpressUnreliable", root for _, name in { "App","Base64","Benchmark","Bridge","Broadcast","Cross","Hook","RTTP", "Listener","Network","Port","Promise","Router","Tamper", "TokenBucket","TypeCoercer","Types","Version" } do ms(name, fetch(name .. ".luau"), root) end local codec = ms("Codec", fetch("Codec/init.luau"), root) ms("LZ77", fetch("Codec/LZ77.luau"), codec) ms("LZH", fetch("Codec/LZH.luau"), codec) local stream = ms("Stream", fetch("Stream/init.luau"), root) ms("Channel", fetch("Stream/Channel.luau"), stream) ms("Schema", fetch("Stream/Schema.luau"), stream) ms("Types", fetch("Stream/Types.luau"), stream) print("[RoExpress] Done -> ReplicatedStorage/RoExpress (28 modules, 2 remotes)") ``` -------------------------------- ### Round Start Event Implementation Source: https://roexpress.dev/docs/pages/push-methods.html Example of broadcasting round data to all players and handling the event on the client. ```lua -- Server: fire round.start to all players local function startRound(roundData) app:PushAll("round.start", { map = roundData.map, duration = roundData.duration, }) end -- Client: listen for it local listener = RoExpress("Listener") listener:On("round.start", function(data) showCountdown(data.duration) loadMap(data.map) end) ``` -------------------------------- ### Install RoExpress via Wally Source: https://roexpress.dev/docs Add the dependency to your wally.toml file and run the install command. ```toml copy[dependencies] RoExpress = "unofficialrobloxtutor/roexpress@2.5.0" ``` ```bash copywally install ``` -------------------------------- ### app:Get(route, handler, options?) Source: https://roexpress.dev/docs/pages/app-routing.html Registers a GET route handler. ```APIDOC ## GET app:Get(route, handler, options?) ### Description Registers a handler for GET requests. ### Parameters - **route** (string) - Required - Route pattern (supports typed params, wildcards, globs). - **handler** (function) - Required - Function signature (req, res) or (Player, Payload, req, res). - **options.compress** (boolean) - Optional - Enable LZ77 Deflate on the response. ``` -------------------------------- ### Full Resolve and Reject Handling Source: https://roexpress.dev/docs/pages/promise-create.html A complete example demonstrating both success and error handling for a POST request. ```lua network:PostAsync("shop/buy", { itemId = 42 }) :Then(function(res) -- server called res:Status(200):Send({ newBalance = 850 }) CoinDisplay:Set(res.data.newBalance) end) :Catch(function(err) if err.type == "error" then -- server called res:Error("Not enough coins") UI:ShowError(err.message) elseif err.type == "timeout" then UI:ShowError("Server not responding — try again") end end) ``` -------------------------------- ### Webhook implementation example Source: https://roexpress.dev/docs/pages/rttp-methods.html Demonstrates initializing a new RTTP instance for a webhook and sending a POST request with a JSON body. ```lua local webhook = RTTP.New({ base = DISCORD_WEBHOOK_URL }) local function NotifyRoundEnd(winner: string, kills: number) webhook:Post("", { content = string.format("Round over! Winner: **%s** (%d kills)", winner, kills) }) end ``` -------------------------------- ### Install via Studio Command Bar Source: https://roexpress.dev/docs/pages/installation.html Paste this line into the Roblox Studio command bar to automatically download and install the library into ReplicatedStorage. ```lua local H=game:GetService"HttpService";loadstring(H:GetAsync"https://raw.githubusercontent.com/unofficialrobloxtutor/RoExpress/main/install.lua")() ``` -------------------------------- ### Install RoExpress via Studio Command Bar Source: https://roexpress.dev/docs Use this command in the Roblox Studio command bar for a quick installation. ```lua copylocal H=game:GetService"HttpService";loadstring(H:GetAsync"https://raw.githubusercontent.com/unofficialrobloxtutor/RoExpress/main/install.lua")() ``` -------------------------------- ### GET and POST Request Benchmarking Source: https://roexpress.dev/docs/pages/benchmark-run.html Demonstrates how to run benchmarks using RoExpress main channels, named ports, and vanilla RemoteFunctions. ```lua local bench = RoExpress("Benchmark") -- RoExpress main channel local result = bench:RunAndPrint("player/123", { method = "GET", iterations = 30, warmup = 5, label = "player GET", }) -- Named port channel local combatNet = RoExpress("Network", "combat") local portResult = bench:RunOnPort(combatNet, "shoot/123", { method = "POST", data = { weapon = "AK47" }, label = "combat POST shoot", }) bench:Print(portResult) -- Vanilla RemoteFunction round-trip local rs = game:GetService("ReplicatedStorage") local echoFunc = rs:WaitForChild("EchoFunc") local rfResult = bench:RunRemoteFunction(echoFunc, { args = { "payload" }, iterations = 30, label = "vanilla RF echo", }) bench:Print(rfResult) ``` -------------------------------- ### Implement MiddlewareHandler Source: https://roexpress.dev/docs/pages/types-response.html Example of an authentication guard middleware. ```Luau -- Auth guard example app:Use(function(player, payload) if not isAuthorized(player) then return false -- blocked | client receives 403 end -- return nil / true to pass through end) ``` -------------------------------- ### Pattern Usage Examples Source: https://roexpress.dev/docs/pages/ex-round.html Quick reference for specific API usage patterns within the round manager. ```lua app:PushAll("round.start") ``` ```lua broadcast:EmitAll("round.tick") ``` ```lua bridge.Fire("round.began") ``` ```lua app.TokenBucket:GrantAll(5) ``` -------------------------------- ### Leaderboard payload compression example Source: https://roexpress.dev/docs/pages/codec-routes.html Demonstrates server-side compression for large payloads and transparent client-side consumption. ```lua -- Server: large leaderboard payload, compress on the way out app:Get("leaderboard/top100", function(req, res) local entries = BuildTop100() -- big table res:Send(entries) end, { compress = true }) -- Client: identical call, transparent decompression network:Get("leaderboard/top100", nil, function(res) RenderLeaderboard(res.data) end) ``` -------------------------------- ### Project Directory Structure Source: https://roexpress.dev/docs/pages/guide-routes.html Example file organization for a RoExpress project utilizing both route handler patterns. ```text copyServerScriptService/ └── Server.luau -- requires app, registers middleware, calls each Register() └── Handlers/ ├── ShopHandlers.luau -- Style A: table ├── CombatHandlers.luau -- Style B: Register(app) └── PlayerHandlers.luau -- Style B: Register(app) ReplicatedStorage/ ├── Routes.luau -- all route strings as named constants └── RoExpress/ -- the framework StarterPlayerScripts/ └── Client.luau -- requires Routes, calls network:Get(Routes.SHOP_ITEMS) ``` -------------------------------- ### Perform GET Requests Source: https://roexpress.dev/docs/pages/network-get.html Fetches resources from the server using the network:Get method, supporting both direct routes and query strings. ```lua -- basic GET network:Get("player/123", nil, function(res) if res.type == "success" then print(res.data.coins) else warn(res.message) end end) -- with query string network:Get("shop/items?category=weapons&page=1", nil, function(res) populateShop(res.data) end) ``` -------------------------------- ### Use typed route parameters Source: https://roexpress.dev/docs/pages/router-params.html Examples of capturing various data types including integers, booleans, vectors, and enums. ```lua -- Integer item ID app:Get("shop/:itemId=int", function(req, res) print(req.params.itemId) -- always a whole number end) -- Boolean flag app:Get("flag/:on=boolean", function(req, res) print(req.params.on) -- true or false end) -- Vector3 position app:Get("pos/:v=vector3", function(req, res) print(req.params.v) -- Vector3 value end) -- Specific Enum type app:Get("cam/:mode=Enum.CameraType", function(req, res) print(req.params.mode) -- EnumItem end) ``` -------------------------------- ### Implement route handler with Response Source: https://roexpress.dev/docs/pages/types-response.html Example of using the Response object to send data or error statuses. ```Luau app:Get("player/:id", function(req, res) local player = Players:GetPlayerByUserId(req.params.id) if not player then res:Status(404):Error("player not found") return end res:Send({ name = player.Name }) end) ``` -------------------------------- ### network:Get(route, data?, callback) Source: https://roexpress.dev/docs/pages/network-get.html Fetches a resource from the server. Query-string parameters can be included directly in the route string. ```APIDOC ## network:Get(route, data?, callback) ### Description Fetches a resource from the server. Pass query-string parameters directly in the route string. ### Parameters - **route** (string) - Required - Route string matching a registered server handler. - **data** (any?) - Optional - Payload sent with the request. - **callback** ((res: NetworkResponse) -> ()) - Required - Function called when the server responds or times out. ### Returns - **requestId** (number) - Unique identifier for the request, used for cancellation. ### Example ```lua network:Get("shop/items?category=weapons&page=1", nil, function(res) populateShop(res.data) end) ``` ``` -------------------------------- ### Handle Round Lifecycle Events Source: https://roexpress.dev/docs/pages/listener-push.html Example of subscribing to multiple round-related events using the listener. ```lua local listener = RoExpress("Listener") listener:On("round.start", function(data) print("Round starting — duration:", data.duration) HUD:StartTimer(data.duration) HUD:ShowMessage("Round started!") end) listener:On("round.end", function(data) print("Round over — winner:", data.winner) HUD:StopTimer() HUD:ShowWinner(data.winner) end) ``` -------------------------------- ### TypeCoercer Wire Format Examples Source: https://roexpress.dev/docs/pages/typecoercer-types.html Demonstrates how to use the Coerce method to convert string inputs into specific Luau types like Vector3, Color3, and CFrame. ```lua local TC = RoExpress.TypeCoercer TC.Coerce("10,5,-20", "vector3") -- Vector3.new(10, 5, -20) TC.Coerce("255,0,128", "color3") -- Color3.fromRGB(255, 0, 128) TC.Coerce("1,2,3,0,0,0", "cframe") -- CFrame at (1,2,3) TC.Coerce("Scriptable", "Enum.CameraType") -- Enum.CameraType.Scriptable TC.Coerce("Part:Baseplate", "Instance") -- workspace.Baseplate TC.Coerce("0.5,10", "udim") -- UDim.new(0.5, 10) TC.Coerce("0,0,100,50", "rect") -- Rect.new(0, 0, 100, 50) ``` -------------------------------- ### Initialize Server Application Source: https://roexpress.dev/docs Set up the server-side application with middleware, route handlers, and push notifications. ```lua copylocal RoExpress = require(game.ReplicatedStorage.RoExpress) local app = RoExpress.GetApp() -- global middleware | runs before every handler app:Use("logger", function(Player, Payload) print(Player.Name, Payload.method, Payload.route) end) -- typed :userId param auto-converted to number app:Get("player/:userId=number", function(req, res) res:Send({ userId = req.params.userId }) end) app:Put("player/:userId=number/name", function(req, res) res:Status(200):Send() end) -- push to all connected clients reliably app:PushAll("round.end", { winner = "PlayerName" }) ``` -------------------------------- ### Initialize and use Cross-server features Source: https://roexpress.dev/docs/pages/cross.html Demonstrates registering a server, publishing events, subscribing to messages, updating shared memory, and listing active servers. ```lua local Cross = RoExpress.Cross -- register this server so others can find it Cross.RegisterServer({ mode = "pvp", map = "Forest" }) -- broadcast a message to all servers Cross.Publish("round.end", { winner = "Player1" }) -- receive messages from any server Cross.Subscribe("round.end", function(data, ctx) print(ctx.serverId, "ended round, winner:", data.winner) end) -- shared global counter Cross.Memory.Update("stats.wins", function(n) return (n or 0) + 1 end) local wins = Cross.Memory.Get("stats.wins") -- list every live server local servers = Cross.GetServers() print(#servers, "servers online") ``` -------------------------------- ### Import Benchmark Module Source: https://roexpress.dev/docs/pages/benchmark.html Initialize the Benchmark module to begin performance testing. ```lua local Benchmark = RoExpress("Benchmark") ``` -------------------------------- ### network:Get(route, payload, callback) Source: https://roexpress.dev/docs Performs a GET request from the client using a callback-style interface. ```APIDOC ## GET [route] ### Description Sends a GET request to the server and executes a callback upon response. ### Parameters - **route** (string) - Required - The target route. - **payload** (any) - Optional - Data to send with the request. - **callback** (function) - Required - Function to handle the response object. ``` -------------------------------- ### Basic Push Methods Usage Source: https://roexpress.dev/docs/pages/push-methods.html Demonstrates server-side event triggering and corresponding client-side listener implementation. ```lua -- server app:Push(player, "inventoryUpdate", { item = "sword" }) app:PushAll("roundEnd", { winner = "PlayerA" }) app:PushTo({ p1, p2 }, "zoneAlert", { zoneId = 3 }) -- client | same listener, zero API change listener:On("inventoryUpdate", function(data) print(data.item) end) listener:Once("roundEnd", function(data) UI:ShowWinner(data.winner) end) ``` -------------------------------- ### app:Get(route, handler) Source: https://roexpress.dev/docs Defines a GET route handler on the server. Supports typed parameters such as :userId=number. ```APIDOC ## GET [route] ### Description Registers a handler for GET requests on the specified route. ### Parameters - **route** (string) - Required - The route path, supporting typed parameters (e.g., :userId=number). - **handler** (function) - Required - Callback function receiving (req, res) arguments. ``` -------------------------------- ### app:Get(pattern, handler) Source: https://roexpress.dev/docs/pages/router-params.html Registers a GET route handler with support for named parameters and type coercion. ```APIDOC ## GET [pattern] ### Description Registers a route handler. Named parameters in the pattern (e.g., :id=number) are parsed and made available in req.params. ### Parameters - **pattern** (string) - Required - The route path with optional named parameters (e.g., "player/:id=number"). - **handler** (function) - Required - The callback function (req, res) to execute when the route is matched. ### Supported Types - :name (string) - :name=number (number) - :name=int (number) - :name=boolean (boolean) - :name=vector2 (Vector2) - :name=vector3 (Vector3) - :name=cframe (CFrame) - :name=color3 (Color3) - :name=Enum (EnumItem) - :name=instance (Instance) ``` -------------------------------- ### Implementing matchmaking selection Source: https://roexpress.dev/docs/pages/cross-registry.html Demonstrates how to filter the server list to find an appropriate instance for a player to join based on game mode and player count. ```lua local TeleportService = game:GetService("TeleportService") local Cross = RoExpress.Cross local function FindBestServer(mode: string) local servers = Cross.GetServers() local best for _, s in servers do if s.meta.mode == mode and s.players < 20 then if not best or s.players > best.players then best = s -- pick least-empty server with room end end end return best end local function JoinOrCreate(player: Player, mode: string) local server = FindBestServer(mode) if server then TeleportService:TeleportToPlaceInstance(game.PlaceId, server.id, player) else TeleportService:TeleportAsync(game.PlaceId, { player }) end end ``` -------------------------------- ### Require RoExpress (Command Bar Install) Source: https://roexpress.dev/docs/pages/installation.html Use this require statement in your scripts after performing the command bar installation. ```lua local RoExpress = require(game.ReplicatedStorage.RoExpress) ``` -------------------------------- ### Initialize RoExpress App Source: https://roexpress.dev/docs/pages/app Instantiate the application instance using either the GetApp method or the constructor. ```lua local app = RoExpress.GetApp() -- typed · or: RoExpress("App") ``` -------------------------------- ### rttp:Get(path, callback?) Source: https://roexpress.dev/docs/pages/rttp-methods.html Performs a GET request. Fires in a new thread and executes the provided callback upon completion. ```APIDOC ## rttp:Get(path, callback?) ### Description Performs an asynchronous GET request to the specified path. The callback receives a response object containing the request status and data. ### Parameters - **path** (string) - Required - The endpoint path to request. - **callback** (function) - Optional - Function executed when the request completes, receiving a response object. ``` -------------------------------- ### Initialize Client Networking Source: https://roexpress.dev/docs Configure client-side networking to handle requests and listen for server pushes. ```lua copylocal RoExpress = require(game.ReplicatedStorage.RoExpress) local network = RoExpress.GetNetwork() local listener = RoExpress("Listener") -- callback style network:Get("player/123", nil, function(res) print(res.data.userId) end) -- promise style (v2.2+) network:GetAsync("player/123") :Then(function(res) return res.data end) :Catch(function(err) warn(err.message) end) -- reliable push subscription listener:On("round.end", function(data) print("Winner:", data.winner) end) ``` -------------------------------- ### Initialize RTTP Client Source: https://roexpress.dev/docs/pages/rttp.html Create a new RTTP instance with a base URL. This must be performed on the server. ```lua local rttp = RoExpress.RTTP.New({ baseUrl = "https://api.example.com" }) ``` -------------------------------- ### Initialize an RTTP client Source: https://roexpress.dev/docs/pages/rttp-config.html Create a new RTTP client instance with a base URL and retry configuration. ```lua local RoExpress = require(game.ReplicatedStorage.RoExpress) local RTTP = RoExpress.RTTP local client = RTTP.New({ base = "https://api.example.com", retries = 3, -- optional, default 3 }) ``` -------------------------------- ### GET shop/catalogue Source: https://roexpress.dev/docs/pages/ex-shop.html Retrieves the current shop catalogue. ```APIDOC ## GET shop/catalogue ### Description Retrieves the full list of items available in the shop catalogue. ### Method GET ### Endpoint shop/catalogue ``` -------------------------------- ### Initialize Listener Source: https://roexpress.dev/docs/pages/listener.html Import the listener module to begin subscribing to server events. ```lua local listener = RoExpress("Listener") ``` -------------------------------- ### GET shop/balance/:userId Source: https://roexpress.dev/docs/pages/ex-shop.html Retrieves the currency balance for a specific user. ```APIDOC ## GET shop/balance/:userId ### Description Retrieves the current currency balance for the specified user ID. ### Method GET ### Endpoint shop/balance/:userId ### Parameters #### Path Parameters - **userId** (number) - Required - The ID of the user to check the balance for. ``` -------------------------------- ### GET inventory/:slot Source: https://roexpress.dev/docs/pages/examples-inventory.html Fetches the item located in a specific inventory slot. ```APIDOC ## GET inventory/:slot ### Description Fetches the item located in a specific inventory slot. ### Method GET ### Endpoint inventory/:slot ### Parameters #### Path Parameters - **slot** (int) - Required - The inventory slot index (1-40) ``` -------------------------------- ### Implement Server Push and Client Listener Source: https://roexpress.dev/docs/pages/listener-push.html Demonstrates the server-side push trigger and the corresponding client-side listener subscription. ```lua -- SERVER (Script) local app = RoExpress.GetApp() app:Get("round/start", function(req, res) -- push to everyone when a round begins app:PushAll("round.start", { duration = 120 }) res:Send({ ok = true }) end) -- CLIENT (LocalScript) local listener = RoExpress("Listener") listener:On("round.start", function(data) HUD:ShowTimer(data.duration) end) ``` -------------------------------- ### network:GetAsync(route) Source: https://roexpress.dev/docs Performs a GET request from the client using a promise-based interface. ```APIDOC ## GET [route] ### Description Sends a GET request to the server and returns a promise for the response. ### Parameters - **route** (string) - Required - The target route. ``` -------------------------------- ### Initialize Stream Source: https://roexpress.dev/docs/pages/stream Import the Stream module to begin defining channels and schemas. ```lua local Stream = RoExpress("Stream") ``` -------------------------------- ### network:GetAsync(route, data?) Source: https://roexpress.dev/docs/pages/network-async.html Performs an asynchronous GET request and returns a Promise. ```APIDOC ## network:GetAsync(route, data?) ### Description Performs an asynchronous GET request to the specified route. Returns a Promise that resolves with a NetworkResponse. ### Parameters - **route** (string) - Required - The API endpoint path. - **data** (table) - Optional - Query parameters to include in the request. ``` -------------------------------- ### Registering a server Source: https://roexpress.dev/docs/pages/cross-registry.html Registers the current server in the shared registry with optional metadata. Call this once at startup to initiate the heartbeat and automatic cleanup handlers. ```lua local Cross = RoExpress.Cross -- call once at the top of a server Script Cross.RegisterServer({ mode = "pvp", map = "Forest", ranked = true, }) ``` -------------------------------- ### Configure a standalone TokenBucket Source: https://roexpress.dev/docs/pages/tokenbucket-config.html Initialize a standalone bucket with a burst capacity of 60 and a recovery rate of 30 tokens per second. ```lua -- Burst of 60, recovers 30 tokens/s (30 per 1s tick) local TokenBucket = RoExpress("TokenBucket") local bucket = TokenBucket.new({ max = 60, refillRate = 30, refillInterval = 1, }) ``` -------------------------------- ### GET inventory Source: https://roexpress.dev/docs/pages/examples-inventory.html Fetches all items in the player's inventory. This endpoint supports Deflate compression. ```APIDOC ## GET inventory ### Description Fetches all items in the player's inventory. The response is compressed. ### Method GET ### Endpoint inventory ``` -------------------------------- ### GET leaderboard Source: https://roexpress.dev/docs/pages/ex-leaderboard.html Retrieves a paginated list of leaderboard entries sorted by score in descending order. ```APIDOC ## GET leaderboard ### Description Retrieves a paginated list of leaderboard entries sorted by score in descending order. ### Method GET ### Endpoint /leaderboard ### Parameters #### Query Parameters - **page** (number) - Optional - The page number to retrieve (default: 1). - **limit** (number) - Optional - The number of entries per page (default: 10, max: 50). ### Response #### Success Response (200) - **entries** (array) - List of player score objects. - **total** (number) - Total number of entries in the leaderboard. ``` -------------------------------- ### Configure client headers Source: https://roexpress.dev/docs/pages/rttp-config.html Define initial headers in the constructor and update them dynamically using SetHeader. ```lua local client = RTTP.New({ base = "https://hooks.slack.com/services/...", headers = { ["Content-Type"] = "application/json" }, retries = 1, }) -- Add or update headers after construction client:SetHeader("Authorization", "Bearer " .. SECRET) client:SetHeader("X-Game-Id", "my-game-123") ``` -------------------------------- ### GET zone/:name/players Source: https://roexpress.dev/docs/pages/ex-friendzone.html Retrieves a list of player names currently inside the specified zone. ```APIDOC ## GET zone/:name/players ### Description Returns a list of players currently located within the specified zone name. ### Method GET ### Endpoint zone/:name/players ### Parameters #### Path Parameters - **name** (string) - Required - The identifier of the zone to query. ### Response #### Success Response (200) - **players** (array) - List of player names currently inside the zone. #### Response Example { "players": ["Player1", "Player2"] } ``` -------------------------------- ### Import Network Module Source: https://roexpress.dev/docs/pages/network Initialize the network module using either the typed getter or the RoExpress factory method. ```lua local network = RoExpress.GetNetwork() -- typed · or: RoExpress("Network") ``` -------------------------------- ### Targeted Data Transmission Source: https://roexpress.dev/docs/pages/guide-stream-server.html Examples of sending data to specific players or defined subsets of players. ```lua -- Send only to one player (e.g. private data on join) move:SendTo(player, { userId = 0, cf = spawnCFrame, health = 100 }) -- Send to a zone's player list move:SendToList(zoneMembers, data) ``` -------------------------------- ### Compare standard and optimized network replication Source: https://roexpress.dev/why.html Demonstrates the difference between sending individual tables per player versus using a single packed binary buffer with delta compression. ```lua -- One FireAllClients per player per tick -- Table overhead, no compression, no delta RunService.Heartbeat:Connect(function() for _, player in Players.GetPlayers() do stateRemote:FireAllClients({ userId = player.UserId, position = player.Character.HumanoidRootPart.Position, health = player.Character.Humanoid.Health, -- full table every tick, no delta }) end end) ``` ```lua -- One packed buffer per tick, all players -- Delta compression built in local ch = Stream.Channel({ userId = "uint32", position = "vector3", health = "float32", }) RunService.Heartbeat:Connect(function() for _, player in Players.GetPlayers() do ch:Write(player.UserId, { position = player.Character.HumanoidRootPart.Position, health = player.Character.Humanoid.Health, }) end ch:Flush() -- one FireAllClients, only changed records end) ``` -------------------------------- ### Server-side Inventory Implementation Source: https://roexpress.dev/docs/pages/examples-inventory.html Handles inventory CRUD operations with DataStore persistence and compression for GET requests. ```luau local RoExpress = require(game.ReplicatedStorage.RoExpress) local app = RoExpress.GetApp() local DS = game:GetService("DataStoreService"):GetDataStore("Inventory") local cache = {} -- { [userId] = { [slot] = item } } local function getInv(player) return cache[player.UserId] or {} end game.Players.PlayerAdded:Connect(function(p) local ok, data = pcall(DS.GetAsync, DS, tostring(p.UserId)) cache[p.UserId] = (ok and data) or {} end) game.Players.PlayerRemoving:Connect(function(p) pcall(DS.SetAsync, DS, tostring(p.UserId), cache[p.UserId]) cache[p.UserId] = nil end) -- GET all items (large table | compress it) app:Get("inventory", function(req, res) res:Send(getInv(req.player)) end, { compress = true }) -- GET single slot app:Get("inventory/:slot=int[1,40]", function(req, res) local inv = getInv(req.player) res:Send(inv[req.params.slot]) end) -- POST add item to first free slot app:Post("inventory", function(req, res) local inv = getInv(req.player) local item = req.data for slot = 1, 40 do if not inv[slot] then inv[slot] = item res:Send({ slot = slot }) return end end res:Status(409):Error("Inventory full") end) -- PUT update/swap slot app:Put("inventory/:slot=int[1,40]", function(req, res) getInv(req.player)[req.params.slot] = req.data.item res:Send() end) -- DELETE remove item app:Delete("inventory/:slot=int[1,40]", function(req, res) getInv(req.player)[req.params.slot] = nil res:Send() end) ``` -------------------------------- ### Require RoExpress (Wally Install) Source: https://roexpress.dev/docs/pages/installation.html Use this require statement when using Wally, which places packages in ReplicatedStorage/Packages. ```lua local RoExpress = require(game.ReplicatedStorage.Packages.RoExpress) ``` -------------------------------- ### Initialize Client Network on a port Source: https://roexpress.dev/docs/pages/port-use.html Create a Network instance bound to a specific port to perform requests using either callback or promise patterns. ```lua local RoExpress = require(game.ReplicatedStorage.RoExpress) local combatNet = RoExpress("Network", "combat") -- callback style combatNet:Post("shoot/123", nil, function(res) if res.ok then showHit(res.data) end end) -- promise style combatNet:PostAsync("shoot/123") :Then(function(res) showHit(res.data) end) :Catch(function(err) warn(err.message) end) ``` -------------------------------- ### Manage Tool Lifecycle Source: https://roexpress.dev/docs/pages/maid.html Use a Maid to manage connections during a tool's equipped state. ```lua local maid: RoExpress.Maid tool.Equipped:Connect(function() maid = RoExpress("Maid") maid:Add(Bridge.On("ammo.refill", function(data) ammo = data.amount end)) maid:Add(UserInputService.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then shoot() end end)) maid:Add(function() print("gun unequipped") end) end) tool.Unequipped:Connect(function() if maid then maid:Destroy() end end) ``` -------------------------------- ### Movement Schema Implementation Source: https://roexpress.dev/docs/pages/stream-schema.html A complete example of a shared movement schema module for use on both server and client. ```luau -- shared.luau — required on both server and client local RoExpress = require(game.ReplicatedStorage.RoExpress) local Stream = RoExpress("Stream") local moveSchema = Stream.Schema.New({ { "pos", "Vector3" }, -- 12 B { "vel", "Vector3" }, -- 12 B { "cf", "CFrameLight" }, -- 16 B { "state", { "flags", "jumping", "sprinting", "crouching" } }, -- 1 B { "health", "u16" }, -- 2 B }) -- 43 bytes per packet | fixed-size | delta eligible return moveSchema ``` -------------------------------- ### app:Listen(name, callback, settings?) Source: https://roexpress.dev/docs/pages/port-create.html Registers a named port with an isolated pipeline, including its own RemoteEvent, Router, and TokenBucket. ```APIDOC ## app:Listen(name, callback, settings?) ### Description Registers a named port. The callback receives the port's own App-like instance, which provides an isolated environment for routing and event handling. ### Parameters - **name** (string) - Required - Unique port identifier that must match the name used on the client. - **callback** ((port) -> ()) - Required - Function called immediately with the port instance to register routes. - **settings** (PortSettings) - Optional - TokenBucket configuration for rate limiting. ### Returns - **Port** - The created port instance. ``` -------------------------------- ### Initialize Maid Instance Source: https://roexpress.dev/docs/pages/maid.html Create a new Maid instance using the RoExpress factory. ```lua local maid = RoExpress("Maid") ``` -------------------------------- ### Register middleware in specific order Source: https://roexpress.dev/docs/pages/guide-middleware.html Demonstrates how registration order determines execution sequence. Guards should be registered before logging to avoid tracking rejected requests. ```lua app:Use("ban-check", banCheckFn) -- 1st app:Use("admin-guard", adminGuardFn) -- 2nd app:Use("analytics", analyticsFn) -- 3rd | only runs if not rejected ``` -------------------------------- ### app:PushAll Source: https://roexpress.dev/docs/pages/push.html Sends a server-initiated event to all connected players. ```APIDOC ## app:PushAll ### Description Initiates a reliable, one-way event from the server to all connected players. ### Method app:PushAll(eventName, ...) ### Parameters - **eventName** (string) - Required - The name of the event to trigger. - **...** (any) - Optional - Data to pass to the client listeners. ``` -------------------------------- ### Broadcast Hit Effect Implementation Source: https://roexpress.dev/docs/pages/listener-broadcast.html Example of triggering a visual effect across clients using the broadcast system. ```lua -- SERVER fires a hit effect for everyone nearby broadcast:Emit("hit.fx", { pos = hitPos, weapon = "rifle" }) -- CLIENT plays it listener:On("hit.fx", function(data) FX:PlayImpact(data.pos, data.weapon) end) ``` -------------------------------- ### Manual Webhook Implementation with HttpService Source: https://roexpress.dev/why.html Standard approach requiring manual boilerplate for error handling, JSON encoding, and request management. ```lua -- Written from scratch, every time, in every game local HS = game:GetService("HttpService") local function sendWebhook(winner) local ok, err = pcall(function() HS:RequestAsync({ Url = WEBHOOK_URL, Method = "POST", Headers = { ["Content-Type"] = "application/json" }, Body = HS:JSONEncode({ content = winner .. " won!" }), }) end) if not ok then -- retry? manually? give up? end end ``` -------------------------------- ### Implement Shop Purchase Flow Source: https://roexpress.dev/docs/pages/ex-shop.html Handles catalogue retrieval, balance checking, and item purchasing with server-side validation. ```lua copyapp:Get("shop/catalogue", function(Player, Payload, req, res) res:Send(catalogue) end) app:Get("shop/balance/:userId=number", function(Player, Payload, req, res) res:Send({ balance = currency[req.params.userId] or 0 }) end) app:Post("shop/buy", function(Player, Payload, req, res) local item = catalogue[req.data and req.data.itemId] if not item then res:Status(404):Error("Not found"); return end if item.stock <= 0 then res:Status(400):Error("Out of stock"); return end if (currency[Player.UserId] or 0) < item.price then res:Status(400):Error("Insufficient funds"); return end currency[Player.UserId] -= item.price; item.stock -= 1 app:Push(Player, "shop.purchased", { item = req.data.itemId, balance = currency[Player.UserId] }) res:Send({ success = true }) end) ``` -------------------------------- ### Decouple modules with Bridge Source: https://roexpress.dev/docs/pages/bridge-bind.html Example of a shop module and inventory module communicating via the Bridge without direct references. ```luau -- ShopModule.luau local function Purchase(player, itemId) -- ... validate, charge currency ... Bridge.Emit("item.purchased", { player = player, itemId = itemId }) end -- InventoryModule.luau Bridge.On("item.purchased", function(data) addToInventory(data.player, data.itemId) end) ``` -------------------------------- ### Configure Port TokenBucket Settings Source: https://roexpress.dev/docs/pages/port-settings.html Define custom rate limits for specific ports by passing a settings table to the app:Listen method. High-frequency ports should use stricter limits to prevent abuse. ```lua -- A high-frequency combat port: tight limits app:Listen("combat", function(port) port:Post("shoot/:targetId=number", function(req, res) res:Send(handleShoot(req.player, req.params.targetId)) end) end, { maxTokens = 20, refillRate = 10, startTokens = 10, cost = 1, }) -- A low-frequency inventory port: generous limits app:Listen("inventory", function(port) port:Get("items", function(req, res) res:Send(getInventory(req.player)) end) end, { maxTokens = 60, refillRate = 20, }) ``` -------------------------------- ### Define constrained route parameters Source: https://roexpress.dev/docs/pages/router-constraints.html Examples of using Lua patterns to restrict route segments and optionally coerce them into specific types. ```lua -- digits only, raw string app:Get("user/:id(%d+)", function(req, res) print(req.params.id) -- "123" — string end) -- digits only, coerced to number app:Get("user/:id(%d+)=number", function(req, res) print(req.params.id) -- 123 — number end) -- lowercase letters only app:Get("cmd/:action(%a+)", function(req, res) print(req.params.action) -- "fire", "reload" | rejects "fire2" end) -- page number: digits, returned as integer app:Get("page/:n(%d+)=int", function(req, res) res:Send({ page = req.params.n }) end) ``` -------------------------------- ### Initialize Client Listener on a port Source: https://roexpress.dev/docs/pages/port-use.html Subscribe to push events originating from a specific port using a Listener instance. ```lua local combatListener = RoExpress("Listener", "combat") combatListener:On("hit.confirmed", function(data) print("damage:", data.damage) end) ``` -------------------------------- ### bucket:Reset(player: Player?) Source: https://roexpress.dev/docs/pages/tokenbucket-grant.html Resets a player's token count to the configured start value, or resets all active players if no player is specified. ```APIDOC ## bucket:Reset(player: Player?) ### Description Sets the player's token count back to the configured `start` value (defaults to `max`). If `player` is omitted, resets all active players. ### Parameters - **player** (Player) - Optional - The player to reset. If omitted, all active players are reset. ``` -------------------------------- ### TokenBucket.new(options) Source: https://roexpress.dev/docs/pages/tokenbucket-config.html Constructs a new TokenBucket instance with specified capacity and refill parameters. ```APIDOC ## TokenBucket.new(options) ### Description Creates a new TokenBucket instance. The options table controls the burst capacity and the sustained refill rate. ### Parameters - **options** (table) - Required - Configuration table containing: - **max** (number) - Optional - Maximum tokens per player (burst ceiling). Default: 60. - **refillRate** (number) - Optional - Tokens added per refill tick. Default: 20. - **refillInterval** (number) - Optional - Seconds between refill ticks. Default: 1. - **start** (number) - Optional - Initial token count. Default: max. - **cost** (number) - Optional - Tokens deducted per Consume call. Default: 1. ### Returns - **TokenBucket** - A new instance of the TokenBucket. ``` -------------------------------- ### bench:RunAndPrint(path, options) Source: https://roexpress.dev/docs/pages/benchmark-run.html Executes a benchmark run against a path using the main RoExpress channel and prints the results immediately. ```APIDOC ## bench:RunAndPrint(path, options) ### Description Runs a benchmark on the main channel and prints the output. ### Parameters - **path** (string) - Required - The target path to benchmark. - **options** (table) - Required - Configuration table including method, iterations, warmup, and label. ``` -------------------------------- ### HTTP Methods (Get, Post, Put, Delete) Source: https://roexpress.dev/docs/pages/rttp.html Methods for performing HTTP requests. Each method supports callback and promise variants, with automatic JSON encoding for request bodies. ```APIDOC ## rttp:Get / rttp:Post / rttp:Put / rttp:Delete ### Description Executes an HTTP request using the specified verb. Bodies are automatically JSON-encoded. ### Response - **ok** (boolean) - Indicates if the request was successful. - **status** (number) - The HTTP status code. - **data** (table) - The parsed response body. - **headers** (table) - The response headers. - **raw** (string) - The raw response body. ``` -------------------------------- ### Importing Types and Usage Source: https://roexpress.dev/docs/pages/types-request.html Demonstrates how to import RoExpress types and register a route handler using the compact form. ```Luau local RoExpress = require(game.ReplicatedStorage.RoExpress) -- Explicit type import type Request = RoExpress.Request type Payload = RoExpress.Payload type RouteHandler = RoExpress.RouteHandler -- Or use GetApp() which returns a fully-typed App — no annotation needed local app = RoExpress.GetApp() app:Get("player/:id", function(req, res) res:Send({ id = req.params.id, player = req.player.Name }) end) ``` -------------------------------- ### network:Get, network:Post, network:Put, network:Delete Source: https://roexpress.dev/docs/pages/network Standard request methods to send data to server-registered routes using a callback pattern. Each method returns a requestId for potential cancellation. ```APIDOC ## network:Get / Post / Put / Delete ### Description Sends a request to a server-registered route. Executes a provided callback function upon receiving a NetworkResponse. ### Returns - **requestId** (number) - An identifier used to cancel the request. ### Parameters - **route** (string) - The target server-registered route. - **data** (table) - The payload to send (for POST/PUT). - **callback** (function) - Function receiving the NetworkResponse object. ``` -------------------------------- ### Issue a manual strike on bad data Source: https://roexpress.dev/docs/pages/tamper-strikes.html Use Tamper.Strike within a route handler to flag players providing invalid request data. This example demonstrates validating a trade payload and issuing a strike when the itemId is missing or malformed. ```lua local Tamper = RoExpress.Tamper app:Post("trade", function(req, res) local data = req.data if not data or not data.itemId or type(data.itemId) ~= "number" then Tamper.Strike(req.player, "missing or invalid itemId in trade payload") return res:Error(400, "Bad request") end -- process trade... res:Send({ ok = true }) end) ``` -------------------------------- ### Implement Server and Client combat communication Source: https://roexpress.dev/docs/pages/port-use.html Demonstrates the full lifecycle of a port-based request, from server-side listening to client-side invocation. ```lua -- Server (Script) local RoExpress = require(game.ReplicatedStorage.RoExpress) local app = RoExpress.GetApp() app:Listen("combat", function(port) port:Post("shoot/:targetId=number", function(req, res) res:Send(handleShoot(req.player, req.params.targetId)) end) end) -- Client (LocalScript) local RoExpress = require(game.ReplicatedStorage.RoExpress) local combatNet = RoExpress("Network", "combat") combatNet:Post("shoot/456", nil, function(res) print(res.data) end) ``` -------------------------------- ### Stream.Init() Source: https://roexpress.dev/docs/pages/stream-channels.html Finalizes all registered channels and connects the underlying remotes. ```APIDOC ## Stream.Init() -> () ### Description Finalizes all registered channels and connects the underlying remotes. Call exactly once after all Stream.Channel() definitions on both server and client. ``` -------------------------------- ### Importing the Bridge module Source: https://roexpress.dev/docs/pages/bridge.html Initialize the Bridge module from the RoExpress library. ```lua local Bridge = RoExpress.Bridge ``` -------------------------------- ### RoExpress Routing and Middleware Source: https://roexpress.dev/why.html Shows how to consolidate routes into a single application instance and apply global middleware for logging. ```lua -- One remote. Every route in one place. local app = RoExpress.GetApp() app:Post("shop/buy/:itemId=string", handler) app:Post("shop/sell/:itemId=string", handler) app:Post("combat/hit", handler) app:Post("admin/kick/:userId=number", handler) -- One global middleware log for everything app:Use("logger", function(player, payload) print(player.Name, payload.method, payload.route) end) ``` -------------------------------- ### Initialize Broadcast Source: https://roexpress.dev/docs/pages/broadcast Instantiate the Broadcast service to begin sending unreliable events. ```lua local broadcast = RoExpress("Broadcast") ``` -------------------------------- ### Import Codec Module Source: https://roexpress.dev/docs/pages/codec Access the compression utilities by importing the Codec module from the RoExpress library. ```lua local Codec = RoExpress.Codec ``` -------------------------------- ### bench:Print(results) Source: https://roexpress.dev/docs/pages/benchmark-run.html Writes a formatted summary of benchmark results to the Studio output window, including statistics like min, avg, median, p95, p99, max, error counts, and throughput. ```APIDOC ## bench:Print(results) ### Description Writes a formatted summary to the Studio output window. The header line shows the target type and method so you can identify each run at a glance. ### Parameters - **results** (Results) - Required - The results object returned from a benchmark run. ``` -------------------------------- ### app:Post(route, handler, options?) Source: https://roexpress.dev/docs/pages/app-routing.html Registers a POST route handler. ```APIDOC ## POST app:Post(route, handler, options?) ### Description Registers a handler for POST requests. ### Parameters - **route** (string) - Required - Route pattern. - **handler** (function) - Required - Function signature (req, res) or (Player, Payload, req, res). - **options.compress** (boolean) - Optional - Enable LZ77 Deflate on the response. ``` -------------------------------- ### Runtime Configuration Methods Source: https://roexpress.dev/docs/pages/rttp-config.html Methods to modify client configuration after initialization. ```APIDOC ## Runtime Configuration Methods ### :SetBase(url) Sets or replaces the base URL for all future requests. ### :SetHeader(key, value) Adds or updates a persistent header sent on every request. ### :RemoveHeader(key) Removes a persistent header. ### :SetRetries(n) Changes the maximum retry count. Use 0 to disable retries. ```