Register for your free account! | Forgot your password?

You last visited: Today at 09:25

  • Please register to post and access all features, it's quick, easy and FREE!

Runtime-generated factory for network messages

Discussion on Runtime-generated factory for network messages within the CO2 PServer Guides & Releases forum part of the CO2 PServer - Discussions / Questions category.

Old   #1

elite*gold: 0
Join Date: Jan 2008
Posts: 1,396
Received Thanks: 1,065
Runtime-generated factory for network messages

In probably all sources released (and it's kind of normal), we'll have something in the code that generates the proper message object from a byte array when we receive data on the socket. Some sources will use polymorphism to process this message after that.

This something is always a large switch block. Sometimes in the base message class (it creates a circular coupling in this case) or in another factory/helper class that must know the world.

Using LINQ expression to emit IL code at runtime and Reflection to discover all subclasses, this code generates the switch block at runtime. As such, the base message class can only know itself and the API it exposes, but still provide a factory method to create the proper message object based on the type.

using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;

namespace COServer.Protocol
    public abstract class Msg
        private static readonly Func<int, Msg> factoryFct;

        static Msg()
            var assembly = Assembly.GetAssembly(typeof(Msg));
            var msgTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Msg)) && !t.IsGenericType).ToArray();

                var typeParam = Expression.Parameter(typeof(int), "type");
                var msgVar = Expression.Variable(typeof(Msg), "msg");

                var switchCases = new SwitchCase[msgTypes.Length];
                var defaultCase = Expression.Assign(msgVar, Expression.Constant(null, typeof(Msg)));

                for (int i = 0; i < msgTypes.Length; ++i)
                    var createMethod = msgTypes[i].BaseType.GetMethod("Create", BindingFlags.Public | BindingFlags.Static);
                    var testValue = msgTypes[i].GetField("Type", BindingFlags.Public | BindingFlags.Static).GetValue(null);

                    switchCases[i] = Expression.SwitchCase(
                        Expression.Assign(msgVar, Expression.Call(null, createMethod)),

                var switchExpr = Expression.Switch(typeParam, defaultCase, switchCases);
                var returnExpr = Expression.Assign(msgVar, msgVar); // not truly a return, but equivalent

                var expr = Expression.Block(new[] { msgVar }, switchExpr, returnExpr);
                factoryFct = Expression.Lambda<Func<int, Msg>>(expr, typeParam).Compile();

        public static Msg Create(int type)
            return factoryFct(type);

        public abstract void Serialize(byte[] buf, int offset, int len);
        public abstract void Deserialize(byte[] buf, int offset, int len);

        public abstract void Recycle();

    public abstract class Msg<T> : Msg
        where T : Msg<T>
        private static ObjectPool<T> messages;

        static Msg()
            messages = new ObjectPool<T>(GetGenerator());

        private static Func<T> GetGenerator()
            var newExp = Expression.New(typeof(T));
            return Expression.Lambda<Func<T>>(newExp).Compile();

        public static T Create()
            Contract.Ensures(Contract.Result<T>() != null);
            return messages.Get();

        public override void Recycle()
The function requires that the subclasses define a static constant (Type). For example:
public sealed unsafe class MsgAccount : Msg<MsgAccount>
    public static readonly int Type = 1051;
    public static readonly int MinSize = sizeof(NetworkMessage);
    public static readonly int MaxSize = MinSize;

    // ...

CptSky is offline  
8 Users
Old 12/18/2018, 12:32   #2
elite*gold: 0
Join Date: Dec 2012
Posts: 1,633
Received Thanks: 841
Even though I will never ever use this, it truly is beautiful code.

Super Aids is offline  
Old 01/08/2019, 03:58   #3
elite*gold: 0
Join Date: Nov 2009
Posts: 745
Received Thanks: 539
You guys are making me feel quite nostalgic.
ImmuneOne is offline  
Old 01/08/2019, 19:09   #4
elite*gold: 28
Join Date: Jun 2010
Posts: 2,206
Received Thanks: 852
Originally Posted by ImmuneOne View Post
You guys are making me feel quite nostalgic.
seeing u post makes me the feel the same
_DreadNought_ is offline  

« [Release] World Conquer Source v2 | Comet, Open Source Server (5017) »

Similar Threads
Combat Arms Self Generated ScreenShots Deleter [EU]
09/23/2010 - Combat Arms - 4 Replies
I know that you can go and delete the screenshots yourself, which is easy however, i figured why not make a tool that does it for you... THIS IS COMBAT ARMS EU VERSION !! What it does : It basically goes into your nexon folder, checking for screenshots generated by Nexon ONLY (not screenshots you took) since as far as i know, nexon only uses the ones they generate. It will check for them in order from 00 to 10000
Generated: not found
06/06/2010 - Metin2 PServer - Discussions / Questions - 5 Replies
Hallo leute ich hab ein kleines problem, ich starte Virtual PC, Da log ich mich mit root ein und geb das passwort ein, nach einer weile kommt immerzu generated:not found kann mir bitte einer helfen

All times are GMT +1. The time now is 09:25.

Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Abuse
Copyright ©2019 elitepvpers All Rights Reserved.