关于 roblox-networking
roblox-networking 是面向 Roblox 开发者的客户端与服务器通信指南,介绍通信方式的选择与实现。内容包括远程调用验证、序列化、带宽管理及延迟处理。
主要功能
- 01选择远程调用与复制通道
- 02配置类型化远程对象
- 03服务器端请求验证
- 04网络序列化规则
- 05带宽与 CPU 优化实践
- 06时间与延迟处理
适用场景
- 01为游戏事件选择通信通道
- 02验证客户端请求
- 03减少网络流量
- 04处理计时器和延迟敏感功能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ el4cteo/ roblox-skills --skill roblox-networking安装后在 Claude Code 中输入/roblox-networking确认已加载。
附带文件references/patterns.md
详细介绍
Roblox 网络通信
选择正确的通道
| 通道 | 投递方式 | 适用场景 |
|---|---|---|
RemoteEvent | 可靠、有序 | 请求(客户端→服务端)、游戏事件、UI 更新。默认选择。 |
UnreliableRemoteEvent | 可能丢失或乱序;负载 ≤ 1000 字节(更大的负载会被丢弃) | 连续的、会被下一个数据取代的数据:瞄准方向、外观 VFX、非物理物体的位置。 |
RemoteFunction | 请求/响应,会挂起调用方 | 需要答复的客户端→服务端查询(例如 “我能买 X 吗?”)。 |
| 属性 / property | 服务端→客户端自动复制 | 客户端读取的持久状态(生命值、队伍、回合状态)。 |
| 物理复制 | 对拥有的组装体自动进行 | 移动的部件;参见 roblox-physics。 |
永远不要使用 RemoteFunction:InvokeClient:如果客户端报错、断开连接或永不返回,服务端会
抛出异常或永久挂起。如果需要回复,请向客户端发送一个 RemoteEvent,再用另一个 RemoteEvent 发回。
一次性定义远程事件(带类型)
在服务端创建远程事件;客户端等待它们。集中管理名称,避免出现拼写错误。
--!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,
}
服务端处理程序模板
每个 OnServerEvent/OnServerInvoke 处理程序:速率限制 → 对每个参数做类型检查 → 检查
游戏规则 → 执行操作。第一个参数始终是真实的 Player(Roblox 会填入;无法伪造)。
--!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 检查、实例归属、距离和冷却检查):roblox-security。
哪些数据能通过网络传输
- 表会被复制:身份和元表会丢失;函数会变成
nil。 - 不要在同一个表中混用数组键和字典键;不要在数组中留下
nil空洞。 - 非字符串的字典键(Instances、稀疏表中的数字)会被转换为字符串 —— 应改为发送键值对数组。
- 如果接收方看不到 Instances,它们会以
nil到达(例如ServerStorage中的物品,或客户端 尚未流式加载的部件)。 - 支持的类型:
nil、布尔值、数字、字符串、表、buffer、Roblox 数据类型(Vector3、CFrame、Color3、EnumItem、……),以及已复制的Instance引用。 - 服务端→客户端的顺序与属性复制之间不保证一致:远程事件可能在相关属性变化之前或 之后到达。
带宽和 CPU
- 发送变化,而非完整状态(发送“新增物品”,而非整个物品栏)。在变化时发送,而不是每帧发送。
- 在客户端对由输入驱动的发送进行节流(例如瞄准最多 20 Hz),同时在服务端仍然做速率限制。
- 将 ID/枚举作为小数字发送;对浮点数进行量化;将高频的热点数据打包进
buffer。 - 将许多小消息批处理为每帧一条(在表中排队,在
Heartbeat时刷新)。 - 服务端的
TweenService会每帧复制,看起来会抖动:应改为在客户端做补间。 - VFX:由服务端决定结果并触发一个最小事件;每个客户端在本地生成效果。
- 使用 MicroProfiler(
ProcessPackets、Allocate Bandwidth and Run Senders)和 Developer Console 的 Network 标签页进行分析。
--!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))))
对于大量带热点路径的远程事件,schema 驱动的生成器(Zap、Blink)会自动生成带类型、 经过验证、以 buffer 打包的远程事件。当性能分析显示存在网络开销时再采用它们。
时间和延迟
workspace:GetServerTimeNow()在服务端和客户端上都是同步时钟:发送一个开始时间戳, 让客户端在本地计算进度(回合计时器、冷却条、投射物)。player:GetNetworkPing()返回往返网络延迟,单位为秒(客户端只能查询LocalPlayer);用它来确定延迟补偿窗口的大小。- 不要每秒发送倒计时滴答;只发送一次结束时间。
更多模式(带超时的请求/响应、状态快照 + 增量、每帧批处理、客户端 预测):references/patterns.md。
相关技能
roblox-security(验证、反作弊)、roblox-architecture(远程事件所在位置)、
roblox-physics(网络归属、服务端权威)、roblox-performance。
评论
支持 Markdown