mcpField
R
roblox-networking

roblox-networking

github.com/gamedev-skills开发5.4k4.2k 次安装1 天前更新
代码仓库

关于 roblox-networking

roblox-networking 是帮助 Roblox 开发者设计并保护客户端与服务器通信的技能,重点包括服务器权威状态、载荷验证、复制策略和多人滥用测试。

主要功能

  1. 01梳理现有远程协议
  2. 02按消息语义选择传输方式
  3. 03验证载荷与玩家状态
  4. 04在服务器应用游戏状态变更
  5. 05限制请求频率
  6. 06测试多人和滥用场景

适用场景

  1. 01防止客户端指定伤害或价格
  2. 02保护以 Instance 为目标的远程调用
  3. 03减少过量远程流量
  4. 04处理重生后的过期请求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill roblox-networking

安装后在 Claude Code 中输入/roblox-networking确认已加载。

附带文件agents/openai.yamlreferences/validation-and-testing.md

详细介绍

Roblox 网络

构建明确的请求与复制契约,由服务器决定权威游戏状态,客户端提供输入或意图。面向 Roblox 持续更新的平台 API。本技能比 roblox-luau 中的网络入门内容更深入。

适用场景

  • 用于设计、实现、调试或保护跨边界的 Roblox 通信。
  • 用于远程调用信任客户端值、攻击者可指定任意 Instance、消息使服务过载、流式传输的对象缺失,或客户端与服务器状态不一致的情况。

不适用场景: 基础 Luau/服务相关内容请参阅 roblox-luau;持久化状态请参阅 roblox-datastores;物理所有权机制也可结合 roblox-physics 使用。

工作流

  1. 检查现有协议。 找出每个远程对象及其两个端点;记录方向、发送方、载荷、频率、权限、验证方式和使用方。复用规范的远程对象文件夹——不要因为漏做查找而创建重复对象。
  2. 对每条消息分类。 客户端请求、服务器事实或临时外观样本。根据语义而非便利性选择可靠事件、不可靠事件或请求/响应。
  3. 精简载荷。 发送稳定标识符和意图。不要发送服务器可推导出的价格、伤害、所有权结果、任意路径或计算结果。
  4. 分层验证。 在执行耗时操作前,检查类型/结构/有限性、允许列表中的值、Instance 类别和祖先关系、玩家权限/状态、相关情况下的距离/视线、服务器冷却时间以及速率预算。
  5. 在服务器上执行。 由服务器解析目标并修改生命值、物品栏、货币、冷却时间和进度。客户端检查可改善用户体验,但不构成信任依据。
  6. 谨慎复制。 私有或本地事实使用 FireClient;仅广播共享事实。除非客户端需要独立的呈现事件,否则避免再次发送已复制的属性。
  7. 处理时间和生命周期。 请求可能在死亡、重生、流式传输变化或断开连接后到达。处理请求时解析当前角色/状态,并清理每位玩家的限流数据。
  8. 使用 Server & Clients 验证。 测试正常、格式错误、垃圾请求、超出范围、角色过期、快速重生、离开、同时有多名玩家、定向和广播等情况。分别检查服务器和每个客户端的 Output。

选择传输方式

原语用途不要用于
RemoteEvent有序、可靠的单向请求/事实新样本会取代旧样本的连续数据
UnreliableRemoteEvent可容忍丢失和乱序的临时外观/连续状态购买、伤害判定、物品栏、单次状态转换
RemoteFunction确实需要立即回复的、有界客户端到服务器查询服务器到客户端调用;耗时或结果不确定的工作;常规命令

绝不要在服务器中同步调用客户端。客户端可能断开连接、出错或始终不返回。优先使用服务器端 RemoteEvent:FireClient(),并在需要时使用单独的响应事件。

模式:在解析游戏逻辑前进行验证

-- ServerScriptService/CombatRequests.server.luau
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

local attack = ReplicatedStorage.Remotes.Attack
local lastRequest: {[Player]: number} = {}
local RANGE = 12
local COOLDOWN = 0.25

attack.OnServerEvent:Connect(function(player: Player, target: unknown)
    local now = Workspace:GetServerTimeNow()
    if now - (lastRequest[player] or -math.huge) < COOLDOWN then return end
    lastRequest[player] = now

    if typeof(target) ~= "Instance" or not target:IsA("Model") then return end
    if not target:IsDescendantOf(Workspace.Characters) then return end
    local targetHumanoid = target:FindFirstChildOfClass("Humanoid")
    local targetRoot = target:FindFirstChild("HumanoidRootPart")
    local character = player.Character
    local root = character and character:FindFirstChild("HumanoidRootPart")
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not targetHumanoid or not targetRoot or not root or not humanoid then return end
    if humanoid.Health <= 0 or targetHumanoid.Health <= 0 then return end
    if (root.Position - targetRoot.Position).Magnitude > RANGE then return end
    if not serverCombatStateAllowsAttack(player, now) then return end

    targetHumanoid:TakeDamage(serverDamageFor(player))
end)

