跳至正文
版本:最新版 - a1.169.2 ⚖️

网络与复制

服务器与客户端之间自动同步的内容,以及如何高效发送自定义数据。

nanos world 以服务器主权为主:服务器保存游戏的“真实”状态,客户端接收并显示该状态。大部分同步工作已为你自动完成,本页将说明其工作原理,以便你了解何时需要自行发送数据,以及如何在不浪费带宽的情况下进行传输。

自动同步的内容​

  • 在服务器生成的实体会自动在所有客户端生成,包括稍后加入的玩家。
  • 在服务器调用的方法(如 SetLocation、SetMaterialColorParameter、PlayAnimation)也会自动应用到所有客户端。
  • 角色、载具和物理道具的移动会持续同步(参见下方的网络主控)。
  • 在服务器使用 SetValue(key, value, true) 设置的同步值会发送给所有客户端,参见实体值。

不会自动同步的内容:

  • 在客户端生成的实体仅存在于该客户端。你可以通过这种方式创建纯视觉内容,例如 UI、本地声音和粒子。
  • 在客户端调用的方法仅更改该客户端的实体副本(且服务器随后可能会覆盖该更改)。
  • Lua 变量绝不会被自动同步,每一端(以及每个包)都拥有各自独立的变量。
Server/Index.lua
-- 在服务器生成:每个玩家都能看到,包括稍后加入的玩家
local door = StaticMesh(Vector(0, 0, 100), Rotator(), "nanos-world::SM_Cube")

-- 在服务器调用:每个玩家都能看到更改
door:SetMaterialColorParameter("Tint", Color.RED)
Client/Index.lua
-- 在客户端生成:只有此玩家能看到
local marker = Particle(Vector(0, 0, 200), Rotator(), "nanos-world::P_Fire", false, true)
提示

请检查脚本参考中每个方法的网络主控图标,它们会说明可以在哪一端调用该方法以及是否会自动同步。

发送你自己的数据​

当你需要发送不属于实体状态一部分的信息时(如“对局已开始”、“打开商店菜单”、“玩家点击了按钮”),请使用远程事件。对于属于实体或游戏状态一部分的信息(如玩家所属队伍或分数),建议优先使用同步值,因为它们会自动发送给稍后加入的玩家。

我需要……使用
通知客户端发生了某事Events.BroadcastRemote / Events.CallRemote
通知服务器玩家执行了某操作Events.CallRemote(在客户端)
在实体上保存当前及后续加入的所有玩家都必须知道的值entity:SetValue(key, value, true)
保存所有玩家都必须知道的全局值Server.SetValue(key, value, true)
与另一端的特定实体通信entity:CallRemoteEvent / entity:BroadcastRemoteEvent

可靠与不可靠​

每个远程事件发送时都带有 Reliability:

  • Reliability.Reliable:保证按顺序送达。丢失的数据包将被重发直到送达。适用于所有重要信息:游戏状态变更、购买、聊天。
  • Reliability.Unreliable:可能会丢失或乱序到达,但开销更小且绝不延迟其他消息。适用于非常频繁发送且仅最新数据有效的信息:每 Tick 发送的位置、视觉效果、声音。
注意

发送大量的 Reliable 事件(例如每 Tick 发送)可能会填满网络状况较差玩家的连接带宽,从而延误其他所有内容。如果是每帧发送的数据,几乎总是应该使用 Unreliable。

减少发送​

每个玩家的带宽都是有限的(参阅服务器配置中的 max_send_rate)。保持低占用的一些建议:

  • 除非确实需要,否则不要每 Tick 发送。通过 Timer 每秒发送 10 次通常就足够了,客户端可以插值计算剩余数据。
  • 仅发送给需要的玩家:与其向所有人广播,不如使用 Events.CallRemote 发送给单个玩家,使用 Events.CallRemotePlayers 发送给玩家列表,或使用 Events.BroadcastRemoteInRadius 发送给附近的玩家。
  • 发送 ID,而不是整个表:如果两端都已经知道了数据(例如在 Shared/ 中定义的物品列表),只需发送物品 ID 即可。
  • 直接传递实体:实体可以作为远程事件的参数传递,并在另一端作为同一个实体到达,你无需自行发送它们的位置或 ID。
  • 避免极其频繁变动的同步值,因为每次变动都会发送给所有玩家。

网络主控​

物理(和 AI)不由服务器进行模拟。相反,服务器会选择一个客户端作为每个物理实体的网络主控(通常是距离最近的玩家,或正与其交互的玩家),由该客户端进行模拟并将移动数据发送给服务器,再由服务器转发给其他所有人。

这就是为什么某些方法和事件被标示为在网络主控端可用。更多内容请参阅网络主控。

距离优化​

为节省带宽,服务器向各个玩家发送较远实体的移动更新频率会更低。这仅影响不可靠数据(移动和瞬时动作),可靠数据如生成、销毁、远程事件和数值变更始终会被传递。

你可以通过 SetDistanceOptimizationMultiplier 按实体或按玩家进行微调,请参见距离优化。

维度​

实体和玩家可以隔离在不同的维度中:玩家只会接收与其处于相同维度中的实体。这适用于大厅、副本室内场景或在同一服务器中运行的不同对局。