2

在我计划制作的游戏服务器和客户端中,从长远来看,我将有很多必须处理的数据包。我想知道如何处理我的不同数据包的最佳实践是什么。

我的数据包有效负载将以 PacketID 开头。

创建一个扩展 Packet 的单独类并处理那里的逻辑是个好主意吗?例如 Packet001Login?从长远来看,这将为我提供很多课程。

做一个巨大的switch 语句会更好吗?我对此表示怀疑。

最好的方法是我没有想到的吗?

任何意见是极大的赞赏。

4

1 回答 1

2

如果您在服务器端确实有足够的计算时间,您应该研究一种技术来为不同的包类型创建原型。

为了说明这一点和想法,我给你一些类似 UML 的类描述

UML:

class PacketPrototype
+ PacketPrototype(PacketType)
+ addData(DataType, Bitlength, ...)
+ deserilize(Bitstream stream) : ReceivedPacket
+ serilizeThis() : ToSendPacket
+ getPacketType() : int

您还需要一个具有所有 PacketPrototypes 的类,并决定每个 PacketPrototype 对象的类型,应该使用哪个 Prototype 对数据进行反序列化。

你需要一个知道每个 PacketPrototype 的类,我称之为 PacketPrototypeHolder

class PacketPrototypeHolder
+ register(PacketPrototype)
+ getPrototypeForPacketType(int Type) : PacketPrototype

建立时间的协议如下

PacketEnemy00 = new PacketPrototype(0)
PacketEnemy00.addData(Types.Int, 5) // health
PacketEnemy00.addData(Types.String, 16) // name
...

这意味着类型 0 的数据包由一个 5 位长的 int 和一个最大长度为 16 个字符的字符串组成。

我们必须在设置后将 PacketPrototype 添加到我们的 PacketPrototypeHolder

PacketHolder.register(PacketEnemy00)

如果服务器或客户端收到我们读取数据包类型的内容,那么我们会执行(您可以从Bitstream读取数据)

Type = Bitstream.readInt(5)
Prototype = PacketHolder.getPrototypeForPacketType(Type)

ReceivedPacket OfReceivedPacket = Prototype.deserilize(Bitstream)

// we need here a switch/if statement to determine how to handle the reading of the data
switch(Type)
{
  case 0: // PacketEnemy00

Prototype.deserilize调用从数据流中读取数据并将其放入 ReceivedPacket 对象中,从那里您可以使用索引操作或命名访问来访问您的数据。

作为一个例子,我用索引来做

   int UnitHealth = OfReceivedPacket.getInt(/* index, we want the 1st int */0);
   string UnitName = OfReceivedPacket.getString(/* index, we want the 1st string */0);

   and so on...
   break;
   ...
}

因此,我有效地将 switch 语句从网络层内部移动到了应用程序/使用层。

要删除开关,您需要一种数据驱动的方法。但它在引擎中的实现比硬编码的方法更复杂。

于 2013-07-17T20:41:12.283 回答