Players.PlayerRemoving:Connect(function(player)
    lastRequest[player] = nil
end)

这仍然只是一个简洁示例:真实的近战系统可能需要服务器已知的攻击时段、视线/形状检查、队伍规则和延迟处理策略。不要把单次距离检查当作安全保障。

模式:在边界使用令牌桶

type Bucket = {tokens: number, updatedAt: number}
local buckets: {[Player]: Bucket} = {}
local CAPACITY, REFILL_PER_SECOND = 6, 3

local function consume(player: Player, cost: number): boolean
    local now = os.clock()
    local bucket = buckets[player] or {tokens = CAPACITY, updatedAt = now}
    bucket.tokens = math.min(CAPACITY,
        bucket.tokens + (now - bucket.updatedAt) * REFILL_PER_SECOND)
    bucket.updatedAt = now
    if bucket.tokens < cost then buckets[player] = bucket; return false end
    bucket.tokens -= cost
    buckets[player] = bucket
    return true
end

根据对服务器的影响分配成本。在进行数据存储调用、克隆、射线检测或大范围复制前,以低成本拒绝请求。记录汇总后的滥用信号,不要针对每个被拒绝的数据包都发出警告。

复制、流式传输与预测

  • 已复制的实例/属性本身就是状态通道。将远程事件用于意图、私有状态或呈现提示,而不要无条件地并行复制 DataModel。
  • 启用实例流式传输后,客户端上可能不存在有效的服务器 Instance。发送稳定 ID 并容忍实例缺失;不要无限等待可选的流式内容。
  • 高频率的装饰性数据可以使用 UnreliableRemoteEvent;每个样本都应包含完整信息,因为无法保证送达或顺序。超过 1000 bytes 的负载会被丢弃(Studio Output 会报告超出的字节数)。RemoteEvent 和 UnreliableRemoteEvent 还共享一个限制:每种类型的所有远程事件合计,每个客户端每秒大约 500 次调用。这是正常玩家会先于任何攻击者触及的限制。
  • 只对延迟敏感、可撤销的呈现进行预测。包含客户端序列/命令 ID;服务器返回权威状态和确认信息;客户端平滑修正。绝不要让预测增加伤害、货币、物品栏内容或进度。
  • 网络所有权能提高响应速度,但也让客户端可以影响物理模拟。在服务器上验证游戏结果;拥有所有权不等于拥有授权。

常见故障

症状可能原因解决方法
利用漏洞的玩家自行选择伤害/价格接受来自客户端的结果发送意图/ID;在服务器上推导并应用结果
任意对象都能被删除只检查了 typeof(Instance)验证类、祖先关系、所有权、状态及允许列表中的操作
服务器因某个玩家而停滞服务器调用客户端的 RemoteFunction改用异步事件
正常玩家触发限流每帧发送可靠消息降低频率,使用状态复制、批处理或不可靠的装饰性数据
旧数据包反转了新效果将无序的不可靠样本当作命令处理使样本可替换/带版本号;状态转换使用可靠事件
重生后远程事件失效缓存了角色/根部实例处理请求时解析当前角色,并拒绝过时状态
私有数据泄露默认使用了 FireAllClients使用 FireClient 并尽量减少负载
距离检查被绕过客户端拥有的对象被移至目标附近固定关键对象/将其设为服务器所有,并验证完整的服务器上下文

资源

  • 阅读 references/validation-and-testing.md,了解负载规则、Instance/有限性检查、复制设计以及必需的多客户端滥用测试矩阵。

相关技能

  • roblox-luau — 执行位置和 RemoteEvent 的基本机制。
  • roblox-characters — 重生安全的角色解析。
  • roblox-physics — 网络所有权、射线/重叠验证和物理结果。
  • roblox-studio-workflow — Server & Clients 测试和 Output 检查。

主要参考资料

  • https://create.roblox.com/docs/scripting/events/remote
  • https://create.roblox.com/docs/scripting/security/client-server-boundary
  • https://create.roblox.com/docs/physics/network-ownership
  • https://create.roblox.com/docs/studio/testing-modes

评论

支持 Markdown