using Google.Protobuf; using Luban.Job.Cfg.Datas; using Luban.Job.Cfg.DataSources; using Luban.Job.Cfg.DataVisitors; using Luban.Job.Cfg.Defs; using Luban.Job.Common.Generate; using Luban.Job.Common.Types; using Luban.Job.Common.TypeVisitors; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Luban.Job.Cfg.DataExporters { class ProtobufBinExportor : IDataActionVisitor { public static ProtobufBinExportor Ins { get; } = new(); public void WriteList(DefTable table, List datas, MemoryStream x) { var cos = new CodedOutputStream(x); foreach (var d in datas) { cos.WriteTag(1, WireFormat.WireType.LengthDelimited); d.Data.Apply(this, cos); } cos.Flush(); } public void Accept(DBool type, CodedOutputStream x) { x.WriteBool(type.Value); } public void Accept(DByte type, CodedOutputStream x) { x.WriteInt32(type.Value); } public void Accept(DShort type, CodedOutputStream x) { x.WriteInt32(type.Value); } public void Accept(DFshort type, CodedOutputStream x) { x.WriteInt32(type.Value); } public void Accept(DInt type, CodedOutputStream x) { x.WriteInt32(type.Value); } public void Accept(DFint type, CodedOutputStream x) { x.WriteSFixed32(type.Value); } public void Accept(DLong type, CodedOutputStream x) { x.WriteInt64(type.Value); } public void Accept(DFlong type, CodedOutputStream x) { x.WriteSFixed64(type.Value); } public void Accept(DFloat type, CodedOutputStream x) { x.WriteFloat(type.Value); } public void Accept(DDouble type, CodedOutputStream x) { x.WriteDouble(type.Value); } public void Accept(DEnum type, CodedOutputStream x) { x.WriteInt32(type.Value); } public void Accept(DDateTime type, CodedOutputStream x) { x.WriteInt64(type.UnixTimeOfCurrentAssembly); } public void Accept(DString type, CodedOutputStream x) { x.WriteString(type.Value); } public void Accept(DBytes type, CodedOutputStream x) { x.WriteBytes(ByteString.CopyFrom(type.Value)); } public void Accept(DText type, CodedOutputStream x) { // 此处与 binary格式不同. binary格式还包含了key // 意味pb格式是无法支持动态本土化的。 x.WriteString(type.TextOfCurrentAssembly); } private MemoryStream AllocMemoryStream() { // TODO 优化 return new MemoryStream(); } private void FreeMemoryStream(MemoryStream cos) { cos.Seek(0, SeekOrigin.Begin); } public void Accept(DVector2 type, CodedOutputStream x) { var ms = AllocMemoryStream(); var temp = new CodedOutputStream(ms); temp.WriteTag(1, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.X); temp.WriteTag(2, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.Y); temp.Flush(); ms.Seek(0, SeekOrigin.Begin); x.WriteBytes(ByteString.FromStream(ms)); FreeMemoryStream(ms); } public void Accept(DVector3 type, CodedOutputStream x) { var ms = AllocMemoryStream(); var temp = new CodedOutputStream(ms); temp.WriteTag(1, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.X); temp.WriteTag(2, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.Y); temp.WriteTag(3, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.Z); temp.Flush(); ms.Seek(0, SeekOrigin.Begin); x.WriteBytes(ByteString.FromStream(ms)); FreeMemoryStream(ms); } public void Accept(DVector4 type, CodedOutputStream x) { var ms = AllocMemoryStream(); var temp = new CodedOutputStream(ms); temp.WriteTag(1, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.X); temp.WriteTag(2, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.Y); temp.WriteTag(3, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.Z); temp.WriteTag(4, WireFormat.WireType.Fixed32); temp.WriteFloat(type.Value.W); temp.Flush(); ms.Seek(0, SeekOrigin.Begin); x.WriteBytes(ByteString.FromStream(ms)); FreeMemoryStream(ms); } private void WriteRawMessageWithoutLength(DBean type, CodedOutputStream temp) { //var ms = AllocMemoryStream(); //var temp = new CodedOutputStream(ms); //if (bean.IsAbstractType) //{ // temp.WriteTag(type.ImplType.AutoId, WireFormat.WireType.LengthDelimited); //} var defFields = type.ImplType.HierarchyFields; int index = 0; foreach (var field in type.Fields) { var defField = (DefField)defFields[index++]; if (!defField.NeedExport) { continue; } var fieldType = defField.CType; if (field == null) { continue; } switch (field) { case DArray arr: { WriteList(fieldType.ElementType, defField.AutoId, arr.Datas, temp); break; } case DList list: { WriteList(fieldType.ElementType, defField.AutoId, list.Datas, temp); break; } case DSet set: { WriteList(fieldType.ElementType, defField.AutoId, set.Datas, temp); break; } case DMap map: { WriteMap(map, defField.AutoId, temp); break; } default: { temp.WriteTag(defField.AutoId, defField.CType.Apply(ProtobufWireTypeVisitor.Ins)); field.Apply(this, temp); break; } } } //temp.Flush(); //ms.Seek(0, SeekOrigin.Begin); //var bs = ByteString.FromStream(ms); //x.WriteBytes(bs); //FreeMemoryStream(ms); } private void EnterScope(CodedOutputStream x, Action action) { var ms = AllocMemoryStream(); var temp = new CodedOutputStream(ms); action(temp); temp.Flush(); ms.Seek(0, SeekOrigin.Begin); var bs = ByteString.FromStream(ms); x.WriteBytes(bs); FreeMemoryStream(ms); } public void Accept(DBean type, CodedOutputStream x) { EnterScope(x, cos => { var bean = type.Type; if (bean.IsAbstractType) { cos.WriteTag(type.ImplType.AutoId, WireFormat.WireType.LengthDelimited); EnterScope(cos, cos2 => WriteRawMessageWithoutLength(type, cos2)); } else { WriteRawMessageWithoutLength(type, cos); } }); } private void WriteList(TType elementType, int fieldId, List datas, CodedOutputStream x) { if (elementType.Apply(IsProtobufPackedType.Ins)) { x.WriteTag(fieldId, WireFormat.WireType.LengthDelimited); var ms = AllocMemoryStream(); var temp = new CodedOutputStream(ms); foreach (var data in datas) { data.Apply(this, temp); } temp.Flush(); ms.Seek(0, SeekOrigin.Begin); x.WriteBytes(ByteString.FromStream(ms)); FreeMemoryStream(ms); } else { var eleWireType = elementType.Apply(ProtobufWireTypeVisitor.Ins); foreach (var data in datas) { x.WriteTag(fieldId, eleWireType); data.Apply(this, x); } } } public void Accept(DArray type, CodedOutputStream x) { throw new NotImplementedException(); } public void Accept(DList type, CodedOutputStream x) { throw new NotImplementedException(); } public void Accept(DSet type, CodedOutputStream x) { throw new NotImplementedException(); } private void WriteMap(DMap type, int fieldId, CodedOutputStream x) { var keyType = type.Type.KeyType; var valueType = type.Type.ValueType; var ms = AllocMemoryStream(); foreach (var e in type.Datas) { x.WriteTag(fieldId, WireFormat.WireType.LengthDelimited); ms.Seek(0, SeekOrigin.Begin); var temp = new CodedOutputStream(ms); temp.WriteTag(1, keyType.Apply(ProtobufWireTypeVisitor.Ins)); e.Key.Apply(this, temp); temp.WriteTag(2, valueType.Apply(ProtobufWireTypeVisitor.Ins)); e.Value.Apply(this, temp); temp.Flush(); ms.Seek(0, SeekOrigin.Begin); x.WriteBytes(ByteString.FromStream(ms)); } FreeMemoryStream(ms); } public void Accept(DMap type, CodedOutputStream x) { throw new NotSupportedException(); } } }