### Install Wally Dependencies Source: https://github.com/littensy/remo/blob/main/examples/lua/README.md Run this script to install all necessary Wally dependencies for the project. ```bash sh scripts/install.sh ``` -------------------------------- ### Start Rojo Server Source: https://github.com/littensy/remo/blob/main/examples/lua/README.md After building the place, open it in Roblox Studio and start the Rojo server to enable live development. ```bash rojo serve ``` -------------------------------- ### Install Remo via npm, yarn, or pnpm Source: https://github.com/littensy/remo/blob/main/README.md Use these commands to add the Remo library to your project via different package managers. ```bash npm install @rbxts/remo yarn add @rbxts/remo pnpm add @rbxts/remo ``` -------------------------------- ### Install Remo via Wally Source: https://github.com/littensy/remo/blob/main/README.md Add Remo to your project's dependencies in your Wally configuration file. ```toml [dependencies] Remo = "littensy/remo@VERSION" ``` -------------------------------- ### Configure throttleMiddleware in Luau Source: https://context7.com/littensy/remo/llms.txt Configure throttleMiddleware in Luau with options for rate-limiting remotes. This example shows setting the throttle time and enabling trailing edge invocation. ```luau local remotes = Remo.createRemotes({ updatePosition = Remo.remote(t.number, t.number, t.number) .unreliable() .middleware(Remo.throttleMiddleware(0.05)), chatMessage = Remo.remote(t.string) .middleware(Remo.throttleMiddleware({ throttle = 1, trailing = true })), }) ``` -------------------------------- ### Implement Custom Rate-Limiting Middleware with getSender in TypeScript Source: https://context7.com/littensy/remo/llms.txt Create a custom RemoteMiddleware in TypeScript that uses getSender to identify the player and implement rate-limiting logic. This example limits calls to 0.5 seconds per player. ```typescript import { getSender, RemoteMiddleware } from "@rbxts/remo"; const rateLimitMiddleware: RemoteMiddleware = (nextFn, remote) => { const lastCall = new Map(); return (...args: unknown[]) => { const player = getSender(...args); if (player) { const now = os.clock(); const last = lastCall.get(player.Name) ?? 0; if (now - last < 0.5) { warn(`${player.Name} is calling ${remote.name} too fast!`); return; } lastCall.set(player.Name, now); } return nextFn(...args); }; }; ``` -------------------------------- ### Implement Custom Rate-Limiting Middleware with getSender in Luau Source: https://context7.com/littensy/remo/llms.txt Implement a custom Remo.Middleware in Luau that utilizes Remo.getSender to identify the calling player and enforce rate limits. This example prevents calls within 0.5 seconds. ```luau local Remo = require(ReplicatedStorage.Packages.Remo) local rateLimitMiddleware: Remo.Middleware = function(next, remote) local lastCall = {} return function(...) local player = Remo.getSender(...) if player then local now = os.clock() if (now - (lastCall[player.Name] or 0)) < 0.5 then warn(player.Name .. " is spamming " .. remote.name) return end lastCall[player.Name] = now end return next(...) end end ``` -------------------------------- ### Define Luau Remotes with Types Source: https://github.com/littensy/remo/blob/main/README.md Define a type for your remotes to enable full type-checking in your editor. This setup includes client-to-server, server-to-client, and server-asynchronous remotes, as well as namespaced remotes. ```lua type Remotes = { client: Remo.ServerToClient, server: Remo.ClientToServer, serverAsync: Remo.ServerAsync<(number), (string)> , namespace: { client: Remo.ServerToClient, server: Remo.ClientToServer, } } local remotes: Remotes = Remo.createRemotes({ client = Remo.remote(t.number), server = Remo.remote(t.number), serverAsync = Remo.remote(t.number).returns(t.string), namespace = Remo.namespace({ client = Remo.remote(t.number), server = Remo.remote(t.number), }), }) ``` -------------------------------- ### TypeScript Get Sender Function Signature Source: https://github.com/littensy/remo/blob/main/README.md Signature for the getSender function in TypeScript, which attempts to identify and return the Player object from remote invocation arguments. ```typescript function getSender(...args: unknown[]): Player | undefined; ``` -------------------------------- ### Request Remote Async Functions in Luau Source: https://github.com/littensy/remo/blob/main/README.md Use the `request` method to invoke a remote function and get its result via a promise. Arguments are validated before the handler is called, and the return value is validated before the promise resolves. ```lua -- client -> server async remotes.async:request(...):andThen(function(result) print(result) end) -- server -> client async remotes.async:request(player, ...):andThen(function(result) print(result) end) ``` -------------------------------- ### Connecting to Events Source: https://github.com/littensy/remo/blob/main/README.md Shows how to listen for incoming events using the `connect` method and handle received arguments. ```APIDOC ## Connecting to Events ### Description Uses the `connect` method to attach a callback function to a remote event. Listeners are called only if all provided validators pass. ### Client to Server Event Listener ```lua -- client -> server local disconnect = remotes.event:connect(function(player, ...) print(player, ...) end) ``` ### Server to Client Event Listener ```lua -- server -> client local disconnect = remotes.event:connect(function(...) print(...) end) ``` ``` -------------------------------- ### Create Remotes with Namespaces (Luau) Source: https://github.com/littensy/remo/blob/main/README.md Demonstrates creating nested namespaces of remotes using `Remo.createRemotes` and `Remo.namespace`. Supports client events, server-processed functions, and nested structures. ```lua local remotes: Remotes = Remo.createRemotes({ event = Remo.remote(t.number), async = Remo.remote(t.number).returns(t.string), namespace = Remo.namespace({ event = Remo.remote(t.number), async = Remo.remote(t.number).returns(t.string), }), }) ``` -------------------------------- ### Build Roblox Place with Rojo Source: https://github.com/littensy/remo/blob/main/examples/lua/README.md Use this command to build the Roblox place file from the Luau source code. ```bash rojo build -o "lua.rbxlx" ``` -------------------------------- ### Firing Events Source: https://github.com/littensy/remo/blob/main/README.md Demonstrates how to send events from the client to the server and vice-versa using the `fire` method. ```APIDOC ## Firing Events ### Description Uses the `fire` method to send arguments over a remote event. This is analogous to `FireServer` and `FireClient`. ### Client to Server Event ```lua -- client -> server remotes.event:fire(...); ``` ### Server to Client Events ```lua -- server -> client remotes.event:fire(player, ...); remotes.event:fireAll(...); remotes.event:fireAllExcept(player, ...); remotes.event:firePlayers(players, ...); ``` ``` -------------------------------- ### Create Remotes with Namespaces (TypeScript) Source: https://github.com/littensy/remo/blob/main/README.md Demonstrates creating nested namespaces of remotes using `createRemotes` and `namespace`. Supports client events, server-processed functions, and nested structures. ```typescript const remotes = createRemotes({ event: remote(t.number), async: remote(t.number).returns(t.string), namespace: namespace({ event: remote(t.number), async: remote(t.number).returns(t.string), }), }); ``` -------------------------------- ### Initialize Remotes with Events and Functions (Luau) Source: https://github.com/littensy/remo/blob/main/README.md Initializes remote objects using `Remo.createRemotes`. Supports client events, server-processed functions, and logged events. Connect to events and request functions after initialization. ```lua type Remotes = { -- An event processed on the client event: Remo.ServerToClient, -- A function whose result, a string, is processed on the server async: Remo.ServerAsync<(number), (string)>, -- An event fired to a client, with logging logged: Remo.ServerToClient, } local remotes: Remotes = Remo.createRemotes({ event = Remo.remote(t.number), async = Remo.remote(t.number).returns(t.string), logged = Remo.remote(t.number).middleware(loggerMiddleware), }) remotes.event:connect(print) remotes.async:request(123):andThen(print) ``` -------------------------------- ### createRemotes Source: https://context7.com/littensy/remo/llms.txt Initializes all remotes from a schema, creating instances on the server and connecting on the client. It returns a typed Remotes object with a destroy() method. ```APIDOC ## createRemotes(schema, ...middleware) ### Description Initializes all remotes from a schema, creating instances on the server and connecting on the client. It returns a typed Remotes object with a destroy() method. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **schema** (object) - Required - An object mapping remote names to builder declarations. - **...middleware** (function) - Optional - A list of global middleware functions to be applied to all remotes. ### Request Example ```typescript // TypeScript example import { createRemotes, namespace, remote } from "@rbxts/remo"; import { t } from "@rbxts/t"; const remotes = createRemotes({ addTodo: remote(t.string), removeTodo: remote(t.string), getTodos: remote().returns(t.array(t.string)), todosChanged: remote(t.array(t.string)), chat: namespace({ sendMessage: remote(t.string), receiveMessage: remote(t.string, t.string), }), }, loggerMiddleware); // Clean up when no longer needed // remotes.destroy(); ``` ```lua -- Luau example local Remo = require(ReplicatedStorage.Packages.Remo) local t = require(ReplicatedStorage.Packages.t) local remotes = Remo.createRemotes({ addTodo = Remo.remote(t.string), removeTodo = Remo.remote(t.string), getTodos = Remo.remote().returns(t.array(t.string)), todosChanged = Remo.remote(t.array(t.string)), }, Remo.loggerMiddleware) return remotes ``` ### Response #### Success Response (200) - **Remotes** (object) - A typed object containing the live remote instances and a `destroy()` method. ``` -------------------------------- ### Configure throttleMiddleware in TypeScript Source: https://context7.com/littensy/remo/llms.txt Configure throttleMiddleware with options for rate-limiting remotes. Options include 'throttle' for the time window and 'trailing' to fire the last dropped invocation. ```typescript import { Client, Server, createRemotes, throttleMiddleware, remote } from "@rbxts/remo"; export const remotes = createRemotes({ // Allow at most one position update per 0.05 seconds per player updatePosition: remote() .unreliable() .middleware(throttleMiddleware(0.05)), // Throttle with trailing edge: fire once more after window ends chatMessage: remote().middleware( throttleMiddleware({ throttle: 1, trailing: true }), ), // Throttled async: returns cached result during throttle window getLeaderboard: remote() .returns<{ name: string; score: number }[]>() .middleware(throttleMiddleware({ throttle: 5 })) }); ``` -------------------------------- ### TypeScript: Unit Testing with Remo's `test` Interface Source: https://context7.com/littensy/remo/llms.txt Demonstrates how to use the `test` interface for mocking server remotes in unit tests. Use `onFire` to listen for outgoing events and `handleRequest` to mock responses for async remotes. Always call `disconnectAll` to clean up test listeners and handlers. ```typescript import { createRemotes, remote, Client, Server } from "@rbxts/remo"; import { t } from "@rbxts/t"; const remotes = createRemotes({ addTodo: remote(t.string), getTodos: remote().returns(t.array(t.string)), todosChanged: remote(t.array(t.string)), }); // --- Testing a ServerRemote event --- const firedArgs: string[] = []; const cleanup = remotes.addTodo.test.onFire((name) => { firedArgs.push(name); }); remotes.addTodo.fire("Buy milk"); // triggers test listener print(firedArgs); // → ["Buy milk"] cleanup(); // disconnect listener // --- Mocking an async remote response --- remotes.getTodos.test.handleRequest(() => ["MockedItem1", "MockedItem2"]); remotes.getTodos.request().then((todos) => { print(todos); // → ["MockedItem1", "MockedItem2"] remotes.getTodos.test.disconnectAll(); // clean up handler }); // --- Check if a handler is registered --- print(remotes.getTodos.test.hasRequestHandler()); // → true ``` -------------------------------- ### Initialize Remotes with Events and Functions (TypeScript) Source: https://github.com/littensy/remo/blob/main/README.md Initializes remote objects using `createRemotes`. Supports client events, server-processed functions, and logged events. Connect to events and request functions after initialization. ```typescript const remotes = createRemotes({ // An event processed on the client event: remote(t.number), // A function whose value is processed by the server async: remote(t.number).returns(t.string), // An event fired to a client, with logging logged: remote(t.number).middleware(loggerMiddleware), }); remotes.event.connect((value) => print(value)); remotes.async.request(123).then((value) => print(value)); ``` -------------------------------- ### remote.connect(listener) Source: https://context7.com/littensy/remo/llms.txt Subscribes a listener to a remote event. On the server, the listener receives (player, ...args); on the client, it receives (...args). Returns a cleanup function. ```APIDOC ## `remote.connect(listener)` — Subscribe to a remote event `connect` registers a listener that is invoked when the remote fires. On the server, the listener for a `ClientToServer` remote receives `(player, ...args)`; on the client, a `ServerToClient` listener receives just `(...args)`. Returns a cleanup function that disconnects the listener when called. Arguments are validated before listeners are called. ### TypeScript Example ```ts // Server-side: player argument prepended automatically const disconnect = remotes.addTodo.connect((player, name) => { print(`${player.Name} added todo: ${name}`); }); // Client-side: remotes.todosChanged is a ClientRemote remotes.todosChanged.connect((todos) => { print(`Received ${todos.size()} todos`); }); ``` ### Luau Example ```lua -- Server local disconnect = remotes.addTodo:connect(function(player, name) print(player.Name .. " added: " .. name) end) -- Client remotes.todosChanged:connect(function(todos) print("Got", #todos, "todos") end) ``` ``` -------------------------------- ### createRemotes Source: https://github.com/littensy/remo/blob/main/README.md Creates a set of remotes from a schema, optionally applying middleware to all remotes. ```APIDOC ## `createRemotes(schema)` ### Description Creates a set of remotes from a schema. ### Parameters - `schema` - An object whose keys are the names of the remotes, and whose values are the remote declarations. - `...middleware` - An optional list of middleware to apply to all remotes. ### Returns `createRemotes` returns a Remotes object, which contains the remotes defined in the schema. You can access your remotes through this object, and it also has a `destroy` method that can be used to destroy all of the remotes. ``` -------------------------------- ### Requesting Async Functions Source: https://github.com/littensy/remo/blob/main/README.md Explains how to invoke remote asynchronous functions using the `request` method and handle the returned promise. ```APIDOC ## Requesting Async Functions ### Description Uses the `request` method to invoke a remote asynchronous function. It sends arguments and returns a promise that resolves with the function's return value. Arguments and return values are validated. ### Client to Server Async Request ```lua -- client -> server async remotes.async:request(...):andThen(function(result) print(result) end) ``` ### Server to Client Async Request ```lua -- server -> client async remotes.async:request(player, ...):andThen(function(result) print(result) end) ``` ``` -------------------------------- ### Initialize Remotes with createRemotes Source: https://context7.com/littensy/remo/llms.txt Use `createRemotes` to define and initialize all remotes from a schema. It accepts a schema object and optional global middleware, returning a typed `Remotes` object with a `destroy()` method. On the server, it creates Roblox remote instances; on the client, it connects to them. ```typescript import { Client, Server, createRemotes, loggerMiddleware, namespace, remote } from "@rbxts/remo"; import { t } from "@rbxts/t"; export const remotes = createRemotes( { // Client fires → Server processes addTodo: remote(t.string), removeTodo: remote(t.string), // Client requests → Server responds with string[] getTodos: remote().returns(t.array(t.string)), // Server fires → all Clients receive todosChanged: remote(t.array(t.string)), // Nested namespace chat: namespace({ sendMessage: remote(t.string), receiveMessage: remote(t.string, t.string), }), }, loggerMiddleware, // applied globally to all remotes ); // Clean up when no longer needed (typically never at game level) // remotes.destroy(); ``` ```lua -- Luau — shared/remotes.lua local ReplicatedStorage = game:GetService("ReplicatedStorage") local Remo = require(ReplicatedStorage.Packages.Remo) local t = require(ReplicatedStorage.Packages.t) type Remotes = Remo.Remotes<{ addTodo: Remo.ClientToServer, removeTodo: Remo.ClientToServer, getTodos: Remo.ServerAsync<(), ({ string })>, todosChanged: Remo.ServerToClient<{ string }>, }> local remotes: Remotes = Remo.createRemotes({ addTodo = Remo.remote(t.string), removeTodo = Remo.remote(t.string), getTodos = Remo.remote().returns(t.array(t.string)), todosChanged = Remo.remote(t.array(t.string)), }, Remo.loggerMiddleware) return remotes ``` -------------------------------- ### Apply Middleware to Single Remote in Luau Source: https://github.com/littensy/remo/blob/main/README.md Applies the loggerMiddleware to a specific remote named 'event' during the creation of remotes. Requires 'Remo.createRemotes' and 'Remo.remote' functions. ```lua local remotes = Remo.createRemotes({ event = Remo.remote(t.number).middleware(loggerMiddleware), }, ...middleware) ``` -------------------------------- ### Apply loggerMiddleware Per-Remote in Luau Source: https://context7.com/littensy/remo/llms.txt Apply loggerMiddleware to individual remotes in Luau using the .middleware() method. This allows for selective logging of specific remote calls. ```luau local remotes = Remo.createRemotes({ addTodo = Remo.remote(t.string).middleware(Remo.loggerMiddleware), getTodos = Remo.remote().returns(t.array(t.string)), }) ``` -------------------------------- ### namespace Source: https://github.com/littensy/remo/blob/main/README.md Declares a namespace to be used in the remote schema. ```APIDOC ## `namespace(schema)` ### Description Declares a namespace to be used in the remote schema. ### Parameters - `schema` - An object whose keys are the names of the remotes, and whose values are the remote declarations. ### Returns `namespace` returns a RemoteNamespace, which declares a namespace of remotes. It does not have a public API. ``` -------------------------------- ### Connect to Remote Events in Luau Source: https://github.com/littensy/remo/blob/main/README.md Use the `connect` method to attach a callback function to a remote event. The callback receives arguments sent by `fire`. The `connect` method returns a function to disconnect the listener. ```lua -- client -> server local disconnect = remotes.event:connect(function(player, ...) print(player, ...) end) -- server -> client local disconnect = remotes.event:connect(function(...) print(...) end) ``` -------------------------------- ### Grouping Remotes with `namespace` Source: https://context7.com/littensy/remo/llms.txt The `namespace` function allows you to group related remotes into a nested structure, improving organization. It accepts the same schema format as `createRemotes` and can be nested arbitrarily deep. ```APIDOC ## `namespace(schema)` — Group remotes into a nested namespace `namespace` creates a nested group of remotes within the schema. It accepts the same schema format as `createRemotes` and can be nested arbitrarily deep. The resulting remotes are accessible on the returned object under the namespace key. ```ts // TypeScript import { Client, Server, createRemotes, namespace, remote } from "@rbxts/remo"; import { t } from "@rbxts/t"; export const remotes = createRemotes({ player: namespace({ joined: remote(t.number), left: remote(t.number), dataLoaded: remote(), }), inventory: namespace({ addItem: remote(t.string), removeItem: remote(t.string), sync: remote(t.array(t.string)), }), }); // Server usage remotes.player.joined.fire(somePlayer, somePlayer.UserId); remotes.inventory.addItem.connect((player, itemId) => { print(`${player.Name} wants to add item: ${itemId}`); }); // Client usage remotes.player.joined.connect((userId) => { print(`Player ${userId} joined`); }); remotes.inventory.addItem.fire("sword_01"); ``` ``` -------------------------------- ### Subscribe to Remote Events with `connect` Source: https://context7.com/littensy/remo/llms.txt Registers a listener for remote events. On the server, the listener receives an additional `player` argument. Returns a cleanup function to disconnect the listener. ```typescript // TypeScript — server/todos.server.ts import { remotes } from "shared/remotes"; // Server-side: player argument prepended automatically const disconnect = remotes.addTodo.connect((player, name) => { print(`${player.Name} added todo: ${name}`); // Expected output: "Player1 added todo: Buy groceries" }); // Disconnect when no longer needed // disconnect(); // Client-side: remotes.todosChanged is a ClientRemote // todos.client.ts remotes.todosChanged.connect((todos) => { print(`Received ${todos.size()} todos`); // Expected output: "Received 2 todos" }); ``` ```lua -- Luau local remotes = require(ReplicatedStorage.Shared.remotes) -- Server local disconnect = remotes.addTodo:connect(function(player, name) print(player.Name .. " added: " .. name) end) -- Client remotes.todosChanged:connect(function(todos) print("Got", #todos, "todos") end) ``` -------------------------------- ### Throttle Middleware Source: https://github.com/littensy/remo/blob/main/README.md Explains `throttleMiddleware` for limiting remote invocation frequency and its options. ```APIDOC ## Throttle Middleware ### Description `throttleMiddleware(options?)` limits remote invocations to once per specified `throttle` period (in seconds). ### Options - `trailing` (boolean): If true, the last event is fired again after the throttle period. Does not apply to async functions. ### Behavior for Async Remotes If an async remote is throttled or still processing a request, the promise resolves with the last invocation's result. If no previous value exists, the promise rejects. ### Usage ```lua -- Example usage would involve applying this middleware during remote creation or configuration. -- Specific syntax depends on the Remo library's middleware application pattern. -- Example: Remo.remote(t.number).use(Remo.throttleMiddleware({ throttle = 5, trailing = true })) ``` ``` -------------------------------- ### Apply loggerMiddleware Globally in TypeScript Source: https://context7.com/littensy/remo/llms.txt Apply loggerMiddleware globally during the creation of remotes to log all remote invocations. This provides detailed console output for each remote call. ```typescript import { createRemotes, loggerMiddleware, remote } from "@rbxts/remo"; import { t } from "@rbxts/t"; export const remotes = createRemotes( { addTodo: remote(t.string), getTodos: remote().returns(t.array(t.string)), }, loggerMiddleware, // logs all remotes ); // Console output when client fires addTodo("Buy milk"): // 🟡 (client → server) addTodo // 1. "Buy milk" // Console output when client calls getTodos(): // 🟣 (client → server async) getTodos // Parameters // 1. (void) // Returns // 1. ["Milk","Eggs"] ``` -------------------------------- ### Apply Middleware to Single Remote in TypeScript Source: https://github.com/littensy/remo/blob/main/README.md Applies the loggerMiddleware to a specific remote named 'event' during the creation of remotes. Requires 'createRemotes' and 'remote' functions. ```typescript const remotes = createRemotes( { event: remote(t.number).middleware(loggerMiddleware), }, ...middleware ); ``` -------------------------------- ### Fire Remote Events with `fire`, `fireAll`, `firePlayers`, `fireAllExcept` Source: https://context7.com/littensy/remo/llms.txt These methods send remote events across the client-server boundary. `fire` on the client sends to the server. On the server, `fire(player, ...)` targets a specific player. `fireAll`, `firePlayers`, and `fireAllExcept` are server-only for broadcasting. ```typescript // TypeScript — server/broadcasting.server.ts import { remotes } from "shared/remotes"; // Fire to a specific player remotes.todosChanged.fire(somePlayer, ["Milk", "Eggs"]); // Fire to all connected players remotes.todosChanged.fireAll(["Milk", "Eggs"]); // Fire to a subset of players const vipPlayers = [player1, player2]; remotes.todosChanged.firePlayers(vipPlayers, ["Milk", "Eggs"]); // Fire to everyone except one player remotes.todosChanged.fireAllExcept(somePlayer, ["Milk", "Eggs"]); ``` ```lua -- Luau — server local remotes = require(ReplicatedStorage.Shared.remotes) remotes.todosChanged:fire(somePlayer, {"Milk", "Eggs"}) remotes.todosChanged:fireAll({"Milk", "Eggs"}) remotes.todosChanged:firePlayers({player1, player2}, {"Milk", "Eggs"}) remotes.todosChanged:fireAllExcept(somePlayer, {"Milk", "Eggs"}) -- Client fires to server remotes.addTodo:fire("Buy groceries") ``` -------------------------------- ### Handling Async Function Requests Source: https://github.com/littensy/remo/blob/main/README.md Details how to bind a handler to a remote asynchronous function using `onRequest` and manage its return values or errors. ```APIDOC ## Handling Async Function Requests ### Description Uses the `onRequest` method to bind a handler function to a remote asynchronous function. The handler can return a value or a promise. If the handler errors or the promise rejects, the caller receives a promise rejection. ### Client to Server Async Handler ```lua -- client -> server async remotes.async:onRequest(function(player, ...) return result end) ``` ### Server to Client Async Handler ```lua -- server -> client async remotes.async:onRequest(function(...) return result end) ``` ``` -------------------------------- ### Define Client and Server Remotes (TypeScript) Source: https://github.com/littensy/remo/blob/main/README.md Defines remotes with explicit `Client` or `Server` flags to specify processing context. Includes argument validation using `t`. ```typescript const remotes = createRemotes({ // event processed on the client and fired by the server client: remote(t.number), // event processed on the server and fired by the client server: remote(t.number), }); ``` -------------------------------- ### Logger Middleware Source: https://github.com/littensy/remo/blob/main/README.md Introduces `loggerMiddleware` for detailed logging of remote invocation arguments and return values. ```APIDOC ## Logger Middleware ### Description `loggerMiddleware` provides detailed logs for arguments and return values during remote invocations. ### Usage ```lua -- Example usage would involve applying this middleware during remote creation or configuration. -- Specific syntax depends on the Remo library's middleware application pattern. ``` ``` -------------------------------- ### Firing Remote Events Source: https://context7.com/littensy/remo/llms.txt Methods like `fire`, `fireAll`, `firePlayers`, and `fireAllExcept` are used to send remote events across the client-server boundary. Usage varies depending on whether you are on the client or server. ```APIDOC ## `remote.fire(...args)` / `remote.fireAll(...)` / `remote.firePlayers(...)` / `remote.fireAllExcept(...)` — Fire a remote event These methods send a remote event to the other side of the client–server boundary. `fire` on the client sends to the server; `fire(player, ...)` on the server sends to a specific player. `fireAll`, `firePlayers`, and `fireAllExcept` are server-only convenience methods for broadcasting to multiple clients. ```ts // TypeScript — server/broadcasting.server.ts import { remotes } from "shared/remotes"; // Fire to a specific player remotes.todosChanged.fire(somePlayer, ["Milk", "Eggs"]); // Fire to all connected players remotes.todosChanged.fireAll(["Milk", "Eggs"]); // Fire to a subset of players const vipPlayers = [player1, player2]; remotes.todosChanged.firePlayers(vipPlayers, ["Milk", "Eggs"]); // Fire to everyone except one player remotes.todosChanged.fireAllExcept(somePlayer, ["Milk", "Eggs"]); ``` ```lua -- Luau — server local remotes = require(ReplicatedStorage.Shared.remotes) remotes.todosChanged:fire(somePlayer, {"Milk", "Eggs"}) remotes.todosChanged:fireAll({"Milk", "Eggs"}) remotes.todosChanged:firePlayers({player1, player2}, {"Milk", "Eggs"}) remotes.todosChanged:fireAllExcept(somePlayer, {"Milk", "Eggs"}) -- Client fires to server remotes.addTodo:fire("Buy groceries") ``` ``` -------------------------------- ### getSender Source: https://github.com/littensy/remo/blob/main/README.md Returns the player that sent the remote invocation, useful for server-side middleware. ```APIDOC ## `getSender(...)` ### Description Returns the player that sent the remote invocation using the arguments passed to the remote. This checks whether the first argument is a table or an instance with a `ClassName` property equal to `"Player"`. This is used for finding the `player` argument in a middleware function on the server. ### Parameters - `...args` - The arguments passed to the remote. ### Returns `getSender` returns the player that sent the remote invocation, or `undefined` if the remote was not invoked by a player. ``` -------------------------------- ### remote.promise(predicate?, mapper?) Source: https://context7.com/littensy/remo/llms.txt Awaits a single remote event as a Promise, optionally filtered by a predicate and transformed by a mapper. ```APIDOC ## `remote.promise(predicate?, mapper?)` — Await a single remote event as a Promise `promise` returns a Promise that resolves the next time the remote fires, optionally filtered by a `predicate` function. An optional `mapper` transforms the tuple arguments (useful in Roblox-TS where Promises don't support tuples). ### TypeScript Example ```ts // wait for a specific player's todo addition remotes.addTodo .promise( (player, name) => player === targetPlayer && name.size() > 0, // predicate (player, name) => ({ player, name }), // mapper: tuple → object ) .then(({ player, name }) => { print(`${player.Name} added: ${name}`); }); // Client: wait for the next todosChanged event remotes.todosChanged .promise( (todos) => todos.size() > 0, (todos) => todos, ) .then((todos) => { print(`First non-empty todos: ${todos.join(", ")}`); }); ``` ``` -------------------------------- ### Defining Remotes Source: https://github.com/littensy/remo/blob/main/README.md Defines the structure for remotes, including client-to-server, server-to-client, and server-asynchronous remotes, along with their types and return types. ```APIDOC ## Defining Remotes ### Description Defines the type for various remotes: `ClientToServer`, `ServerToClient`, and `ServerAsync`. It also shows how to create these remotes using `Remo.createRemotes` and `Remo.remote`, specifying argument and return types. ### Luau Type Definitions ```lua type Remotes = { client: Remo.ServerToClient, server: Remo.ClientToServer, serverAsync: Remo.ServerAsync<(number), (string)>, namespace: { client: Remo.ServerToClient, server: Remo.ClientToServer, } } ``` ### Creating Remotes ```lua local remotes: Remotes = Remo.createRemotes({ client = Remo.remote(t.number), server = Remo.remote(t.number), serverAsync = Remo.remote(t.number).returns(t.string), namespace = Remo.namespace({ client = Remo.remote(t.number), server = Remo.remote(t.number), }), }) ``` ``` -------------------------------- ### Fire Remote Events in Luau Source: https://github.com/littensy/remo/blob/main/README.md Use the `fire` method to send arguments over a remote event. Specify the target player for server-to-client events, or use `fireAll`, `fireAllExcept`, and `firePlayers` for broader distribution. ```lua -- client -> server remotes.event:fire(...); -- server -> client remotes.event:fire(player, ...); remotes.event:fireAll(...); remotes.event:fireAllExcept(player, ...); remotes.event:firePlayers(players, ...); ``` -------------------------------- ### Group Remotes with `namespace` Source: https://context7.com/littensy/remo/llms.txt Use `namespace` to create nested groups of remotes within a schema. This function accepts the same schema format as `createRemotes` and can be nested deeply. Remotes are accessed via the namespace key on the returned object. ```typescript import { Client, Server, createRemotes, namespace, remote } from "@rbxts/remo"; import { t } from "@rbxts/t"; export const remotes = createRemotes({ player: namespace({ joined: remote(t.number), left: remote(t.number), dataLoaded: remote(), }), inventory: namespace({ addItem: remote(t.string), removeItem: remote(t.string), sync: remote(t.array(t.string)), }), }); // Server usage remotes.player.joined.fire(somePlayer, somePlayer.UserId); remotes.inventory.addItem.connect((player, itemId) => { print(`${player.Name} wants to add item: ${itemId}`); }); // Client usage remotes.player.joined.connect((userId) => { print(`Player ${userId} joined`); }); remotes.inventory.addItem.fire("sword_01"); ``` -------------------------------- ### remote Source: https://github.com/littensy/remo/blob/main/README.md Declares a remote to be used in the remote schema, returning a RemoteBuilder. ```APIDOC ## `remote(...validators?)` ### Description Declares a remote to be used in the remote schema. ### Parameters - `...validators` - A list of validators to call before processing the remote. ### Returns `remote` returns a RemoteBuilder, which can be used to define a remote. It has the following functions: - `remote.returns(validator)` - Declares that this is an async remote that returns a value of the given type. - `remote.middleware(...middleware)` - Applies the given middleware to this remote. - `remote.unreliable()` - Marks this remote as an [unreliable remote event](https://devforum.roblox.com/t/introducing-unreliableremoteevents/2724155). ``` -------------------------------- ### Create Logger Middleware in Luau Source: https://github.com/littensy/remo/blob/main/README.md Defines a Luau middleware function that logs remote invocation arguments and return values. Ensure Remo.Middleware type is available. ```lua local loggerMiddleware: Remo.Middleware = function(next, remote) return function(...) if remote.type == "event" then print(`{remote.name} fired with arguments:`, ...) return next(...) end print(`{remote.name} called with arguments:`, ...) local result = next(...) print(`{remote.name} returned:`, result) return result end end ``` -------------------------------- ### TypeScript Throttle Middleware Options Interface Source: https://github.com/littensy/remo/blob/main/README.md Defines the optional configuration object for the throttleMiddleware in TypeScript, specifying throttle duration and trailing event behavior. ```typescript interface ThrottleMiddlewareOptions { throttle?: number; trailing?: boolean; } ``` -------------------------------- ### remote.onRequest(handler) Source: https://context7.com/littensy/remo/llms.txt Binds a handler to process incoming asynchronous remote function requests. The handler can return a value or a Promise. ```APIDOC ## `remote.onRequest(handler)` — Bind a handler to an async remote function `onRequest` registers the handler that processes incoming requests. The handler receives `(player, ...args)` on the server and may return a value directly or a Promise. If the handler throws or the Promise rejects, the caller's Promise will reject with the same error. ### TypeScript Example ```ts const todos: string[] = ["Milk", "Eggs"]; remotes.getTodos.onRequest((player) => { print(`${player.Name} requested todos`); return todos; // can also return Promise.resolve(todos) }); // Async handler returning a Promise remotes.getTodos.onRequest((player) => { return Promise.resolve(todos).tap(() => { print(`Served ${todos.size()} todos to ${player.Name}`); }); }); ``` ### Luau Example ```lua local remotes = require(ReplicatedStorage.Shared.remotes) local todos = {"Milk", "Eggs"} remotes.getTodos:onRequest(function(player) return todos end) ``` ``` -------------------------------- ### remote.request(...args) Source: https://context7.com/littensy/remo/llms.txt Invokes an asynchronous remote function, sending a request and returning a Promise that resolves with the handler's return value. ```APIDOC ## `remote.request(...args)` — Invoke an async remote function `request` sends a request to the other side and returns a Promise that resolves with the handler's return value. On the client, it invokes the server handler; on the server (deprecated), it invokes the client handler. Arguments are validated before the handler is called; the return value is validated before the Promise resolves. ### TypeScript Example ```ts // Request the full todo list from the server remotes.getTodos .request() .then((todos) => { print(`Server has ${todos.size()} todos: ${todos.join(", ")}`); }) .catch((err) => { warn(`Failed to get todos: ${err}`); }); ``` ### Luau Example ```lua remotes.getTodos:request():andThen(function(todos) print("Got todos:", table.concat(todos, ", ")) end):catch(function(err) warn("Failed:", err) end) ``` ``` -------------------------------- ### loggerMiddleware Source: https://github.com/littensy/remo/blob/main/README.md A middleware function that creates detailed logs of remote invocation arguments and return values. ```APIDOC ## `loggerMiddleware` ### Description Creates detailed logs of the arguments and return values of a remote invocation. ```ts const loggerMiddleware: RemoMiddleware; ``` ``` -------------------------------- ### TypeScript Throttle Middleware Function Signatures Source: https://github.com/littensy/remo/blob/main/README.md Signatures for the throttleMiddleware function in TypeScript, showing overloaded versions for direct throttle value or a configuration object. ```typescript function throttleMiddleware(options?: ThrottleMiddlewareOptions): RemoMiddleware; function throttleMiddleware(throttle?: number): RemoMiddleware; ``` -------------------------------- ### Handle Remote Async Function Requests in Luau Source: https://github.com/littensy/remo/blob/main/README.md Use `onRequest` to bind a handler to a remote function. The handler can return a value or a promise. If the handler throws an error or the promise rejects, the caller receives a promise rejection. ```lua -- client -> server async remotes.async:onRequest(function(player, ...) return result end) -- server -> client async remotes.async:onRequest(function(...) return result end) ``` -------------------------------- ### Await Single Remote Event with `promise` Source: https://context7.com/littensy/remo/llms.txt Returns a Promise that resolves when the remote event fires next, optionally filtered by a predicate and transformed by a mapper. Useful for waiting for specific event occurrences. ```typescript // TypeScript — wait for a specific player's todo addition import { remotes } from "shared/remotes"; remotes.addTodo .promise( (player, name) => player === targetPlayer && name.size() > 0, // predicate (player, name) => ({ player, name }), // mapper: tuple → object ) .then(({ player, name }) => { print(`${player.Name} added: ${name}`); // Expected output: "Player1 added: Buy milk" }); // Client: wait for the next todosChanged event remotes.todosChanged .promise( (todos) => todos.size() > 0, (todos) => todos, ) .then((todos) => { print(`First non-empty todos: ${todos.join(", ")}`); }); ``` -------------------------------- ### Bind Handler to Async Remote Function with `onRequest` Source: https://context7.com/littensy/remo/llms.txt Registers a handler to process incoming requests for an asynchronous remote function. The handler can return a value or a Promise. Rejections will reject the caller's Promise. ```typescript // TypeScript — server/todos.server.ts import { remotes } from "shared/remotes"; const todos: string[] = ["Milk", "Eggs"]; remotes.getTodos.onRequest((player) => { print(`${player.Name} requested todos`); return todos; // can also return Promise.resolve(todos) }); // Async handler returning a Promise remotes.getTodos.onRequest((player) => { return Promise.resolve(todos).tap(() => { print(`Served ${todos.size()} todos to ${player.Name}`); }); }); ``` ```lua -- Luau — server local remotes = require(ReplicatedStorage.Shared.remotes) local todos = {"Milk", "Eggs"} remotes.getTodos:onRequest(function(player) return todos end) ``` -------------------------------- ### Create Logger Middleware in TypeScript Source: https://github.com/littensy/remo/blob/main/README.md Defines a TypeScript middleware function that logs remote invocation arguments and return values. Ensure RemoMiddleware type is available. ```typescript const loggerMiddleware: RemoteMiddleware = (nextFn, remote) => { return (...args: unknown[]) => { if (remote.type === "event") { print(`${remote.name} fired with arguments:`, ...args); return nextFn(...args); } print(`${remote.name} called with arguments:`, ...args); const result = nextFn(...args); print(`${remote.name} returned:`, result); return result; }; }; ``` -------------------------------- ### Invoke Async Remote Function with `request` Source: https://context7.com/littensy/remo/llms.txt Sends a request to the other side and returns a Promise that resolves with the handler's return value. Use this to call remote functions asynchronously. ```typescript // TypeScript — client/todos.client.ts import { remotes } from "shared/remotes"; // Request the full todo list from the server remotes.getTodos .request() .then((todos) => { print(`Server has ${todos.size()} todos: ${todos.join(", ")}`); // Expected output: "Server has 2 todos: Milk, Eggs" }) .catch((err) => { warn(`Failed to get todos: ${err}`); }); ``` ```lua -- Luau — client local remotes = require(ReplicatedStorage.Shared.remotes) remotes.getTodos:request():andThen(function(todos) print("Got todos:", table.concat(todos, ", ")) end):catch(function(err) warn("Failed:", err) end) ``` -------------------------------- ### throttleMiddleware Source: https://github.com/littensy/remo/blob/main/README.md A middleware function that prevents a remote from being invoked more than once within a specified time interval. ```APIDOC ## `throttleMiddleware(options?)` ### Description Prevents a remote from being invoked more than once every `throttle` seconds. ### Parameters - `options` - An optional object with the following properties: - `throttle` - The number of seconds to throttle the remote for. Defaults to `0.1`. - `trailing` - If `true`, the last event will be fired again after the throttle period has passed. Does not apply to async functions. Defaults to `false`. ```