通过网络以每个字段为基础传输对象

本文关键字:字段 为基础 传输 对象 网络 | 更新日期: 2023-09-27 18:16:32

我需要通过网络(多人游戏)传输。net对象(带层次结构)。为了节省带宽,我想只传输更改的字段(和/或属性),因此不会更改的字段将不会传输。

我还需要一些机制来匹配其他客户端的适当对象(全局对象标识符…比如对象ID?)

我需要一些如何做这件事的建议。
你会使用反射吗?(性能至关重要)
我还需要一种机制来传输列表增量(添加的对象,删除的对象)。

MMO网络是如何完成的,他们是否传输整个对象?
(也许我的每场传输的想法是愚蠢的)

编辑:
明确一点:我已经有了跟踪更改的机制(假设每个字段都有属性,setter将字段添加到包含更改的某种列表或字典中-结构现在不是最终的)。

我不知道如何序列化这个列表,然后在其他客户端上反序列化它。以及如何有效地更新和更新合适的对象。

大约有100个对象,所以我尽量避免为每个对象编写特殊函数的情况。用属性修饰字段或属性是可以的(例如指定序列化器、字段id或类似的东西)。

关于对象的更多信息:每个对象平均有5个字段。

谢谢你的回答。

通过网络以每个字段为基础传输对象

另一种方法;不要试图序列化复杂的数据更改:相反,只发送实际命令来应用(以简洁的形式),例如:

move 12432 134, 146
remove 25727

(这将移动一个对象并移除另一个对象)。

你可以在接收端应用命令,如果它们不同步,允许完全重新同步。

我不建议你在这里实际使用文本——那只是为了使示例更清晰。

这样做的一个好处是:它还免费提供了"重放"功能。

跟踪脏字段最便宜的方法是将其作为对象模型的一个关键特征,即为每个数据字段"foo"设置一个"fooDirty"字段,在"set"中设置为true(如果值不同)。这也可以与条件序列化相结合,也许是一些序列化器观察到的"ShouldSerializeFoo()"模式。我不知道有任何库完全匹配你所描述的(除非我们包括DataTable,但是…想想小猫吧!)

也许另一个问题是需要在反序列化期间跟踪所有要合并的对象;这本身不是免费的。

考虑到所有的事情,尽管,我认为你可以沿着上面的行(fooDirty/ShouldSerializeFoo)做一些事情,并使用protobuf-net作为序列化器,因为(重要的是)它支持条件序列化和合并。我还建议使用如下接口:

ISomeName {
    int Key {get;}
    bool IsDirty {get;}
}

IsDrty将允许您快速检查所有对象是否有更改,然后将密钥添加到流中,然后进行(条件)序列化。调用者将读取键,获得所需的对象(或用该键分配一个新对象),然后使用启用合并的反序列化(传入现有/新对象)。

不是一个完整的演练,但是如果是我,那就是我要考虑的方法。注意:子集合中对象的添加/删除/排序是一个棘手的领域,可能需要考虑。

我只想说Marc Gravell的建议确实是正确的方法。他掩盖了一些次要的细节,比如解决冲突(你可能需要阅读Leslie Lamport的作品)。他的整个职业生涯基本上都在描述分布式系统中处理冲突解决的不同方法,但这个想法是合理的。

如果你确实想传输状态快照,而不是状态变化的过程描述,那么我建议你考虑将快照差异构建为前缀树。基本思想是构建对象和字段的层次结构。当您更改一组字段时,它们拥有的任何公共前缀只包含一次。这可能看起来像:

world -> player 1 -> lives: 1
...               -> points: 1337
...               -> location -> X: 100
...                           -> Y: 32
...   -> player 2 -> lives: 3

("…"中的所有内容只传输一次)。

只传输改变了的字段是不符合逻辑的,因为你会浪费时间去检测哪些字段改变了,哪些没有,以及如何在接收端重建,这会给你的游戏增加很多延迟,使它无法在线玩。

我建议的解决方案是你分解你的对象到最小,并发送这些小对象,这是快速的。此外,您可以使用压缩来减少带宽使用。

对于Object ID,您可以使用静态ID,该ID在构造新Object时增加。

您需要手工完成此操作。与手工制作相比,自动跟踪对象层次结构中的属性和实例变化将非常缓慢。

如果你决定尝试一下,我会尝试将你的对象映射到一个数据集,并使用其内置的修改跟踪机制。

我仍然认为你应该手工完成。