Claude
Skill
roblox-networking
Use when validating RemoteEvent or RemoteFunction arguments, adding rate limits, designing server-authoritative systems, or preventing exploits.
Virus-scanned
Reviewed automatically before listing.
Download
tabooharmony-roblox-brain-skills_core_roblox-networking-6051c35.zip · 10 KB
Install
skills CLI
npx skills add https://github.com/TabooHarmony/roblox-brain/tree/main/skills/core/roblox-networking
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tabooharmony-roblox-brain@llmmart
Git
git clone https://github.com/TabooHarmony/roblox-brain.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole tabooharmony/roblox-brain collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
roblox networking
When to Load
Load when adding a remote, handling untrusted input, implementing cooldowns, or deciding which side owns a result.
Quick Reference
- Treat every client argument as attacker-controlled input.
- Validate type, size, ownership, state, distance, and cooldown on the server.
- Look up prices, damage, rewards, and permissions from server-owned definitions.
- Choose the authority model before designing movement or continuous simulation: Server Authority uses client prediction and server rollback, and is not
SetNetworkOwner. - For simulation input under Server Authority use
InputAction/InputContextwithRunService:BindToSimulation(), not aRemoteEvent. - Use events for most gameplay requests. Keep
RemoteFunctioncalls short and bounded. - Use
RemoteEventfor reliable state changes. It is not ordered relative to property or attribute replication: use one explicit state channel or version the state when ordering matters. ReserveUnreliableRemoteEventfor replaceable data such as VFX and snapshots. - Unreliable is not automatically faster: delivery is unordered, packets may be dropped, and payloads should stay at or below the documented 1000-byte limit.
- Measure payload size and fire rate under load; packet-size estimators are not an official wire-format spec.
- Check numbers for NaN and infinity (
x ~= x,math.abs(x) == math.huge) first:NaNdefeats</>. Strings:utf8.len(s)catches malformed UTF-8 that fails a DataStore save. - Serialization decides validation: functions arrive as
nil, metatables are stripped, mixed-key tables are mangled,nilin a table truncates the payload, and tables are copies, not references. Validate field by field; share state via server-owned snapshots or ids. - Server Authority needs
Workspace.AuthorityMode = Serverplus the full bundle: NextGenerationReplication, PlayerScriptsUseInputActionSystem, deferred SignalBehavior, UseFixedSimulation, StreamingEnabled. Misprediction and rollback are normal (debug surface in full.md). - Rate limits protect the server; validation still rejects invalid requests.
- Record suspicious behavior with thresholds; never punish one malformed packet.
- Edit-mode play: wrap the network layer so
RunContext:IsEdit()gets a local loopback mock (full.md).
Need details? references/full.md has validation, throttling, and mock patterns.
Files (roblox-brain)
-
references
-
full.md 23.5 KB
# roblox networking: full reference > Code examples are illustrative. Adapt them to your project and verify in Studio before production use. Networking is an API boundary, not a trust boundary. Anything running in a player's client can be inspected, modified, or called outside the intended UI flow. ## When to Load Use this when creating or reviewing remotes, synchronizing gameplay state, or hardening a server handler. Choose the authority model before designing continuous movement or physics. ## 0. Server Authority model Server Authority is an opt-in Roblox model configured through `Workspace.AuthorityMode = Server` and its required replication, fixed-simulation, streaming, and input settings. The server owns the authoritative core simulation while clients predict input and recover from misprediction through rollback and resimulation. For simulation-affecting input, use the Input Action System (`InputAction` and `InputContext`) and mirror synchronized logic through `RunService:BindToSimulation()` (requires `Workspace.UseFixedSimulation` enabled in Studio). Use `RemoteEvent` for discrete requests or notifications, not as a replacement for the continuous input path. The model does not remove server-side validation for custom attacks, purchases, teleports, permissions, or other game-specific actions. ### Settings bundle and debug surface Server Authority is only real when the whole settings bundle travels together. Setting `Workspace.AuthorityMode = Enum.AuthorityMode.Server` automatically sets the other five; verify all six during review because a place file can drift: 1. `Workspace.AuthorityMode` = `Enum.AuthorityMode.Server` 2. `Workspace.NextGenerationReplication` enabled 3. `Workspace.PlayerScriptsUseInputActionSystem` enabled 4. `Workspace.SignalBehavior` = `Enum.SignalBehavior.Deferred` 5. `Workspace.UseFixedSimulation` enabled 6. `Workspace.StreamingEnabled` enabled Misprediction and rollback are normal operation, not defects: clients cannot predict other players' inputs, so corrections should be small and imperceptible when tuned. On a detected misprediction the client resets to the server's authoritative state and resimulates its predicted frames. Debug surface for review sessions: - Studio ships a server authority visualization overlay for review sessions, but its shortcuts, counters, and per-reason input-drop tallies are not documented on the pages reviewed here. Do not quote specific numbers from it as if they were published thresholds; describe what you observed instead. - Read prediction state from the scriptable surface instead: `RunService:SetPredictionMode()` forces prediction for a given instance and is client-only, and `Instance.PredictionMode` reflects the mode applied to that instance. ## 1. Define the request contract Write the contract before writing the handler: - who may call it; - argument types and maximum sizes; - what game state must be true; - how often a player may call it; - what the server sends back on success or failure. Put remotes in a predictable replicated folder. Keep configuration tables shared only when their contents are safe for clients to read. ```luau -- ReplicatedStorage/Shared/Net.luau local ReplicatedStorage = game:GetService("ReplicatedStorage") local remotes = ReplicatedStorage:WaitForChild("Remotes") return { ClaimQuest = remotes:WaitForChild("ClaimQuest"), BuyItem = remotes:WaitForChild("BuyItem"), } ``` ## 2. Validate at the server boundary `typeof` checks are only the first layer. Validate the value against server state and trusted definitions. ```luau local function validItemRequest(player: Player, itemId: unknown, amount: unknown): (boolean, string?) if typeof(itemId) ~= "string" or #itemId > 40 then return false, "bad item id" end if typeof(amount) ~= "number" or amount ~= amount or math.abs(amount) == math.huge or amount % 1 ~= 0 or amount < 1 or amount > 20 then return false, "bad amount" end local item = ItemDefinitions[itemId] if not item or not item.Tradeable then return false, "item unavailable" end if InventoryService:GetCount(player, itemId) < amount then return false, "not owned" end return true end TradeRemote.OnServerEvent:Connect(function(player, itemId, amount) local ok = validItemRequest(player, itemId, amount) if not ok then return end InventoryService:Remove(player, itemId, amount) end) ``` Do not send a detailed failure reason to an untrusted caller if it reveals private state. Log enough context for operators without logging secrets or raw payloads indefinitely. ### Numeric and string poison checks Two cheap checks belong next to every `typeof` guard because both defeat naive validation and both kill DataStore saves downstream: - **NaN / infinity:** `NaN ~= NaN`, so equality guards pass it through, and comparisons like `amount < limit` return false for `NaN`, skipping range checks silently. Reject with `x ~= x or math.abs(x) == math.huge`. - **Malformed UTF-8:** client-supplied strings may contain invalid byte sequences that DataStores refuse to serialize. `utf8.len(s)` returns `nil` plus an error position for malformed input; reject when it does not return a count. Rejecting these at the remote boundary protects both the gameplay logic and the persistence layer (`roblox-data` covers the save-side contract). ## 2a. What survives a remote call Remote arguments are serialized, not shared. Verified against the [remote events and functions](https://create.roblox.com/docs/scripting/events/remote) docs: | What you send | What arrives | Consequence | |---------------|--------------|-------------| | Function | `nil` | Functions are not replicated; the receiving argument is `nil`. | | Table with a metatable | Plain table, metatable lost | All metatable information is stripped in transfer; `__index`-backed methods are gone on arrival. | | Mixed table (numeric + string keys) | Mangled data | Pass all key-value (dictionary) or all numeric indices, never both. Non-string indices (Instance, userdata, function) are converted to strings. | | Table with `nil` in an index | Truncated payload | Avoid `nil` values in any index of a passed table. | | Table | Copy, not a reference | Table identity differs on arrival and on return; mutating a "shared" table only mutates the local copy. | | Instance only the sender can see | `nil` | Server-only instances (e.g. under `ServerStorage`) and client-created parts are not replicable across the boundary. | Practical consequences: - **Type checking:** a `typeof` guard per argument is only the entry check. Because functions arrive as `nil`, non-string keys get stringified, and any "table" can be any shape, validate the fields and values of every table against the contract, not just its type. - **State sharing:** tables arrive as copies, so remotes cannot share mutable state. Keep authoritative state on the server and replicate explicit snapshots or deltas, or send stable identifiers (`UserId`, item ids) and re-resolve them from server state. ## 3. Keep outcomes server-owned The client may request "attack target X" or "buy item Y." It must not request "deal 100 damage" or "subtract 20 coins." The server calculates the result from current state. For combat, check at least: - the attacker is alive and allowed to act; - the target exists and is in the relevant world or match; - the weapon is equipped and its cooldown has elapsed; - the reported position is plausible for the player's character; - the target is within server-calculated range or line of sight when required. A client-side hit effect is presentation. The server's damage decision is the game result. ## 4. Per-player throttling Use a monotonic clock and clean entries when players leave. A limiter should reject bursts without turning normal network jitter into a ban. ```luau local Players = game:GetService("Players") local lastCall: {[Player]: {[string]: number}} = {} local intervalByAction = { BuyItem = 0.25, ClaimQuest = 0.5, } local function allowed(player: Player, action: string): boolean local now = os.clock() local playerCalls = lastCall[player] if not playerCalls then playerCalls = {} lastCall[player] = playerCalls end local previous = playerCalls[action] local interval = intervalByAction[action] or 0.2 if previous and now - previous < interval then return false end playerCalls[action] = now return true end Players.PlayerRemoving:Connect(function(player) lastCall[player] = nil end) ``` For expensive work, add a token or queue budget as well as a simple cooldown. The limit should be attached to the action, not copied blindly to every remote. ## 5. RemoteFunction cautions A `RemoteFunction` makes one side wait for a response. Use it for a small query with a clear timeout strategy, not for a long-running transaction or a callback that can block server work. Prefer this shape for mutations: 1. client fires a request event; 2. server validates and applies it; 3. server sends an acknowledgement or replicates the changed state. If a request must be idempotent, include a server-checked request identifier and retain only the small amount of history needed to reject duplicates. ## 6. Choose the remote semantics Use the least powerful transport that preserves the gameplay contract: - `RemoteEvent` for reliable messages such as inventory mutations, accepted hits, and state transitions. Its delivery is not a general ordering guarantee relative to property or attribute replication. - `UnreliableRemoteEvent` for replaceable snapshots, aim previews, particles, sound cues, and other data that is stale as soon as a newer update exists. - `RemoteFunction` only for short request-response queries with bounded work and an explicit failure path. Unreliable events are not a free bandwidth or latency upgrade. Roblox may drop them, does not guarantee ordering against other traffic, and documents a 1000-byte payload ceiling. Under Server Authority, RemoteEvents may also be observed out of order relative to property and attribute updates. If ordering matters, use one explicit state channel or carry a version/request identifier. Never use unreliable events for currency, inventory, purchases, damage, or any result that must arrive exactly once. ```luau -- ReplicatedStorage/Remotes/Effects is an UnreliableRemoteEvent. local Effects = game:GetService("ReplicatedStorage").Remotes.Effects -- The event carries presentation data only. The server still decides whether -- the underlying gameplay action happened. Effects:FireAllClients("MuzzleFlash", muzzlePosition, direction) ``` For typed wrappers, `RbxUtil` exposes `TypedRemote` and `Comm`. They can improve discoverability and middleware structure, but they do not validate attacker-controlled values for you. Keep the server contract and validation visible at the handler boundary. ## 7. Measure packet budgets Track both payload size and frequency. A small payload fired every frame can be worse than a larger payload sent occasionally. The community `RemotePacketSizeCounter` resource is useful for estimating supported datatype sizes and testing the 1000-byte unreliable-event ceiling, but its own documentation notes that some Roblox encoding behavior is undocumented and has edge cases. Record at least: - remote name and direction; - calls per second; - estimated bytes per call and bytes per second; - reliable versus unreliable transport; - player count and representative latency. Do this in a test place with realistic load. A local ping measurement is not a network-budget benchmark. ## 7a. Replicate state to subscribed clients For a state object that many clients must observe, avoid re-sending whole tables per change. Keep the state server-owned and let clients subscribe by a token or name; each client receives an initial snapshot plus delta updates for only the fields that changed. Community replication modules (e.g. Replica, successor to ReplicaService, https://devforum.roblox.com/t/replica-server-to-client-state-replication-module/3216980) implement this pattern; you can also build it with a single state RemoteEvent carrying a versioned delta. Keep creation and mutation server-side so the client subscription is a read-only mirror. ## 7b. Shrink payloads with binary serialization When a high-frequency remote exceeds budget, replace high-precision tables with compact typed fields. Pick the smallest precision that reads correctly per field (a quantized `CFrame` or low-bit float for positions, a small integer for counters) rather than always sending 64-bit values. Community serialization modules (e.g. Bitstream, https://devforum.roblox.com/t/bitstream-%E2%80%93-binary-framework/4788654) provide typed, precision-varied formats; keep a schema/version so both sides agree on field order and size. Prefer this for replaceable, high-frequency data (positions, aim), not for state that must be exactly once and easily debugged. ## 8. Movement and physics checks Do not compare a client's position to a fixed speed threshold without accounting for legitimate teleports, seats, network ownership, respawns, and server corrections. In a Server Authority project, do not add a blanket `Heartbeat` CFrame correction loop; keep synchronized movement logic in `BindToSimulation()` and validate only custom movement or action transitions. In classic projects, use server-side state transitions and tolerance windows. A suspicious score is usually safer than an immediate kick: - collect several independent violations; - clear or decay the score after normal behavior; - notify operators or apply a limited response at a threshold; - never let the score itself grant or remove valuable items. ## 9. Client/server test matrix Test handlers without the expected UI path: - wrong types and oversized strings; - missing or foreign instance references; - requests before the player is loaded; - duplicate and out-of-order requests; - rapid bursts; - player removal during a request; - legitimate high-latency and respawn cases. The goal is not to make the client impossible to modify. The goal is to make modification unable to create an unearned authoritative outcome. ### Edit-mode network mock Client code that requires a network module fails to load in edit mode (no player, no server). Wrap the network layer so edit mode gets a local loopback instead, and keep the wrapper behind one module boundary so call sites never branch on context themselves: ```luau -- Network/init.luau local RunContext = require(Shared.RunContext) local Network if RunContext.IsEdit then Network = require(script.mock) :: any -- loopback events/functions else Network = require(Packages.YourNetworkLayer) end return Network ``` The mock implements the same surface (`Event`, `Function`, or your project's equivalents) but fires signals locally instead of over remotes. Client and shared modules can then run and be exercised in Studio without a play session, and remote-specific bugs stay confined to code that actually runs online. The same split pattern applies to any server-only dependency a client-facing module would otherwise touch. Note the loopback skips real serialization and validation, so behavior differences found in edit mode are not conclusive: re-test through real remotes before shipping. ## 10. Text chat: TextChatService (modern) and legacy Chat [TextChatService](https://create.roblox.com/docs/reference/engine/classes/TextChatService) is the current chat system. The legacy chat system was retired April 30, 2025: Roblox auto-migrates experiences still on `ChatVersion.LegacyChatService`, and custom integrations that bypass TextChatService break or get moderated ([migration announcement](https://devforum.roblox.com/t/migrate-to-textchatservice-removing-support-for-legacy-chat-and-custom-chat-systems/3237100), [status update](https://devforum.roblox.com/t/update-on-legacy-chat-deprecation-and-textchatservice-migration/3376880)). <!-- temporal: 2025-05 --> `TextChatService.ChatVersion` is not scriptable; set it in Studio. Never build new chat features on the legacy `Chat` service. ### Instance tree Default runtime tree when `TextChatService.CreateDefaultTextChannels` and `CreateDefaultCommands` are true (both are Studio properties, not scriptable): - `TextChatService.TextChannels` (Folder): `RBXGeneral` (player messages), `RBXSystem` (system messages; red when `TextChatMessage.Metadata` contains "Error"), `RBXTeam[BrickColor]` per team, `RBXWhisper:[UserId1]_[UserId2]` per whisper pair. - `TextChatService.TextChatCommands` (Folder): `RBXClearCommand`, `RBXEmoteCommand`, `RBXHelpCommand`, `RBXMuteCommand`, `RBXTeamCommand`, `RBXWhisperCommand`, and others (`/e`, `/t`, `/w`, `/mute`, ...). - Configuration singletons directly under `TextChatService`: `ChatWindowConfiguration`, `ChatInputBarConfiguration`, `BubbleChatConfiguration`, `ChannelTabsConfiguration`. You can add your own `TextChannel` and `TextChatCommand` instances even with defaults on. A `TextChatCommand` must be parented to `TextChatService` to function. ### Client/server split ([TextChannel](https://create.roblox.com/docs/reference/engine/classes/TextChannel)) - `TextChannel:SendAsync(message, metadata)` (client only). Sends a player message to the server; the engine filters it server-side, and clients receive "the result of the filtered message from the server". Metadata over 200 characters means the message is not delivered. - `TextChannel:DisplaySystemMessage(message, metadata)` (client only). Visible only to that local user and **not** automatically filtered or localized. - `TextChannel.MessageReceived` / `TextChatService.MessageReceived` (client only). - `TextChannel:AddUserAsync(userId)` (server only). Adds a `TextSource`; returns `nil, false` when the user has chat off or is not in the server. - Server-side delivery control: `TextChannel.ShouldDeliverCallback(message, textSource)` (return `false` to withhold from a recipient) plus `TextChatService:CanUserChatAsync` / `CanUsersChatAsync` / `CanUsersDirectChatAsync` for platform permission gates. `OnIncomingMessage` (on both `TextChatService` and `TextChannel`) is documented client-only: it decorates or replaces messages for display by returning `TextChatMessageProperties`; returning `nil` leaves the message unchanged. `TextChatService.OnIncomingMessage` runs before any `TextChannel.OnIncomingMessage`. Define each callback exactly once: multiple bindings override one another nondeterministically. Messages are not replicated to a custom UI by themselves; the default chat UI consumes `MessageReceived` for you, and a custom UI must render those payloads itself. ### Filtering rules - Player messages sent via `TextChannel:SendAsync` are filtered by the engine server-side; do not double-filter before `SendAsync`. - `DisplaySystemMessage` strings are not filtered. Static developer-authored text is fine. If a system message embeds player input (names, item names), filter that input server-side with `TextService:FilterStringAsync` first; the same rule applies as for any other user-generated text. ### Example: custom channel plus `/heal` command ```luau -- ServerScriptService (server) local TextChatService = game:GetService("TextChatService") local Players = game:GetService("Players") local battleChannel = Instance.new("TextChannel") battleChannel.Name = "Battle" battleChannel.Parent = TextChatService.TextChannels local healCommand = Instance.new("TextChatCommand") healCommand.Name = "HealCommand" healCommand.PrimaryAlias = "/heal" healCommand.Parent = TextChatService.TextChatCommands healCommand.Triggered:Connect(function(textSource: TextSource, unfilteredText: string) local player = Players:GetPlayerByUserId(textSource.UserId) if not player then return end -- unfilteredText is attacker-controlled: parse/validate before use. -- Apply the heal server-side; check cooldown and permission here. end) Players.PlayerAdded:Connect(function(player) battleChannel:AddUserAsync(player.UserId) end) ``` ```luau -- StarterPlayerScripts (client) local TextChatService = game:GetService("TextChatService") local channels = TextChatService:WaitForChild("TextChannels") local battleChannel = channels:WaitForChild("Battle") battleChannel.MessageReceived:Connect(function(message: TextChatMessage) print(message.Text) -- default UI renders messages; a custom UI mirrors this end) -- Local confirmation that only this user sees: battleChannel:DisplaySystemMessage("You are healed", "Heal") TextChatService.OnIncomingMessage = function(message: TextChatMessage) if string.find(message.Metadata, "Error") then local props = Instance.new("TextChatMessageProperties") return props -- decorate error messages here end return nil end ``` When a sent message matches a `TextChatCommand` alias, the command sinks it server-side: `Triggered` fires and the message is not replicated to other users. ### Legacy Chat (deprecated; migration reference only) - `Chat:Chat(partOrCharacter, message, color?)` fires `Chat.Chatted` and drives the legacy bubble-chat LocalScript. Replace with `TextChatService:DisplayBubble()` and `BubbleChatConfiguration`. - `Chat:FilterStringAsync` / `Chat:FilterStringForBroadcast` filter legacy chat text; the client-side call form is deprecated. Replace with server-side `TextService:FilterStringAsync` ([Chat](https://create.roblox.com/docs/reference/engine/classes/Chat)). - `Chat:RegisterChatCallback` (`OnServerReceivingMessage`, `OnClientFormattingMessage`) customizes the legacy Luau chat pipeline. There is no 1:1 port; re-model the logic on `TextChannel.ShouldDeliverCallback` and the `OnIncomingMessage` callbacks. ## Networking checklist - [ ] Every remote has a documented contract. - [ ] Server handlers validate type, range, ownership, state, and rate. - [ ] Handlers never rely on remote tables being references: state is re-resolved server-side. - [ ] Handlers do not assume functions, metatables, or mixed-key tables survive the boundary. - [ ] Trusted values come from server definitions or server state. - [ ] Long work cannot be forced through an unbounded `RemoteFunction`. - [ ] Reliable and unreliable transports are chosen by data semantics, not by a blanket performance claim. - [ ] Packet size and fire rate are measured for high-frequency remotes. - [ ] Player cleanup removes limiter, subscription, and connection state. - [ ] Suspicion handling tolerates false positives and does not expose private data. - [ ] Server Authority projects: the six-setting bundle verified and prediction/rollback behavior understood before review sign-off. ## Community ecosystem (leads, not sources) Top-sorted DevForum canon for networking libraries. Verify status in-thread; several are archived. - State replication: [Replica](https://devforum.roblox.com/t/replica-server-to-client-state-replication-module/3216980) (2024, current favorite; pairs with ProfileStore per [PlayerState](https://devforum.roblox.com/t/playerstate-profilestore-replica-without-the-headache/3766568)); [ReplicaService](https://devforum.roblox.com/t/replicate-your-states-with-replicaservice-networking-system/894736) older. - Remote tooling: [Packet](https://devforum.roblox.com/t/packet-networking-library/3573907) (2025), [Warp](https://devforum.roblox.com/t/warp-very-fast-powerful-networking-library/2779813) (2024), [BridgeNet](https://devforum.roblox.com/t/bridgenet-insanely-optimized-easy-to-use-networking-library-full-of-utilities-now-with-roblox-ts-v199-beta/1909935) (legacy). - Case study: [60x bandwidth reduction in Astro Force](https://devforum.roblox.com/t/how-we-reduced-bandwidth-usage-by-60x-in-astro-force-roblox-rts/1202300), the practical RTS-scale optimization write-up. - [StreamX is DEPRECATED](https://devforum.roblox.com/t/deprecated-streamx-reduce-lag-and-prevent-map-cloning/1992484): do not recommend; example of a once-canonical library that died.
-
-
SKILL.md 2.9 KB
--- name: roblox-networking description: "Use when validating RemoteEvent or RemoteFunction arguments, adding rate limits, designing server-authoritative systems, or preventing exploits." last_reviewed: 2026-09-19 sources: - https://create.roblox.com/docs/scripting/events/remote - https://create.roblox.com/docs/scripting/security/security-tactics - https://create.roblox.com/docs/scripting/security/client-server-boundary - https://create.roblox.com/docs/projects/server-authority - https://create.roblox.com/docs/reference/engine/classes/UnreliableRemoteEvent - original --- # roblox networking ## When to Load Load when adding a remote, handling untrusted input, implementing cooldowns, or deciding which side owns a result. ## Quick Reference - Treat every client argument as attacker-controlled input. - Validate type, size, ownership, state, distance, and cooldown on the server. - Look up prices, damage, rewards, and permissions from server-owned definitions. - Choose the authority model before designing movement or continuous simulation: Server Authority uses client prediction and server rollback, and is not `SetNetworkOwner`. - For simulation input under Server Authority use `InputAction`/`InputContext` with `RunService:BindToSimulation()`, not a `RemoteEvent`. - Use events for most gameplay requests. Keep `RemoteFunction` calls short and bounded. - Use `RemoteEvent` for reliable state changes. It is not ordered relative to property or attribute replication: use one explicit state channel or version the state when ordering matters. Reserve `UnreliableRemoteEvent` for replaceable data such as VFX and snapshots. - Unreliable is not automatically faster: delivery is unordered, packets may be dropped, and payloads should stay at or below the documented 1000-byte limit. - Measure payload size and fire rate under load; packet-size estimators are not an official wire-format spec. - Check numbers for NaN and infinity (`x ~= x`, `math.abs(x) == math.huge`) first: `NaN` defeats `<`/`>`. Strings: `utf8.len(s)` catches malformed UTF-8 that fails a DataStore save. - Serialization decides validation: functions arrive as `nil`, metatables are stripped, mixed-key tables are mangled, `nil` in a table truncates the payload, and tables are copies, not references. Validate field by field; share state via server-owned snapshots or ids. - Server Authority needs `Workspace.AuthorityMode = Server` plus the full bundle: NextGenerationReplication, PlayerScriptsUseInputActionSystem, deferred SignalBehavior, UseFixedSimulation, StreamingEnabled. Misprediction and rollback are normal (debug surface in full.md). - Rate limits protect the server; validation still rejects invalid requests. - Record suspicious behavior with thresholds; never punish one malformed packet. - Edit-mode play: wrap the network layer so `RunContext:IsEdit()` gets a local loopback mock (full.md). **Need details?** `references/full.md` has validation, throttling, and mock patterns.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.