roblox-networking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRoblox networking
Roblox 网络
Pick the right channel
选择合适的通道
| Channel | Delivery | Use for |
|---|---|---|
| Reliable, ordered | Requests (client→server), game events, UI updates. The default. |
| May drop or reorder; payload ≤ 1000 bytes (larger payloads are dropped) | Continuous, superseded-by-next data: aim direction, cosmetic VFX, positions of non-physics objects. |
| Request/response, yields caller | Client→server queries that need an answer (e.g. "can I buy X?"). |
| Attributes / properties | Automatic replication server→client | Persistent state clients read (health, team, round state). |
| Physics replication | Automatic for owned assemblies | Moving parts; see |
Never use : if the client errors, disconnects, or never returns, the server
throws or yields forever. Use a to the client and another back if you need a reply.
RemoteFunction:InvokeClientRemoteEvent| 通道 | 交付方式 | 用途 |
|---|---|---|
| 可靠、有序 | 请求(客户端→服务器)、游戏事件、UI 更新。默认选项。 |
| 可能会丢弃或乱序;载荷 ≤ 1000 字节(更大的载荷会被丢弃) | 连续发送且后续数据会取代前一份的数据:瞄准方向、纯视觉 VFX、非物理对象的位置。 |
| 请求/响应,调用方会 yield | 客户端→服务器的、需要返回结果的查询(例如“我能否购买 X?”)。 |
| Attributes / properties | 服务器→客户端自动复制 | 供客户端读取的持久状态(生命值、队伍、回合状态)。 |
| Physics replication | 对已取得所有权的 assemblies 自动复制 | 移动部件;参见 |
绝不要使用 :如果客户端报错、断开连接或永不返回,服务器会抛出异常或永久 yield。如需回复,请使用一个发往客户端的 ,并在需要回复时再使用另一个发回服务器。
RemoteFunction:InvokeClientRemoteEventDefining remotes once (typed)
集中定义 remotes(类型化)
Create remotes on the server; clients wait for them. Centralize names so typos can't happen.
luau
--!strict
-- ReplicatedStorage/Shared/Net.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local FOLDER_NAME = "Remotes"
local IS_SERVER = RunService:IsServer()
local folder: Instance
if IS_SERVER then
folder = ReplicatedStorage:FindFirstChild(FOLDER_NAME) or Instance.new("Folder")
folder.Name = FOLDER_NAME
folder.Parent = ReplicatedStorage
else
folder = ReplicatedStorage:WaitForChild(FOLDER_NAME)
end
local function get<T>(className: string, name: string): T
if IS_SERVER then
local existing = folder:FindFirstChild(name)
if existing then
return existing :: any
end
local remote = Instance.new(className)
remote.Name = name
remote.Parent = folder
return remote :: any
end
return folder:WaitForChild(name) :: any
end
return {
RequestPurchase = get("RemoteEvent", "RequestPurchase") :: RemoteEvent,
InventoryChanged = get("RemoteEvent", "InventoryChanged") :: RemoteEvent,
AimDirection = get("UnreliableRemoteEvent", "AimDirection") :: UnreliableRemoteEvent,
GetShopStock = get("RemoteFunction", "GetShopStock") :: RemoteFunction,
}在服务器上创建 remotes;客户端等待它们创建完成。集中管理名称,从而避免拼写错误。
luau
--!strict
-- ReplicatedStorage/Shared/Net.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local FOLDER_NAME = "Remotes"
local IS_SERVER = RunService:IsServer()
local folder: Instance
if IS_SERVER then
folder = ReplicatedStorage:FindFirstChild(FOLDER_NAME) or Instance.new("Folder")
folder.Name = FOLDER_NAME
folder.Parent = ReplicatedStorage
else
folder = ReplicatedStorage:WaitForChild(FOLDER_NAME)
end
local function get<T>(className: string, name: string): T
if IS_SERVER then
local existing = folder:FindFirstChild(name)
if existing then
return existing :: any
end
local remote = Instance.new(className)
remote.Name = name
remote.Parent = folder
return remote :: any
end
return folder:WaitForChild(name) :: any
end
return {
RequestPurchase = get("RemoteEvent", "RequestPurchase") :: RemoteEvent,
InventoryChanged = get("RemoteEvent", "InventoryChanged") :: RemoteEvent,
AimDirection = get("UnreliableRemoteEvent", "AimDirection") :: UnreliableRemoteEvent,
GetShopStock = get("RemoteFunction", "GetShopStock") :: RemoteFunction,
}Server handler template
服务端处理程序模板
Every / handler: rate limit → type-check every argument → check
game rules → act. The first parameter is always the real (Roblox fills it in; it can't be forged).
OnServerEventOnServerInvokePlayerluau
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Net = require(ReplicatedStorage.Shared.Net)
local ITEM_PRICES: { [string]: number } = { Sword = 100, Shield = 150 }
local MIN_INTERVAL = 0.25
local lastRequest: { [Player]: number } = {}
Net.RequestPurchase.OnServerEvent:Connect(function(player: Player, itemId: unknown)
local now = os.clock()
if now - (lastRequest[player] or 0) < MIN_INTERVAL then
return -- rate limited
end
lastRequest[player] = now
if typeof(itemId) ~= "string" or #itemId > 64 then
return -- malformed: never trust argument types
end
local price = ITEM_PRICES[itemId]
if not price then
return -- unknown item
end
-- check the player's server-side balance, deduct, grant, then notify:
Net.InventoryChanged:FireClient(player, { added = itemId })
end)
game:GetService("Players").PlayerRemoving:Connect(function(player)
lastRequest[player] = nil -- avoid leaking Player keys
end)Full validation toolkit (NaN/inf checks, instance ownership, distance and cooldown checks): .
roblox-security每个 / 处理程序都应遵循:速率限制 → 对每个参数进行类型检查 → 检查游戏规则 → 执行操作。第一个参数始终是真实的 (由 Roblox 填充,客户端无法伪造)。
OnServerEventOnServerInvokePlayerluau
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Net = require(ReplicatedStorage.Shared.Net)
local ITEM_PRICES: { [string]: number } = { Sword = 100, Shield = 150 }
local MIN_INTERVAL = 0.25
local lastRequest: { [Player]: number } = {}
Net.RequestPurchase.OnServerEvent:Connect(function(player: Player, itemId: unknown)
local now = os.clock()
if now - (lastRequest[player] or 0) < MIN_INTERVAL then
return -- rate limited
end
lastRequest[player] = now
if typeof(itemId) ~= "string" or #itemId > 64 then
return -- malformed: never trust argument types
end
local price = ITEM_PRICES[itemId]
if not price then
return -- unknown item
end
-- check the player's server-side balance, deduct, grant, then notify:
Net.InventoryChanged:FireClient(player, { added = itemId })
end)
game:GetService("Players").PlayerRemoving:Connect(function(player)
lastRequest[player] = nil -- avoid leaking Player keys
end)完整验证工具包(NaN/inf 检查、Instance 所有权、距离和冷却检查):。
roblox-securityWhat survives the wire
哪些数据可以通过网络传输
- Tables are copied: identity and metatables are lost; functions become .
nil - Don't mix array and dictionary keys in one table; don't put holes in arrays.
nil - Non-string dictionary keys (Instances, numbers in sparse tables) are converted to strings — send arrays of pairs instead.
- Instances arrive as if the receiver can't see them (e.g.
nilitems, or parts not yet streamed in on the client).ServerStorage - Supported: , booleans, numbers, strings, tables,
nil, Roblox datatypes (buffer,Vector3,CFrame,Color3, ...), and replicatedEnumItemreferences.Instance - Server→client ordering vs. property replication is not guaranteed: a remote may arrive before or after a property change it relates to.
- 表会被复制:对象标识和元表会丢失;函数会变成 。
nil - 不要在同一个表中混用数组键和字典键;不要在数组中留下 空洞。
nil - 作为字典键的非字符串值(、稀疏表中的数字)会被转换为字符串——应改为发送由键值对组成的数组。
Instance - 如果接收方看不到某个 ,它到达时会是
Instance(例如nil中的物品,或客户端尚未流入的部件)。ServerStorage - 支持:、布尔值、数字、字符串、表、
nil、Roblox 数据类型(buffer、Vector3、CFrame、Color3等)以及可复制的EnumItem引用。Instance - 服务器→客户端的远程消息到达顺序与属性复制顺序不保证一致:远程消息可能先于关联的属性变更到达,也可能后于它到达。
Bandwidth and CPU
带宽与 CPU
- Send changes, not full state (item added, not the whole inventory). Send on change, not per frame.
- Throttle input-driven sends on the client (e.g. aim at most 20 Hz) and still rate-limit on the server.
- Send IDs/enums as small numbers; quantize floats; pack hot, high-frequency data into a .
buffer - Batch many small messages into one per frame (queue in a table, flush on ).
Heartbeat - Server-side replicates every frame and looks jittery: tween on clients instead.
TweenService - VFX: the server decides the outcome and fires a minimal event; each client spawns effects locally.
- Profile with the MicroProfiler (,
ProcessPackets) and the Developer Console Network tab.Allocate Bandwidth and Run Senders
luau
--!strict
-- Pack a hit notification into 11 bytes instead of a table with string keys.
local function encodeHit(targetId: number, damage: number, position: Vector3): buffer
local b = buffer.create(11)
buffer.writeu32(b, 0, targetId)
buffer.writeu16(b, 4, math.clamp(math.round(damage), 0, 65535))
buffer.writei16(b, 6, math.clamp(math.round(position.X), -32768, 32767))
buffer.writei16(b, 8, math.clamp(math.round(position.Z), -32768, 32767))
buffer.writeu8(b, 10, math.clamp(math.round(position.Y / 4), 0, 255))
return b
end
local function decodeHit(b: buffer): (number, number, Vector3)
return buffer.readu32(b, 0),
buffer.readu16(b, 4),
Vector3.new(buffer.readi16(b, 6), buffer.readu8(b, 10) * 4, buffer.readi16(b, 8))
end
print(decodeHit(encodeHit(42, 25, Vector3.new(10, 20, 30))))For many remotes with hot paths, schema-driven generators (Zap, Blink) produce typed,
validated, buffer-packed remotes automatically. Adopt them when profiling shows network cost.
- 发送变更,而不是完整状态(发送“新增了物品”,而不是整个背包)。仅在状态变化时发送,不要每帧发送。
- 在客户端限制输入驱动发送的频率(例如瞄准数据最多以 20 Hz 发送),并且服务端仍要执行速率限制。
- 将 ID/enum 编码为较小的数字;量化浮点数;将高频热数据打包到 中。
buffer - 每帧将许多小消息批处理为一条消息(用表进行排队,在 时刷新)。
Heartbeat - 服务端的 会逐帧复制,视觉上容易抖动:应改为在客户端执行补间动画。
TweenService - VFX:服务器决定结果并触发最小化事件;每个客户端在本地生成特效。
- 使用 MicroProfiler(、
ProcessPackets)和 Developer Console 的 Network 选项卡进行性能分析。Allocate Bandwidth and Run Senders
luau
--!strict
-- Pack a hit notification into 11 bytes instead of a table with string keys.
local function encodeHit(targetId: number, damage: number, position: Vector3): buffer
local b = buffer.create(11)
buffer.writeu32(b, 0, targetId)
buffer.writeu16(b, 4, math.clamp(math.round(damage), 0, 65535))
buffer.writei16(b, 6, math.clamp(math.round(position.X), -32768, 32767))
buffer.writei16(b, 8, math.clamp(math.round(position.Z), -32768, 32767))
buffer.writeu8(b, 10, math.clamp(math.round(position.Y / 4), 0, 255))
return b
end
local function decodeHit(b: buffer): (number, number, Vector3)
return buffer.readu32(b, 0),
buffer.readu16(b, 4),
Vector3.new(buffer.readi16(b, 6), buffer.readu8(b, 10) * 4, buffer.readi16(b, 8))
end
print(decodeHit(encodeHit(42, 25, Vector3.new(10, 20, 30))))对于 remotes 数量较多且存在高频热路径的场景,基于 schema 的生成器(Zap、Blink)可以自动生成类型化、经过验证且采用 打包的 remotes。当性能分析显示网络成本较高时,应采用这些生成器。
bufferTime and latency
时间与延迟
- is a synchronized clock on server and clients: send a start timestamp and let clients compute progress locally (round timers, cooldown bars, projectiles).
workspace:GetServerTimeNow() - returns round-trip network latency in seconds (clients may only query
player:GetNetworkPing()); use it to size lag-compensation windows.LocalPlayer - Don't send countdown ticks every second; send the end time once.
More patterns (request/response with timeouts, state snapshots + deltas, per-frame batching, client
prediction): references/patterns.md.
- 是服务器与客户端共享的同步时钟:发送起始时间戳,让客户端在本地计算进度(回合计时器、冷却条、投射物)。
workspace:GetServerTimeNow() - 返回以秒为单位的往返网络延迟(客户端只能查询
player:GetNetworkPing());使用它来确定滞后补偿窗口的大小。LocalPlayer - 不要每秒发送倒计时 tick;只需发送一次结束时间。
更多模式(带超时的请求/响应、状态快照 + 增量、逐帧批处理、客户端预测):references/patterns.md。
Related skills
相关 skills
roblox-securityroblox-architectureroblox-physicsroblox-performanceroblox-securityroblox-architectureroblox-physicsroblox-performance