通过网络在每个字段的基础上传输对象

时间:2011-12-04 16:50:07

标签: c# .net networking serialization multiplayer

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

我还需要一些机制来匹配另一个客户端上的正确对象(全局对象标识符......类似于对象ID?)

我需要一些建议如何去做 你会用反射吗? (表现至关重要)
我还需要机制来传输IList增量(添加的对象,删除的对象)。

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

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

我不知道如何序列化此列表,然后在其他客户端上反序列化它。主要是如何有效地完成它以及如何更新适当的对象。

大约有一百个对象,所以当我为每个对象编写特殊函数时,我正在尝试避免这种情况。使用属性装饰字段或属性是可以的(例如,指定序列化程序,字段ID或类似的东西)。

有关对象的更多信息:每个对象平均有5个字段。某些对象继承自其他对象。

感谢所有回答。

5 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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

答案 1 :(得分:2)

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

move 12432 134, 146
remove 25727

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

然后,您将在接收器上应用命令,如果它们不同步,则允许完全重新同步。

我不建议您实际使用文本 - 这只是为了让示例更清晰。

这是一件好事:它还免费提供“重播”功能。

答案 2 :(得分:2)

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

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

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

(“...”中的所有内容仅传输一次)。

答案 3 :(得分:1)

仅传输已更改的字段是不合逻辑的,因为您将浪费时间检测哪些字段已更改,哪些字段未更改以及如何在接收方重建,这将为您的游戏增加大量延迟并使其成为可能无法在线播放。

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

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

希望这个答案有所帮助。

答案 4 :(得分:1)

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

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

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