在我计划制作的游戏服务器和客户端中,从长远来看,我将有很多必须处理的数据包。我想知道如何处理我的不同数据包的最佳实践是什么。
我的数据包有效负载将以 PacketID 开头。
创建一个扩展 Packet 的单独类并处理那里的逻辑是个好主意吗?例如 Packet001Login?从长远来看,这将为我提供很多课程。
做一个巨大的switch 语句会更好吗?我对此表示怀疑。
最好的方法是我没有想到的吗?
任何意见是极大的赞赏。
在我计划制作的游戏服务器和客户端中,从长远来看,我将有很多必须处理的数据包。我想知道如何处理我的不同数据包的最佳实践是什么。
我的数据包有效负载将以 PacketID 开头。
创建一个扩展 Packet 的单独类并处理那里的逻辑是个好主意吗?例如 Packet001Login?从长远来看,这将为我提供很多课程。
做一个巨大的switch 语句会更好吗?我对此表示怀疑。
最好的方法是我没有想到的吗?
任何意见是极大的赞赏。
如果您在服务器端确实有足够的计算时间,您应该研究一种技术来为不同的包类型创建原型。
为了说明这一点和想法,我给你一些类似 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 语句从网络层内部移动到了应用程序/使用层。
要删除开关,您需要一种数据驱动的方法。但它在引擎中的实现比硬编码的方法更复杂。