1

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?

  • " I guess reflection wont be a good idea since it will kill the performance" - that is an assumption you would have to test first. It is not a killer by definition and you could optimize it a lot by storing the correct type for each byte/type pair in a dictionary. – Emond Aug 04 '14 at 09:40
  • Yeah I guess I have to test it. I had an idea were I would create an instance of each class at start-up and store it in a dictionary. Then have the PopulateFromNetMessage(incMsg) return a new copy of the class every time, but I didn't like the look of it. Would it be a viable option you think? – wheelinlight Aug 04 '14 at 09:48
  • I added to my answer about pooling instances – Emond Aug 04 '14 at 10:25

1 Answers1

2

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);
}
Emond
  • 50,210
  • 11
  • 84
  • 115