Register for your free account! | Forgot your password?

Go Back   elitepvpers > MMORPGs > Conquer Online 2 > CO2 Private Server > CO2 PServer Guides & Releases
You last visited: Today at 03:53

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

Advertisement



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 Private Server category.

Reply
 
Old   #1


 
CptSky's Avatar
 
elite*gold: 0
Join Date: Jan 2008
Posts: 1,434
Received Thanks: 1,146
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.

Code:
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)),
                        Expression.Constant(testValue));
                }

                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()
        {
            messages.Put((T)this);
        }
    }
The function requires that the subclasses define a static constant (Type). For example:
Code:
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  
Thanks
9 Users
Old 12/18/2018, 12:32   #2
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 946
Even though I will never ever use this, it truly is beautiful code.
Super Aids is offline  
Old 01/08/2019, 03:58   #3
 
ImmuneOne's Avatar
 
elite*gold: 0
Join Date: Nov 2009
Posts: 754
Received Thanks: 544
You guys are making me feel quite nostalgic.
ImmuneOne is offline  
Old 01/08/2019, 19:09   #4
 
_DreadNought_'s Avatar
 
elite*gold: 28
Join Date: Jun 2010
Posts: 2,223
Received Thanks: 867
Quote:
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  
Thanks
1 User
Reply


Similar Threads 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 Private Server - 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 03:53.


Powered by vBulletin®
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2024 elitepvpers All Rights Reserved.