protected PacketSerializer(Region region, GameMessageTable gameMessages, SystemMessageTable systemMessages) { Region = region.CheckValidity(nameof(region)); GameMessages = gameMessages ?? throw new ArgumentNullException(nameof(gameMessages)); SystemMessages = systemMessages ?? throw new ArgumentNullException(nameof(systemMessages)); var byType = new Dictionary <Type, PacketInfo>(); var byName = new Dictionary <string, PacketInfo>(); var byCode = new Dictionary <ushort, PacketInfo>(); void RegisterType(Type type, PacketAttribute attribute, ushort?code) { var info = new PacketInfo(type, attribute, (from prop in type.GetProperties() let opts = prop.GetCustomAttribute <PacketFieldOptionsAttribute>() where opts == null || (!opts.Skip && (opts.Regions.Length == 0 || opts.Regions.Contains(region))) orderby prop.MetadataToken select CreateFieldInfo(prop, opts)).ToArray()); byType.Add(type, info); if (code is ushort c) { byName.Add(attribute.Name, info); byCode.Add(c, info); } foreach (var field in info.Fields.Where(x => x.IsArray)) { RegisterType(field.Property.PropertyType.GetGenericArguments()[0], null, null); } } foreach (var type in Assembly.GetExecutingAssembly().DefinedTypes) { var attr = type.GetCustomAttribute <PacketAttribute>(); if (attr == null) { continue; } if (!gameMessages.NameToCode.TryGetValue(attr.Name, out var code)) { _log.Warning("Game message {0} not mapped to a code; ignoring definition", attr.Name); continue; } RegisterType(type, attr, code); } _byType = byType; _byName = byName; _byCode = byCode; }
protected override SerializablePacket OnCreate(PacketInfo info) { return(_creators[info]()); }
Action <GameBinaryReader, object> CompileDeserializer(PacketInfo info) { if (_deserializers.TryGetValue(info, out var d)) { return(d); } var reader = typeof(GameBinaryReader).Parameter("reader"); var target = typeof(object).Parameter("target"); var packet = info.Type.Variable("packet"); BlockExpression CompileByteArray(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable("offset"); var count = typeof(ushort).Variable("count"); var property = prop.PropertyType.Variable("property"); var position = typeof(int).Variable("position"); var read = Expression.Block(new[] { position }, position.Assign(reader.Property(PositionName)), reader.Property(PositionName).Assign(offset), property.Call(AddRangeName, null, new[] { reader.Call(ReadBytesName, null, new[] { count.Convert(typeof(int)) }) }), reader.Property(PositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, offset.Assign(reader.Call(ReadOffsetName, null, null)), count.Assign(reader.Call(ReadUInt16Name, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()) .IfThen(read))); } BlockExpression CompileArray(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var elemInfo = GetPacketInfo(ftype.GetGenericArguments()[0]); var offset = typeof(int).Variable("offset"); var count = typeof(ushort).Variable("count"); var position = typeof(int).Variable("position"); var property = ftype.Variable("property"); var next = typeof(int).Variable("next"); var i = typeof(int).Variable("i"); var elem = elemInfo.Type.Variable("elem"); var loop = CustomExpression.For( i, 0.Constant(), i.LessThan(count.Convert(typeof(int))), i.PreIncrementAssign(), Expression.Block( reader.Property(PositionName).Assign(next), reader.Call(ReadOffsetName, null, null), next.Assign(reader.Call(ReadOffsetName, null, null)), elem.Assign(elemInfo.Type.New()), CompileDeserializer(elemInfo).Constant().Invoke(reader, elem), property.Call(AddName, null, new[] { elem }))); var read = Expression.Block(new[] { position, next, elem }, position.Assign(reader.Property(PositionName)), next.Assign(offset), loop, reader.Property(PositionName).Assign(position)); return(Expression.Block(new[] { offset, count, property }, count.Assign(reader.Call(ReadUInt16Name, null, null)), offset.Assign(reader.Call(ReadOffsetName, null, null)), property.Assign(packet.Property(prop)), property.Call(ClearName, null, null), count.NotEqual(((ushort)0).Constant()) .IfThen(read))); } BlockExpression CompileString(CompilerPacketFieldInfo info) { var offset = typeof(int).Variable("offset"); var position = typeof(int).Variable("position"); return(Expression.Block(new[] { offset, position }, offset.Assign(reader.Call(ReadOffsetName, null, null)), position.Assign(reader.Property(PositionName)), reader.Property(PositionName).Assign(offset), packet.Property(info.Property).Assign(reader.Call(ReadStringName, null, null)), reader.Property(PositionName).Assign(position))); } BlockExpression CompilePrimitive(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = (info.Attribute?.IsSimpleSkill ?? false) ? SimpleName : string.Empty; Expression read = reader.Call(ReadName + prefix + etype.Name, null, null); if (ftype.IsEnum) { read = read.Convert(ftype); } return(Expression.Block(packet.Property(prop).Assign(read))); } var exprs = new List <Expression>() { packet.Assign(target.Convert(info.Type)), }; foreach (var field in info.Fields.Cast <CompilerPacketFieldInfo>()) { exprs.Add( field.IsByteArray ? CompileByteArray(field) : field.IsArray ? CompileArray(field) : field.IsString ? CompileString(field) : CompilePrimitive(field)); } return(Expression.Lambda <Action <GameBinaryReader, object> >( Expression.Block(new[] { packet }, exprs), reader, target).Compile()); }
protected override void OnDeserialize(GameBinaryReader reader, PacketInfo info, SerializablePacket packet) { _deserializers[info](reader, packet); }
Action <GameBinaryWriter, object> CompileSerializer(PacketInfo info) { if (_serializers.TryGetValue(info, out var s)) { return(s); } var writer = typeof(GameBinaryWriter).Parameter("writer"); var source = typeof(object).Parameter("source"); var packet = info.Type.Variable("packet2"); var offsets = new List <(CompilerPacketFieldInfo, ParameterExpression)>(); BlockExpression CompileByteArray1(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable($"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }))); } BlockExpression CompileArray1(CompilerPacketFieldInfo info) { var prop = info.Property; var offset = typeof(int).Variable($"offset{prop.Name}"); offsets.Add((info, offset)); var property = packet.Property(prop); return(Expression.Block( writer.Call(WriteUInt16Name, null, new[] { property.Property(CountName).Convert(typeof(ushort)) }), offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompileString1(CompilerPacketFieldInfo info) { var offset = typeof(int).Variable($"offset{info.Property.Name}"); offsets.Add((info, offset)); return(Expression.Block( offset.Assign(writer.Property(PositionName)), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }))); } BlockExpression CompilePrimitive(CompilerPacketFieldInfo info) { var prop = info.Property; var ftype = prop.PropertyType; var etype = ftype.IsEnum ? ftype.GetEnumUnderlyingType() : ftype; var prefix = (info.Attribute?.IsSimpleSkill ?? false) ? SimpleName : string.Empty; Expression property; if (ftype == typeof(ushort) && (info.Attribute?.IsUnknownArray ?? false)) { property = ((ushort)0).Constant(); } else { property = packet.Property(prop); if (ftype.IsEnum) { property = property.Convert(etype); } } return(Expression.Block( writer.Call(WriteName + prefix + etype.Name, null, new[] { property }))); } var exprs = new List <Expression>() { packet.Assign(source.Convert(info.Type)), }; foreach (var field in info.Fields.Cast <CompilerPacketFieldInfo>()) { exprs.Add( field.IsByteArray ? CompileByteArray1(field) : field.IsArray ? CompileArray1(field) : field.IsString ? CompileString1(field) : CompilePrimitive(field)); } BlockExpression CompileByteArray2(CompilerPacketFieldInfo info, ParameterExpression offset) { var property = info.Property.PropertyType.Variable("property"); var position = typeof(int).Variable("position"); var write = Expression.Block(new[] { position }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), writer.Call(WriteBytesName, null, new[] { property.Call(ToArrayName, null, null).Convert(typeof(ReadOnlySpan <byte>)) })); return(Expression.Block(new[] { property }, property.Assign(packet.Property(info.Property)), property.Property(CountName).NotEqual(0.Constant()) .IfThen(write))); } BlockExpression CompileArray2(CompilerPacketFieldInfo info, ParameterExpression offset) { var prop = info.Property; var ftype = prop.PropertyType; var elemInfo = GetPacketInfo(ftype.GetGenericArguments()[0]); var property = ftype.Variable("property"); var count = typeof(int).Variable("count"); var position = typeof(int).Variable("position"); var i = typeof(int).Variable("i"); var position2 = typeof(int).Variable("position2"); var position3 = typeof(int).Variable("position3"); var writeNext = Expression.Block(new[] { position3 }, position3.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(position2.Add(sizeof(ushort).Constant())), writer.Call(WriteOffsetName, null, new[] { position3 }), writer.Property(PositionName).Assign(position3)); var loop = CustomExpression.For( i, 0.Constant(), i.LessThan(count), i.PreIncrementAssign(), Expression.Block(new[] { position2 }, position2.Assign(writer.Property(PositionName)), writer.Call(WriteOffsetName, null, new[] { position2 }), writer.Call(WriteUInt16Name, null, new[] { ((ushort)0).Constant() }), CompileSerializer(elemInfo).Constant().Invoke(writer, property.Property(ItemName, i)), i.NotEqual(count.Subtract(1.Constant())) .IfThen(writeNext))); var write = Expression.Block(new[] { position, i }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), loop); return(Expression.Block(new[] { property, count }, property.Assign(packet.Property(prop)), count.Assign(property.Property(CountName)), count.NotEqual(0.Constant()) .IfThen(write))); } BlockExpression CompileString2(CompilerPacketFieldInfo info, ParameterExpression offset) { var position = typeof(int).Variable("position"); return(Expression.Block(new[] { position }, position.Assign(writer.Property(PositionName)), writer.Property(PositionName).Assign(offset), writer.Call(WriteOffsetName, null, new[] { position }), writer.Property(PositionName).Assign(position), writer.Call(WriteStringName, null, new[] { packet.Property(info.Property) }))); } foreach (var(field, offset) in offsets) { exprs.Add( field.IsByteArray ? CompileByteArray2(field, offset) : field.IsArray ? CompileArray2(field, offset) : CompileString2(field, offset)); } var vars = new[] { packet }.Concat(offsets.Select(tup => tup.Item2)); return(Expression.Lambda <Action <GameBinaryWriter, object> >( Expression.Block(vars, exprs), writer, source).Compile()); }
protected override void OnSerialize(GameBinaryWriter writer, PacketInfo info, SerializablePacket packet) { _serializers[info](writer, packet); }
Func <SerializablePacket> CompileCreator(PacketInfo info) { return(_creators.TryGetValue(info, out var c) ? c : Expression.Lambda <Func <SerializablePacket> >(info.Type.New()).Compile()); }
protected abstract void OnDeserialize(GameBinaryReader reader, PacketInfo info, SerializablePacket packet);
protected abstract void OnSerialize(GameBinaryWriter writer, PacketInfo info, SerializablePacket packet);
protected abstract SerializablePacket OnCreate(PacketInfo info);
protected override void OnSerialize(GameBinaryWriter writer, PacketInfo info, SerializablePacket packet) { SerializeObject(writer, packet); }
protected override SerializablePacket OnCreate(PacketInfo info) { return((SerializablePacket)Activator.CreateInstance(info.Type)); }