mcpField
R
roblox-networking

roblox-networking

github.com/el4cteo开发61589 次安装2 周前更新
代码仓库

关于 roblox-networking

roblox-networking 是面向 Roblox 开发者的客户端与服务器通信指南,介绍通信方式的选择与实现。内容包括远程调用验证、序列化、带宽管理及延迟处理。

主要功能

  1. 01选择远程调用与复制通道
  2. 02配置类型化远程对象
  3. 03服务器端请求验证
  4. 04网络序列化规则
  5. 05带宽与 CPU 优化实践
  6. 06时间与延迟处理

适用场景

  1. 01为游戏事件选择通信通道
  2. 02验证客户端请求
  3. 03减少网络流量
  4. 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