public void RegisterPacketTypesFromCallingAssembly(Func <PacketStructInfo, bool> predicate)
        {
            var assembly    = Assembly.GetCallingAssembly();
            var packetTypes = PacketStructInfo.GetPacketTypes(assembly);

            RegisterPacketTypes(packetTypes.Where(predicate));
        }
        public override Delegate CreatePacketAction(PacketStructInfo structInfo)
        {
            // TODO: add support IDisposable

            var expressions = new List <Expression>();
            var writerParam = Expression.Parameter(typeof(NetBinaryWriter), "Writer");
            var packetParam = Expression.Parameter(structInfo.Type.MakeByRefType(), "Packet");

            if (typeof(IDataWritable).IsAssignableFrom(structInfo.Type))
            {
                string methodName  = nameof(IDataWritable.Write);
                var    writeMethod = structInfo.Type.GetMethod(methodName, new[] { writerParam.Type });
                if (writeMethod == null)
                {
                    throw new Exception(
                              $"Failed to get public {nameof(IDataWritable.Write)} method required for reflection.");
                }

                var writeCall = Expression.Call(packetParam, writeMethod, writerParam);
                expressions.Add(writeCall);
            }
            else
            {
                ReflectiveWrite(expressions, writerParam, packetParam);
            }

            var actionType   = typeof(NetPacketWriterAction <>).MakeGenericType(structInfo.Type);
            var lambdaBody   = Expression.Block(expressions);
            var lambdaArgs   = new[] { writerParam, packetParam };
            var resultLambda = Expression.Lambda(actionType, lambdaBody, lambdaArgs);

            return(resultLambda.Compile());
        }
 public abstract Delegate CreatePacketAction(PacketStructInfo structInfo);
 public void RegisterPacketType(PacketStructInfo info)
 {
     RegisteredPacketTypes.Add(info.Type, info);
 }
        /// <summary>
        ///
        /// </summary>
        /// <remarks>
        /// <para>
        /// A manual read pattern is inferred by using
        /// <c>(<see cref="NetBinaryReader"/>, <see langword="out"/> <see cref="OperationStatus"/>)</c>
        /// parameters.
        /// </para>
        /// <para>
        /// The packet constructor is not called if data is malformed.
        /// Empty packet constructors are called without reading packet data
        /// (always returning <see cref="OperationStatus.Done"/>).
        /// </para>
        /// </remarks>
        public override Delegate CreatePacketAction(PacketStructInfo structInfo)
        {
            var constructors        = structInfo.Type.GetConstructors();
            var constructorInfoList = constructors
                                      .Where(c => c.GetCustomAttribute <PacketConstructorAttribute>() != null)
                                      .Select(c => new PacketConstructorInfo(c, c.GetCustomAttribute <PacketConstructorAttribute>() !))
                                      .ToList();

            if (constructorInfoList.Count > 1)
            {
                // TODO: Change this after PacketSwitch attribute for params is implemented
                throw new Exception("Only one packet constructor may be defined.");
            }

            var variables       = new List <ParameterExpression>();
            var expressions     = new List <Expression>();
            var constructorArgs = new List <Expression>();

            // TODO: instead of only giving readMethod a NetBinaryReader,
            // give it a state object with user-defined values/objects
            var readerParam    = Expression.Parameter(typeof(NetBinaryReader), "Reader");
            var outPacketParam = Expression.Parameter(structInfo.Type.MakeByRefType(), "Packet");

            var statusVar = Expression.Variable(typeof(OperationStatus), "Status");

            variables.Add(statusVar);

            // The return target allows code to jump out in the middle of packet reading.
            LabelTarget?returnTarget = null;

            ConstructorInfo?constructor = constructorInfoList.FirstOrDefault()?.Constructor;

            NewExpression newPacket;

            if (constructor == null)
            {
                // Struct with empty constructor doesn't require much logic
                newPacket = Expression.New(structInfo.Type);

                // Empty constructors really shouldn't ever fail...
                expressions.Add(Expression.Assign(statusVar, Expression.Constant(OperationStatus.Done)));
            }
            else
            {
                var constructorParams = constructor.GetParameters();

                // Look if the constructors want to use the read-pattern.
                if (constructorParams.Length == 2 &&
                    constructorParams[0].ParameterType == typeof(NetBinaryReader) &&
                    constructorParams[1].ParameterType == typeof(OperationStatus).MakeByRefType() &&
                    constructorParams[1].ParameterType.GetElementType() == typeof(OperationStatus))
                {
                    if (!constructorParams[1].Attributes.HasFlag(ParameterAttributes.Out))
                    {
                        throw new Exception(
                                  $"The constructor parameter types match the read-pattern," +
                                  $"but the {nameof(OperationStatus)} parameter is not an out parameter.");
                    }

                    constructorArgs.Add(readerParam);
                    constructorArgs.Add(statusVar);
                }
                else
                {
                    // Otherwise just create a read-sequence that calls
                    // the constructor with it's params.
                    returnTarget = Expression.Label("Return");

                    CreatePacketReadSequence(
                        variables, constructorArgs, expressions,
                        readerParam, statusVar,
                        returnTarget, constructorParams);
                }

                // The constructor is not called if data is malformed.
                newPacket = Expression.New(constructor, constructorArgs);
            }
            expressions.Add(Expression.Assign(outPacketParam, newPacket));

            if (returnTarget != null)
            {
                expressions.Add(Expression.Label(returnTarget));
            }

            expressions.Add(statusVar); // Return the status by putting it as the last expression.

            var delegateType = typeof(NetPacketReaderAction <>).MakeGenericType(structInfo.Type);
            var lambdaBody   = Expression.Block(variables, expressions);
            var lambda       = Expression.Lambda(delegateType, lambdaBody, new[] { readerParam, outPacketParam });

            return(lambda.Compile());
        }