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