框架更新

This commit is contained in:
Bob.Song
2025-10-29 17:59:43 +08:00
parent fc18c8626a
commit a2cb248512
429 changed files with 7173 additions and 38748 deletions

View File

@@ -11,7 +11,7 @@ using NBC.Serialize;
namespace NBF
{
[ProtoContract]
public sealed partial class (ConfigName) : ASerialize, IProto, IConfigTable
public sealed partial class (ConfigName) : ASerialize, IConfigTable
{
(Fields)
[ProtoIgnore]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class (ConfigName) : ASerialize, IProto, IConfigTable
public sealed partial class (ConfigName) : ASerialize, IConfigTable
{
(Fields)
[ProtoIgnore]

View File

@@ -1,10 +0,0 @@
{
"ExcelPath": "D:\\work\\Fishing2\\Config",
"ClientPath": "D:\\work\\Fishing2\\Assets\\Scripts\\Generate\\Config",
"ClientJsonPath": "D:\\work\\Fishing2\\Assets\\Resources\\config",
"ServerPath": "D:\\work\\Fishing2Server\\Entity\\Generate\\ConfigTable\\Entity",
"ServerJsonPath": "D:\\work\\Fishing2Server\\Config\\Json",
"GenClient": true,
"GenServer": true,
"ExcelVersionPath": "D:\\work\\Fishing2\\Config\\Version.txt"
}

View File

@@ -1,10 +1,10 @@
{
"ExcelPath": "D:\\myself\\Games\\Fishing2\\Config",
"ClientPath": "D:\\myself\\Games\\Fishing2\\Assets\\Scripts\\Generate\\Config",
"ClientJsonPath": "D:\\myself\\Games\\Fishing2\\Assets\\Resources\\config",
"ServerPath": "D:\\myself\\Games\\Fishing2Server\\Entity\\Generate\\ConfigTable\\Entity",
"ServerJsonPath": "D:\\myself\\Games\\Fishing2Server\\Config\\Json",
"ExcelPath": "D:\\work\\Fishing2\\Config",
"ClientPath": "D:\\work\\Fishing2\\Assets\\Scripts\\Generate\\Config",
"ClientJsonPath": "D:\\work\\Fishing2\\Assets\\Resources\\config",
"ServerPath": "D:\\work\\Fishing2Server\\Entity\\Generate\\ConfigTable\\Entity",
"ServerJsonPath": "D:\\work\\Fishing2Server\\Config\\Json",
"GenClient": true,
"GenServer": true,
"ExcelVersionPath": "D:\\myself\\Games\\Fishing2\\Config\\Version.txt"
"ExcelVersionPath": "D:\\work\\Fishing2\\Config\\Version.txt"
}

View File

@@ -0,0 +1,10 @@
{
"ExcelPath": "D:\\myself\\Games\\Fishing2\\Config",
"ClientPath": "D:\\myself\\Games\\Fishing2\\Assets\\Scripts\\Generate\\Config",
"ClientJsonPath": "D:\\myself\\Games\\Fishing2\\Assets\\Resources\\config",
"ServerPath": "D:\\myself\\Games\\Fishing2Server\\Entity\\Generate\\ConfigTable\\Entity",
"ServerJsonPath": "D:\\myself\\Games\\Fishing2Server\\Config\\Json",
"GenClient": true,
"GenServer": true,
"ExcelVersionPath": "D:\\myself\\Games\\Fishing2\\Config\\Version.txt"
}

Binary file not shown.

View File

@@ -1,6 +1,7 @@
#if SERVER
using ProtoBuf;
(UsingNamespace)
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,10 +17,11 @@ using Fantasy.Serialize;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8618
namespace NBC
{
namespace Fantasy
{
#else
using ProtoBuf;
using System;
(UsingNamespace)
using System.Collections.Generic;
using Fantasy;
@@ -30,4 +32,4 @@ using Fantasy.Serialize;
namespace Fantasy
{
#endif
(Content)}
(Content)}

View File

@@ -664,7 +664,6 @@ public sealed class ExcelExporter
Task.WaitAll(exportToBinaryTasks.ToArray());
}
private void GenerateBinary(string fileInfoFullName, ExcelWorksheet excelWorksheet, DynamicConfigDataType dynamicInfo, List<int> cols, string id, int row, bool isLast, bool isServer)
{
if (cols == null || IsNullOrEmpty(id) || cols.Count <= 0 || dynamicInfo?.ConfigType == null)
@@ -719,7 +718,6 @@ public sealed class ExcelExporter
dynamicInfo.Json.AppendLine($"{json},");
}
}
/// <summary>
/// 从 Excel 文件加载工作表并返回 ExcelWorksheet 对象。
/// </summary>

View File

@@ -35,31 +35,28 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Pool\Normal\Pool.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Pool\Normal\Pool.cs">
<Link>Core\Base\Pool.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Packages\Fantasy.ConfigTable\Net\Dictionary\IntDictionaryConfig.cs">
<Compile Include="..\..\..\Fantasy.Packages\Fantasy.ConfigTable\Net\Dictionary\IntDictionaryConfig.cs">
<Link>Core\Dictionary\IntDictionaryConfig.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Packages\Fantasy.ConfigTable\Net\Dictionary\StringDictionaryConfig.cs">
<Compile Include="..\..\..\Fantasy.Packages\Fantasy.ConfigTable\Net\Dictionary\StringDictionaryConfig.cs">
<Link>Core\Dictionary\StringDictionaryConfig.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Packages\Fantasy.ConfigTable\Net\Interface\IConfigTable.cs">
<Compile Include="..\..\..\Fantasy.Packages\Fantasy.ConfigTable\Net\Interface\IConfigTable.cs">
<Link>Core\Interface\IConfigTable.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\ProtoBufPackHelper\IProto.cs">
<Link>Core\Serialize\ProtoBufPackHelper\IProto.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\TimeHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\TimeHelper.cs">
<Link>Excel\Base\TimeHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Pool\Interface\IPool.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Pool\Interface\IPool.cs">
<Link>Excel\Base\IPool.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Assembly\AssemblyInfo.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Assembly\AssemblyInfo.cs">
<Link>Excel\Base\AssemblyInfo.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\DataStructure\Collection\OneToManyListPool.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\DataStructure\Collection\OneToManyListPool.cs">
<Link>Excel\Base\OneToManyList.cs</Link>
</Compile>
<Compile Include="..\Fantasy.Tools.NetworkProtocol\Core\Base\ConsoleLog.cs">
@@ -68,37 +65,37 @@
<Compile Include="..\Fantasy.Tools.NetworkProtocol\Core\Base\ExportPlatform.cs">
<Link>Core\ExportPlatform.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\FileHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\FileHelper.cs">
<Link>Core\FileHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\HashCodeHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\HashCodeHelper.cs">
<Link>Core\HashCodeHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\JsonHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\JsonHelper.cs">
<Link>Core\JsonHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\BsonPackHelperNet.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\BsonPackHelperNet.cs">
<Link>Core\Serialize\BsonPack\BsonPackHelperNet.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\StructBsonSerialize.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\StructBsonSerialize.cs">
<Link>Core\Serialize\BsonPack\StructBsonSerialize.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\SupportInitializeChecker.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\BsonPack\SupportInitializeChecker.cs">
<Link>Core\Serialize\BsonPack\SupportInitializeChecker.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\Interface\ASerialize.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\Interface\ASerialize.cs">
<Link>Core\Serialize\Interface\ASerialize.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\Interface\ISerialize.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\Interface\ISerialize.cs">
<Link>Core\Serialize\Interface\ISerialize.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\SerializerManager.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\SerializerManager.cs">
<Link>Core\Serialize\SerializerManager.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\ProtoBufPackHelper\ProtoBufPackHelperNet.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\ProtoBufPackHelper\ProtoBufPackHelperNet.cs">
<Link>Core\Serialize\ProtoBufPackHelper\ProtoBufPackHelperNet.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\MemoryStreamBuffer.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Serialize\MemoryStreamBuffer.cs">
<Link>Excel\Base\MemoryStreamBuffer.cs</Link>
</Compile>
</ItemGroup>

View File

@@ -0,0 +1,38 @@
namespace Fantasy.Tools.ProtocalExporter;
public static class NetworkProtocolTemplate
{
private static string? _cachedTemplate;
public static string Template
{
get
{
if (_cachedTemplate != null)
{
return _cachedTemplate;
}
// 尝试从多个位置读取模板文件
var possiblePaths = new[]
{
Path.Combine(ExporterAges.Instance.Folder ?? Directory.GetCurrentDirectory(), "NetworkProtocolTemplate.txt"),
Path.Combine(Directory.GetCurrentDirectory(), "NetworkProtocolTemplate.txt"),
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NetworkProtocolTemplate.txt")
};
foreach (var path in possiblePaths)
{
if (File.Exists(path))
{
_cachedTemplate = File.ReadAllText(path);
return _cachedTemplate;
}
}
throw new FileNotFoundException("找不到 NetworkProtocolTemplate.txt 模板文件!\n" +
"请确保模板文件位于以下位置之一:\n" +
string.Join("\n", possiblePaths));
}
}
}

View File

@@ -4,7 +4,6 @@ using Fantasy.Helper;
using Fantasy.Network;
using OpCode = Fantasy.Network.OpCode;
using OpCodeType = Fantasy.Network.OpCodeType;
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
// ReSharper disable PossibleNullReferenceException
@@ -14,19 +13,38 @@ using OpCodeType = Fantasy.Network.OpCodeType;
namespace Fantasy.Tools.ProtocalExporter;
// 自定义异常,用于协议格式错误
public class ProtocolFormatException : Exception
{
public ProtocolFormatException(string message) : base(message) { }
}
public enum NetworkProtocolOpCodeType
{
None = 0,
Outer = 1,
Inner = 2,
}
public sealed class OpcodeInfo
{
public uint Code;
public string Name;
}
public sealed class MessageFieldInfo
{
public string FieldName;
public string FieldType;
}
public sealed class MessageHelperInfo
{
public string MessageName;
public string MessageType; // "IMessage" or "IRequest"
public string ResponseType; // Only for IRequest
public List<MessageFieldInfo> Fields = new(); // 消息的属性字段
}
public sealed class ProtocolExporter
{
private string _serverTemplate;
@@ -38,13 +56,12 @@ public sealed class ProtocolExporter
private readonly string _networkProtocolServerDirectory;
private readonly string _networkProtocolDirectoryOuter;
private readonly string _networkProtocolDirectoryInner;
public ProtocolExporter()
{
Console.OutputEncoding = Encoding.UTF8;
if (ExporterSettingsHelper.NetworkProtocolDirectory == null ||
ExporterSettingsHelper.NetworkProtocolDirectory.Trim() == "")
if (ExporterSettingsHelper.NetworkProtocolDirectory == null || ExporterSettingsHelper.NetworkProtocolDirectory.Trim() == "")
{
Log.Info($"NetworkProtocolDirectory Can not be empty!");
return;
@@ -123,12 +140,21 @@ public sealed class ProtocolExporter
var errorCodeStr = new StringBuilder();
var usingNamespace = new HashSet<string>();
var saveDirectory = new Dictionary<string, string>();
var helperInfos = new List<MessageHelperInfo>();
OpcodeInfo opcodeInfo = null;
ProtocolOpCode protocolOpCode = null;
string[] protocolFiles = null;
_opcodes.Clear();
MessageHelperInfo currentHelperInfo = null; // 当前正在处理的消息Helper信息
// 格式检测相关
var allMessageNames = new HashSet<string>(); // 所有消息名称
var currentFieldNames = new HashSet<string>(); // 当前消息的字段名
var currentFieldNumbers = new HashSet<int>(); // 当前消息的字段编号
var currentFilePath = ""; // 当前处理的文件路径
_opcodes.Clear();
switch (opCodeType)
{
case NetworkProtocolOpCodeType.Outer:
@@ -137,12 +163,12 @@ public sealed class ProtocolExporter
{
saveDirectory.Add(_networkProtocolServerDirectory, _serverTemplate);
}
if (ExporterAges.Instance.ExportPlatform.HasFlag(ExportPlatform.Client))
{
saveDirectory.Add(_networkProtocolClientDirectory, _clientTemplate);
}
protocolOpCode = new ProtocolOpCode()
{
Message = OpCodeType.OuterMessage,
@@ -162,8 +188,7 @@ public sealed class ProtocolExporter
RoamingResponse = OpCodeType.OuterRoamingResponse,
};
opCodeName = "OuterOpcode";
protocolFiles = FileHelper.GetDirectoryFile(_networkProtocolDirectoryOuter, "*.proto",
SearchOption.AllDirectories);
protocolFiles = FileHelper.GetDirectoryFile(_networkProtocolDirectoryOuter, "*.proto", SearchOption.AllDirectories);
break;
}
case NetworkProtocolOpCodeType.Inner:
@@ -188,8 +213,7 @@ public sealed class ProtocolExporter
};
opCodeName = "InnerOpcode";
saveDirectory.Add(_networkProtocolServerDirectory, _serverTemplate);
protocolFiles = FileHelper.GetDirectoryFile(_networkProtocolDirectoryInner, "*.proto",
SearchOption.AllDirectories);
protocolFiles = FileHelper.GetDirectoryFile(_networkProtocolDirectoryInner, "*.proto", SearchOption.AllDirectories);
break;
}
}
@@ -198,11 +222,12 @@ public sealed class ProtocolExporter
{
return;
}
#region GenerateFiles
foreach (var filePath in protocolFiles)
{
currentFilePath = filePath; // 记录当前文件路径
var keyIndex = 1;
var parameter = "";
var hasOpCode = false;
@@ -227,8 +252,7 @@ public sealed class ProtocolExporter
if (currentLine.StartsWith("///"))
{
messageStr.AppendFormat(" /// <summary>\r\n" + " /// {0}\r\n" + " /// </summary>\r\n",
currentLine.Substring("///".Length));
messageStr.AppendFormat(" /// <summary>\r\n" + " /// {0}\r\n" + " /// </summary>\r\n", currentLine.Substring("///".Length));
continue;
}
@@ -247,15 +271,6 @@ public sealed class ProtocolExporter
protocolOpCodeType = OpCodeProtocolType.ProtoBuf;
break;
}
// case "MemoryPack":
// {
// keyIndex = 0;
// protocolType = "\t[MemoryPackable]";
// protocolIgnore = "\t\t[MemoryPackIgnore]";
// protocolMember = "MemoryPackOrder";
// // protocolOpCodeType = OpCodeProtocolType.MemoryPack;
// break;
// }
case "Bson":
{
if (opCodeType == NetworkProtocolOpCodeType.Outer)
@@ -263,7 +278,6 @@ public sealed class ProtocolExporter
Log.Error("Under Outer, /// does not support the Bson protocol!");
return;
}
protocolType = null;
protocolIgnore = "\t\t[BsonIgnore]";
protocolMember = null;
@@ -293,6 +307,21 @@ public sealed class ProtocolExporter
{
isMsgHead = true;
className = currentLine.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries)[1];
// 检测消息名称是否重复
if (!allMessageNames.Add(className))
{
var errorMsg = $"协议格式错误!\n文件: {currentFilePath}\n消息名称重复: {className}";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
throw new ProtocolFormatException(errorMsg);
}
// 清空当前消息的字段集合
currentFieldNames.Clear();
currentFieldNumbers.Clear();
var splits = currentLine.Split(new[] { "//" }, StringSplitOptions.RemoveEmptyEntries);
if (isSetProtocol)
@@ -306,12 +335,11 @@ public sealed class ProtocolExporter
{
messageStr.AppendLine("\t[ProtoContract]");
}
if (splits.Length > 1)
{
hasOpCode = true;
var parameterArray = currentLine.Split(new[] { "//" }, StringSplitOptions.RemoveEmptyEntries)[1]
.Trim().Split(',');
var parameterArray = currentLine.Split(new[] { "//" }, StringSplitOptions.RemoveEmptyEntries)[1].Trim().Split(',');
parameter = parameterArray[0].Trim();
opcodeInfo = new OpcodeInfo()
{
@@ -339,15 +367,12 @@ public sealed class ProtocolExporter
break;
}
}
break;
}
case 3:
{
responseTypeStr = parameterArray[1].Trim();
customRouteType = parameter.Contains("IRoaming")
? $"Fantasy.RoamingType.{parameterArray[2].Trim()}"
: $"Fantasy.RouteType.{parameterArray[2].Trim()}";
customRouteType = parameter.Contains("IRoaming") ? $"Fantasy.RoamingType.{parameterArray[2].Trim()}" : $"Fantasy.RouteType.{parameterArray[2].Trim()}";
break;
}
}
@@ -361,11 +386,6 @@ public sealed class ProtocolExporter
messageStr.Append(string.IsNullOrWhiteSpace(parameter)
? $"\tpublic partial class {className} : AMessage"
: $"\tpublic partial class {className} : AMessage, {parameter}");
if (protocolMember == "ProtoMember")
{
messageStr.Append(", IProto");
}
continue;
}
@@ -380,22 +400,33 @@ public sealed class ProtocolExporter
{
messageStr.AppendLine("\n\t{");
messageStr.AppendLine($"\t\tpublic static {className} Create(Scene scene)");
messageStr.AppendLine(
$"\t\t{{\n\t\t\treturn scene.MessagePoolComponent.Rent<{className}>();\n\t\t}}");
messageStr.AppendLine($"\t\t{{\n\t\t\treturn scene.MessagePoolComponent.Rent<{className}>();\n\t\t}}");
messageStr.AppendLine($"\t\tpublic override void Dispose()");
messageStr.AppendLine($"\t\t{{");
messageStr.AppendLine(
$"<<<<Dispose>>>#if FANTASY_NET || FANTASY_UNITY\n\t\t\tGetScene().MessagePoolComponent.Return<{className}>(this);\n#endif");
messageStr.AppendLine($"<<<<Dispose>>>#if FANTASY_NET || FANTASY_UNITY\n\t\t\tGetScene().MessagePoolComponent.Return<{className}>(this);\n#endif");
messageStr.AppendLine($"\t\t}}");
if (parameter == "IMessage")
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Message,
protocolOpCode.AMessage++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Message, protocolOpCode.AMessage++);
messageStr.AppendLine($"\t\tpublic uint OpCode() {{ return {opCodeName}.{className}; }}");
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IMessage"
};
helperInfos.Add(currentHelperInfo);
}
}
else
{
// 保存 responseTypeStr 用于后续收集Helper信息
var savedResponseType = responseTypeStr;
if (responseTypeStr != null)
{
messageStr.AppendLine(protocolIgnore);
@@ -413,8 +444,7 @@ public sealed class ProtocolExporter
if (hasOpCode)
{
messageStr.AppendLine(
$"\t\tpublic uint OpCode() {{ return {opCodeName}.{className}; }}");
messageStr.AppendLine($"\t\tpublic uint OpCode() {{ return {opCodeName}.{className}; }}");
}
if (customRouteType != null)
@@ -428,19 +458,28 @@ public sealed class ProtocolExporter
{
case "IRequest":
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Request,
protocolOpCode.ARequest++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Request, protocolOpCode.ARequest++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IRequest",
ResponseType = savedResponseType
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "IResponse":
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Response,
protocolOpCode.AResponse++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.Response, protocolOpCode.AResponse++);
if (!string.IsNullOrEmpty(protocolMember))
{
errorCodeStr.AppendLine($"\t\t[{protocolMember}(ErrorCodeKeyIndex)]");
}
errorCodeStr.AppendLine("\t\tpublic uint ErrorCode { get; set; }");
disposeStr.AppendLine($"\t\t\tErrorCode = default;");
break;
@@ -451,28 +490,44 @@ public sealed class ProtocolExporter
{
case "IAddressableRouteMessage":
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.AddressableMessage,
protocolOpCode.AAddressableMessage++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.AddressableMessage, protocolOpCode.AAddressableMessage++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IMessage" // IAddressableRouteMessage也是Send方式
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "IAddressableRouteRequest":
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.AddressableRequest,
protocolOpCode.AAddressableRequest++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.AddressableRequest, protocolOpCode.AAddressableRequest++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IRequest",
ResponseType = savedResponseType
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "IAddressableRouteResponse":
{
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.AddressableResponse,
protocolOpCode.AAddressableResponse++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.AddressableResponse, protocolOpCode.AAddressableResponse++);
if (!string.IsNullOrEmpty(protocolMember))
{
errorCodeStr.AppendLine($"\t\t[{protocolMember}(ErrorCodeKeyIndex)]");
}
errorCodeStr.AppendLine("\t\tpublic uint ErrorCode { get; set; }");
disposeStr.AppendLine($"\t\t\tErrorCode = default;");
break;
@@ -481,44 +536,54 @@ public sealed class ProtocolExporter
{
if (opCodeType == NetworkProtocolOpCodeType.Inner)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.CustomRouteMessage, protocolOpCode.ACustomRouteMessage++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.CustomRouteMessage,
protocolOpCode.ACustomRouteMessage++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IMessage" // ICustomRouteMessage也是Send方式
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "ICustomRouteRequest":
{
if (opCodeType == NetworkProtocolOpCodeType.Inner)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.CustomRouteRequest, protocolOpCode.ACustomRouteRequest++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.CustomRouteRequest,
protocolOpCode.ACustomRouteRequest++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IRequest",
ResponseType = savedResponseType
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "ICustomRouteResponse":
{
if (opCodeType == NetworkProtocolOpCodeType.Inner)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.CustomRouteResponse,
protocolOpCode.ACustomRouteResponse++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.CustomRouteResponse, protocolOpCode.ACustomRouteResponse++);
if (!string.IsNullOrEmpty(protocolMember))
{
errorCodeStr.AppendLine($"\t\t[{protocolMember}(ErrorCodeKeyIndex)]");
}
errorCodeStr.AppendLine("\t\tpublic uint ErrorCode { get; set; }");
disposeStr.AppendLine($"\t\t\tErrorCode = default;");
break;
@@ -529,8 +594,18 @@ public sealed class ProtocolExporter
// {
// throw new NotSupportedException("Under Inner, /// does not support the IRoamingMessage!");
// }
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RoamingMessage, protocolOpCode.ARoamingMessage++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RoamingMessage, protocolOpCode.ARoamingMessage++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IMessage" // IRoamingMessage也是Send方式
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "IRoamingRequest":
@@ -539,8 +614,19 @@ public sealed class ProtocolExporter
// {
// throw new NotSupportedException("Under Inner, /// does not support the IRoamingRequest!");
// }
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RoamingRequest, protocolOpCode.ARoamingRequest++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RoamingRequest, protocolOpCode.ARoamingRequest++);
// 收集客户端Helper信息
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
currentHelperInfo = new MessageHelperInfo
{
MessageName = className,
MessageType = "IRequest",
ResponseType = savedResponseType
};
helperInfos.Add(currentHelperInfo);
}
break;
}
case "IRoamingResponse":
@@ -549,13 +635,11 @@ public sealed class ProtocolExporter
// {
// throw new NotSupportedException("Under Inner, /// does not support the IRoamingResponse!");
// }
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RoamingResponse, protocolOpCode.ARoamingResponse++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RoamingResponse, protocolOpCode.ARoamingResponse++);
if (!string.IsNullOrEmpty(protocolMember))
{
errorCodeStr.AppendLine($"\t\t[{protocolMember}(ErrorCodeKeyIndex)]");
}
errorCodeStr.AppendLine("\t\tpublic uint ErrorCode { get; set; }");
disposeStr.AppendLine($"\t\t\tErrorCode = default;");
break;
@@ -564,41 +648,31 @@ public sealed class ProtocolExporter
{
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RouteMessage, protocolOpCode.ARouteMessage++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RouteMessage, protocolOpCode.ARouteMessage++);
break;
}
case "IRouteRequest":
{
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RouteRequest, protocolOpCode.ARouteRequest++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RouteRequest, protocolOpCode.ARouteRequest++);
break;
}
case "IRouteResponse":
{
if (opCodeType == NetworkProtocolOpCodeType.Outer)
{
throw new NotSupportedException(
"Under Inner, /// does not support the ICustomRouteMessage!");
throw new NotSupportedException("Under Inner, /// does not support the ICustomRouteMessage!");
}
opcodeInfo.Code = OpCode.Create(protocolOpCodeType,
protocolOpCode.RouteResponse, protocolOpCode.ARouteResponse++);
opcodeInfo.Code = OpCode.Create(protocolOpCodeType, protocolOpCode.RouteResponse, protocolOpCode.ARouteResponse++);
if (!string.IsNullOrEmpty(protocolMember))
{
errorCodeStr.AppendLine($"\t\t[{protocolMember}(ErrorCodeKeyIndex)]");
}
errorCodeStr.AppendLine("\t\tpublic uint ErrorCode { get; set; }");
disposeStr.AppendLine($"\t\t\tErrorCode = default;");
break;
@@ -620,6 +694,7 @@ public sealed class ProtocolExporter
case "}":
{
isMsgHead = false;
currentHelperInfo = null; // 清空当前Helper信息
errorCodeStr = errorCodeStr.Replace("ErrorCodeKeyIndex", keyIndex.ToString());
messageStr = messageStr.Replace("<<<<Dispose>>>", disposeStr.ToString());
messageStr.Append(errorCodeStr);
@@ -643,28 +718,35 @@ public sealed class ProtocolExporter
if (currentLine.StartsWith("//"))
{
messageStr.AppendFormat("\t\t///<summary>\r\n" + "\t\t/// {0}\r\n" + "\t\t///</summary>\r\n",
currentLine.TrimStart('/', '/'));
messageStr.AppendFormat("\t\t///<summary>\r\n" + "\t\t/// {0}\r\n" + "\t\t///</summary>\r\n", currentLine.TrimStart('/', '/'));
continue;
}
if (currentLine.StartsWith("repeated"))
if (currentLine.StartsWith("repeatedArray"))
{
Repeated(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex);
Repeated(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex, currentHelperInfo, "repeatedArray", className, currentFilePath, currentFieldNames, currentFieldNumbers);
}
else if (currentLine.StartsWith("repeatedList"))
{
Repeated(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex, currentHelperInfo, "repeatedList", className, currentFilePath, currentFieldNames, currentFieldNumbers);
}
else if (currentLine.StartsWith("repeated"))
{
Repeated(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex, currentHelperInfo, "repeated", className, currentFilePath, currentFieldNames, currentFieldNumbers);
}
else
{
Members(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex);
Members(messageStr, disposeStr, currentLine, protocolMember, ref keyIndex, currentHelperInfo, className, currentFilePath, currentFieldNames, currentFieldNumbers);
}
}
var namespaceBuilder = new StringBuilder();
foreach (var @namespace in usingNamespace)
{
namespaceBuilder.Append($"using {@namespace};\n");
}
var csName = $"{Path.GetFileNameWithoutExtension(filePath)}.cs";
foreach (var (directory, template) in saveDirectory)
{
@@ -673,12 +755,25 @@ public sealed class ProtocolExporter
content = content.Replace("(UsingNamespace)", namespaceBuilder.ToString());
await File.WriteAllTextAsync(csFile, content);
}
file.Clear();
}
#endregion
#region GenerateNetworkProtocolHelper
// 为客户端生成NetworkProtocolHelper
if (opCodeType == NetworkProtocolOpCodeType.Outer &&
ExporterAges.Instance.ExportPlatform.HasFlag(ExportPlatform.Client) &&
helperInfos.Count > 0)
{
var helperContent = GenerateNetworkProtocolHelperFile(helperInfos);
var helperFilePath = Path.Combine(_networkProtocolClientDirectory, "NetworkProtocolHelper.cs");
await File.WriteAllTextAsync(helperFilePath, helperContent);
}
#endregion
#region GenerateOpCode
file.Clear();
@@ -691,7 +786,7 @@ public sealed class ProtocolExporter
{
file.AppendLine($"\t\t public const uint {opcode.Name} = {opcode.Code};");
}
_opcodes.Clear();
file.AppendLine("\t}");
file.AppendLine("}");
@@ -701,12 +796,10 @@ public sealed class ProtocolExporter
var csFile = Path.Combine(directory, $"{opCodeName}.cs");
await File.WriteAllTextAsync(csFile, file.ToString());
}
#endregion
}
private void Repeated(StringBuilder file, StringBuilder disposeStr, string newline, string protocolMember,
ref int keyIndex)
private void Repeated(StringBuilder file, StringBuilder disposeStr, string newline, string protocolMember, ref int keyIndex, MessageHelperInfo currentHelperInfo, string repeatedType, string messageName, string filePath, HashSet<string> fieldNames, HashSet<int> fieldNumbers)
{
try
{
@@ -715,21 +808,97 @@ public sealed class ProtocolExporter
var property = newline.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[1];
var name = property[2];
// var memberIndex = int.Parse(property[4]);
var fieldNumber = int.Parse(property[4]);
type = ConvertType(type);
// 检测字段名重复
if (!fieldNames.Add(name))
{
var errorMsg = $"协议格式错误!\n文件: {filePath}\n消息: {messageName}\n字段名重复: {name}";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
throw new ProtocolFormatException(errorMsg);
}
// 检测字段编号重复
if (!fieldNumbers.Add(fieldNumber))
{
var errorMsg = $"协议格式错误!\n文件: {filePath}\n消息: {messageName}\n字段编号重复: {fieldNumber} (字段: {name})";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
throw new ProtocolFormatException(errorMsg);
}
file.AppendLine($"\t\t[{protocolMember}({keyIndex++})]");
file.AppendLine($"\t\tpublic List<{type}> {name} = new List<{type}>();");
disposeStr.AppendLine($"\t\t\t{name}.Clear();");
switch (repeatedType)
{
case "repeated":
// public List<string> List = new List<string>();
file.AppendLine($"\t\tpublic List<{type}> {name} = new List<{type}>();");
disposeStr.AppendLine($"\t\t\t{name}.Clear();");
// 收集字段信息到Helper
if (currentHelperInfo != null)
{
currentHelperInfo.Fields.Add(new MessageFieldInfo
{
FieldName = name,
FieldType = $"List<{type}>"
});
}
break;
case "repeatedArray":
// public string[] List;
file.AppendLine($"\t\tpublic {type}[] {name};");
disposeStr.AppendLine($"\t\t\t{name} = default;");
// 收集字段信息到Helper
if (currentHelperInfo != null)
{
currentHelperInfo.Fields.Add(new MessageFieldInfo
{
FieldName = name,
FieldType = $"{type}[]"
});
}
break;
case "repeatedList":
// public List<string> List;
file.AppendLine($"\t\tpublic List<{type}> {name};");
disposeStr.AppendLine($"\t\t\t{name} = default;");
// 收集字段信息到Helper
if (currentHelperInfo != null)
{
currentHelperInfo.Fields.Add(new MessageFieldInfo
{
FieldName = name,
FieldType = $"List<{type}>"
});
}
break;
}
}
catch (ProtocolFormatException)
{
// 格式错误已经打印过了,直接重新抛出
throw;
}
catch (Exception e)
{
Log.Error($"{newline}\n {e}");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"解析字段时出错: {newline}\n错误: {e.Message}");
Console.ResetColor();
throw;
}
}
private void Members(StringBuilder file, StringBuilder disposeStr, string currentLine, string protocolMember,
ref int keyIndex)
private void Members(StringBuilder file, StringBuilder disposeStr, string currentLine, string protocolMember, ref int keyIndex, MessageHelperInfo currentHelperInfo, string messageName, string filePath, HashSet<string> fieldNames, HashSet<int> fieldNumbers)
{
try
{
@@ -738,19 +907,56 @@ public sealed class ProtocolExporter
var property = currentLine.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[0];
var name = property[1];
// var memberIndex = int.Parse(property[3]);
var fieldNumber = int.Parse(property[3]);
var typeCs = ConvertType(type);
// 检测字段名重复
if (!fieldNames.Add(name))
{
var errorMsg = $"协议格式错误!\n文件: {filePath}\n消息: {messageName}\n字段名重复: {name}";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
throw new ProtocolFormatException(errorMsg);
}
// 检测字段编号重复
if (!fieldNumbers.Add(fieldNumber))
{
var errorMsg = $"协议格式错误!\n文件: {filePath}\n消息: {messageName}\n字段编号重复: {fieldNumber} (字段: {name})";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
throw new ProtocolFormatException(errorMsg);
}
if (protocolMember != null)
{
file.AppendLine($"\t\t[{protocolMember}({keyIndex++})]");
}
file.AppendLine($"\t\tpublic {typeCs} {name} {{ get; set; }}");
disposeStr.AppendLine($"\t\t\t{name} = default;");
// 收集字段信息到Helper
if (currentHelperInfo != null)
{
currentHelperInfo.Fields.Add(new MessageFieldInfo
{
FieldName = name,
FieldType = typeCs
});
}
}
catch (ProtocolFormatException)
{
// 格式错误已经打印过了,直接重新抛出
throw;
}
catch (Exception e)
{
Log.Error($"{currentLine}\n {e}");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"解析字段时出错: {currentLine}\n错误: {e.Message}");
Console.ResetColor();
throw;
}
}
@@ -823,10 +1029,9 @@ public sealed class ProtocolExporter
{
roamingTypeFileSb.AppendLine($"\t\t\t\tyield return {roamingType};");
}
roamingTypeFileSb.AppendLine("\t\t\t}\n\t\t}");
}
roamingTypeFileSb.AppendLine("\t}\n}");
var file = roamingTypeFileSb.ToString();
@@ -841,7 +1046,7 @@ public sealed class ProtocolExporter
await File.WriteAllTextAsync($"{_networkProtocolClientDirectory}RoamingType.cs", file);
}
}
private async Task RouteType()
{
var routeTypeFile = $"{_networkProtocolDirectory}RouteType.Config";
@@ -855,7 +1060,6 @@ public sealed class ProtocolExporter
{
protoFileText = await File.ReadAllTextAsync(routeTypeFile);
}
var routeTypeFileSb = new StringBuilder();
routeTypeFileSb.AppendLine("namespace Fantasy\n{");
routeTypeFileSb.AppendLine("\t// Route协议定义(需要定义1000以上、因为1000以内的框架预留)\t");
@@ -897,16 +1101,10 @@ public sealed class ProtocolExporter
await File.WriteAllTextAsync($"{_networkProtocolClientDirectory}RouteType.cs", file);
}
}
private void LoadTemplate()
{
// 获取当前运行根目录(即应用程序的工作目录)
string currentDirectory = Directory.GetCurrentDirectory();
string filePath = Path.Combine(currentDirectory, "Template.txt");
// 读取文件所有行
string[] lines = File.ReadAllLines(filePath);
string[] lines = NetworkProtocolTemplate.Template.Split(["\r\n", "\n"], StringSplitOptions.None);
StringBuilder serverSb = new StringBuilder();
StringBuilder clientSb = new StringBuilder();
@@ -921,14 +1119,12 @@ public sealed class ProtocolExporter
flag = 1;
continue;
}
if (trim.StartsWith("#else"))
else if (trim.StartsWith("#else"))
{
flag = 2;
continue;
}
if (trim.StartsWith($"#endif"))
else if (trim.StartsWith($"#endif"))
{
flag = 0;
continue;
@@ -958,4 +1154,109 @@ public sealed class ProtocolExporter
_serverTemplate = serverSb.Replace("(NetworkProtocol)", "ProtoBuf").ToString();
_clientTemplate = clientSb.Replace("(NetworkProtocol)", "ProtoBuf").ToString();
}
private string GenerateNetworkProtocolHelperFile(List<MessageHelperInfo> helperInfos)
{
var helperSb = new StringBuilder();
// 添加文件头和命名空间
helperSb.AppendLine("using System.Runtime.CompilerServices;");
helperSb.AppendLine("using Fantasy;");
helperSb.AppendLine("using Fantasy.Async;");
helperSb.AppendLine("using Fantasy.Network;");
helperSb.AppendLine("using System.Collections.Generic;");
helperSb.AppendLine("#pragma warning disable CS8618");
helperSb.AppendLine();
helperSb.AppendLine("namespace Fantasy");
helperSb.AppendLine("{");
helperSb.AppendLine("\tpublic static class NetworkProtocolHelper");
helperSb.AppendLine("\t{");
foreach (var info in helperInfos)
{
if (info.MessageType == "IMessage")
{
// 版本1: 生成接受消息对象的 Send 方法
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static void {info.MessageName}(this Session session, {info.MessageName} message)");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine("\t\t\tsession.Send(message);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
// 版本2: 生成接受属性参数的 Send 方法
if (info.Fields.Count > 0)
{
var parameters = string.Join(", ", info.Fields.Select(f => $"{f.FieldType} {char.ToLower(f.FieldName[0])}{f.FieldName.Substring(1)}"));
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static void {info.MessageName}(this Session session, {parameters})");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine($"\t\t\tusing var message = Fantasy.{info.MessageName}.Create(session.Scene);");
foreach (var field in info.Fields)
{
var paramName = $"{char.ToLower(field.FieldName[0])}{field.FieldName.Substring(1)}";
helperSb.AppendLine($"\t\t\tmessage.{field.FieldName} = {paramName};");
}
helperSb.AppendLine("\t\t\tsession.Send(message);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
}
else
{
// 没有字段的消息,生成无参数版本
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static void {info.MessageName}(this Session session)");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine($"\t\t\tusing var message = Fantasy.{info.MessageName}.Create(session.Scene);");
helperSb.AppendLine("\t\t\tsession.Send(message);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
}
}
else if (info.MessageType == "IRequest")
{
// 版本1: 生成接受请求对象的 Call 方法
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static async FTask<{info.ResponseType}> {info.MessageName}(this Session session, {info.MessageName} request)");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine($"\t\t\treturn ({info.ResponseType})await session.Call(request);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
// 版本2: 生成接受属性参数的 Call 方法
if (info.Fields.Count > 0)
{
var parameters = string.Join(", ", info.Fields.Select(f => $"{f.FieldType} {char.ToLower(f.FieldName[0])}{f.FieldName.Substring(1)}"));
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static async FTask<{info.ResponseType}> {info.MessageName}(this Session session, {parameters})");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine($"\t\t\tusing var request = Fantasy.{info.MessageName}.Create(session.Scene);");
foreach (var field in info.Fields)
{
var paramName = $"{char.ToLower(field.FieldName[0])}{field.FieldName.Substring(1)}";
helperSb.AppendLine($"\t\t\trequest.{field.FieldName} = {paramName};");
}
helperSb.AppendLine($"\t\t\treturn ({info.ResponseType})await session.Call(request);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
}
else
{
// 没有字段的请求,生成无参数版本
helperSb.AppendLine($"\t\t[MethodImpl(MethodImplOptions.AggressiveInlining)]");
helperSb.AppendLine($"\t\tpublic static async FTask<{info.ResponseType}> {info.MessageName}(this Session session)");
helperSb.AppendLine("\t\t{");
helperSb.AppendLine($"\t\t\tusing var request = Fantasy.{info.MessageName}.Create(session.Scene);");
helperSb.AppendLine($"\t\t\treturn ({info.ResponseType})await session.Call(request);");
helperSb.AppendLine("\t\t}");
helperSb.AppendLine();
}
}
}
helperSb.AppendLine("\t}");
helperSb.AppendLine("}");
return helperSb.ToString();
}
}

View File

@@ -1,15 +1,15 @@
{
"Export": {
"NetworkProtocolDirectory": {
"Value": "../../../Config/NetworkProtocol/",
"Value": "../../../Examples/Config/NetworkProtocol/",
"Comment": "ProtoBuf文件所在的文件夹位置"
},
"NetworkProtocolServerDirectory": {
"Value": "../../../Entity/Generate/NetworkProtocol/",
"Value": "../../../Examples/Server/Entity/Generate/NetworkProtocol/",
"Comment": "ProtoBuf生成到服务端的文件夹位置"
},
"NetworkProtocolClientDirectory": {
"Value": "../../../../Fishing2/Assets/Scripts/Generate/NetworkProtocol/",
"Value": "../../../Examples/Client/Unity/Assets/Scripts/Hotfix/Generate/NetworkProtocol/",
"Comment": "ProtoBuf生成到客户端的文件夹位置"
},
"Serializes": {

View File

@@ -32,16 +32,16 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\FileHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\FileHelper.cs">
<Link>Core\FileHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\HashCodeHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\HashCodeHelper.cs">
<Link>Core\HashCodeHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\JsonHelper.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Helper\JsonHelper.cs">
<Link>Core\JsonHelper.cs</Link>
</Compile>
<Compile Include="..\..\..\Fantasy\Fantasy.Net\Fantasy.Net\Runtime\Core\Network\Message\PacketParser\OpCode.cs">
<Compile Include="..\..\..\Fantasy.Net\Fantasy.Net\Runtime\Core\Network\Message\PacketParser\OpCode.cs">
<Link>ProtocalExporter\OpCode.cs</Link>
</Compile>
<Compile Update="ExporterSettingsHelper.cs">
@@ -53,15 +53,15 @@
<None Update="ExporterSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="NetworkProtocolTemplate.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Run.bat">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Run.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Template.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,48 @@
# NetworkProtocolTemplate.txt 使用说明
## 概述
这个模板文件用于生成网络协议的 C# 代码文件。您可以根据需要自定义模板内容。
## 占位符说明
模板中包含以下占位符,会在代码生成时被替换:
### `(UsingNamespace)`
- 用于插入自定义命名空间的 using 语句
- 例如:当使用自定义序列化器时,会在此处插入相关的 using 语句
### `(Content)`
- 用于插入生成的协议类代码
- 所有 message 定义都会被转换为 C# 类并插入到此处
## 模板结构
### 服务端部分(#if SERVER
包含服务端特有的引用和配置:
- `using MongoDB.Bson.Serialization.Attributes` - 用于 MongoDB 持久化
- 额外的编译器指令和 ReSharper 配置
### 客户端部分(#else
包含客户端需要的基本引用:
- 较少的 using 语句
- 简化的警告抑制
## 自定义方法
如果需要修改生成的代码格式,可以:
1. 修改 using 语句部分
2. 添加或删除编译器指令
3. 修改命名空间结构
4. 添加全局特性或注释
## 注意事项
⚠️ **不要删除占位符**`(UsingNamespace)``(Content)` 是必需的,删除会导致代码生成失败
⚠️ **保持条件编译**`#if SERVER` / `#else` / `#endif` 结构用于区分服务端和客户端代码
⚠️ **编码格式**:文件应使用 UTF-8 编码
## 文件位置
编译后,模板文件会被复制到输出目录,确保与可执行文件在同一位置。

View File

@@ -1,6 +1,7 @@
#if SERVER
using ProtoBuf;
(UsingNamespace)
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -17,18 +18,18 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
#else
using ProtoBuf;
using System;
(UsingNamespace)
using System.Collections.Generic;
using Fantasy;
using NBC;
using NBC.Network.Interface;
using NBC.Serialize;
using Fantasy.Network.Interface;
using Fantasy.Serialize;
#pragma warning disable CS8618
namespace NBC
namespace Fantasy
{
#endif
(Content)}
(Content)}

View File

@@ -16,9 +16,15 @@ try
// 运行导出协议的代码
new ProtocolExporter().Run();
}
catch (Fantasy.Tools.ProtocalExporter.ProtocolFormatException)
{
// 协议格式错误已经打印过详细信息,这里不再重复打印
}
catch (Exception e)
{
Log.Error(e);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"程序执行出错: {e.Message}");
Console.ResetColor();
}
finally
{

View File

@@ -10,7 +10,6 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALicenseContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F5127915f4e764e0285562916e9114ab3411610_003F47_003Fcbc79ab6_003FLicenseContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryPackableAttribute_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F958e054c967b40b3a108913205d36c4933400_003Fcd_003Fa95eada9_003FMemoryPackableAttribute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemoryPackFormatter_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F958e054c967b40b3a108913205d36c4933400_003F7e_003F8f320c8b_003FMemoryPackFormatter_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParserResultExtensions_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc453ace1e4574cfe83f15ca8c8f735bf37000_003F04_003Fdce32804_003FParserResultExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParser_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc453ace1e4574cfe83f15ca8c8f735bf37000_003F47_003F09aa3821_003FParser_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AProtoReader_002EState_002EReadMethods_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F37c1347d9c665a9c38de996b994b62177561c4c4515e4d49fdf93cd848fe49b_003FProtoReader_002EState_002EReadMethods_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARunResults_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fe923221d2ee660ba5d21ad70b29da65ed3a69653ab6311336652dc91242cb_003FRunResults_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>