wheelinlight
wheelinlight

Reputation: 13

Generic packet system for Lidgren

I'm using Lidgren.Networking to send data between server and clients in a client/server architecture. I have created a Packet class to use when sending packets over the network.

Packet.cs

public abstract class Packet : IPackable
{
    public abstract void PackIntoNetMessage(NetOutgoingMessage msg);
    public abstract void PopulateFromNetMessage(NetIncomingMessage msg);
}

IPackable.cs

interface IPackable
{
    void PackIntoNetMessage(NetOutgoingMessage msg);
    void PopulateFromNetMessage(NetIncomingMessage msg);
}

Then I have application specific packet classes like this

public class PositionPacket : Packet
{
    public int PosX { get; set; }
    public int PosY { get; set; }

    public override void PackIntoNetMessage(NetOutgoingMessage m)
    {
        m.Write((byte)Networking.PacketTypes.PositionPacket);
        m.Write(PosX);
        m.Write(PosY);
    }

    public override void PopulateFromNetMessage(NetIncomingMessage m)
    {
        PosX = m.ReadInt32();
        PosY = m.ReadInt32();
    }
}

Sending the packets is easy, I just create a packet and PackIntoNetMessage(msg) and then send it. But it's when I try to recieve packets I get some annoying structural problems.

I start by reading the first byte

var packetType = (Networking.PacketTypes)incomingMsg.ReadByte();

but then I have to make a switch with a case for each packet type in order the get the correct constructor.

switch (packetType)
{
    case Networking.PacketTypes.PositionPacket:
        incPacket = new Packets.PositionPacket();
        break;
    default:
        incPacket = null;
        break;
}
//Populate packet from net message
incPacket.PopulateFromNetMessage(incomingMsg);

This is the part that I don't like. I want to be able to make a generic packet handling system that handles any class deriving from Packet. I guess reflection wont be a good idea since it will kill the performance. How shall I structure this to make it more generic?

Upvotes: 1

Views: 1659

Answers (1)

Emond
Emond

Reputation: 50682

The instance of the specific class needs to be created somehow so you could use reflection on start-up to fill a dictionary with all available classes that derive from Packet stored by their ids.

Creating an instance would then boil down to:

var packetTypeId = (Networking.PacketTypes)incomingMsg.ReadByte();
var incPackage = (Package)Activator.CreateInstance(packetTypes[packetTypeId]);

If you are worried about creating many, many instances and expect garbage collection cycles with big impact on your application's speed you could create a pool of objects that recycles instances. As long as you drop objects, that are no longer used, back into the pool you could prevent GC cycles.

var packetTypeId = (Networking.PacketTypes)incomingMsg.ReadByte();
Type packetType = packetTypes[packetTypeId];
Package incPackage;
if(!pool.TryGetRecycledInstance(packetType, out incPackage))
{
    incPackage = (Package)Activator.CreateInstance(packetType);
}

Upvotes: 2

Related Questions