Search code examples
c#.netlidgren

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?


Solution

  • 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);
    }