python编程示例系列
python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位
C#视觉应用开发问题系列
c#串口应用开发问题系列
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
OpenRA开源红色警戒游戏RPG源码解读
# OpenRA网络通信代码解析
以下是对代码的详细中文注释、逻辑解读、流程图及特殊语法分析:
using System;
using System.Collections.Generic;
using System.IO;
namespace OpenRA.Network
{
/// <summary>
/// 订单数据包类,用于网络游戏中客户端和服务器之间传输游戏指令
/// </summary>
public class OrderPacket
{
// 存储序列化后的订单数据
readonly MemoryStream data;
/// <summary>
/// 从订单集合创建订单数据包
/// </summary>
/// <param name="orders">订单集合</param>
public OrderPacket(IEnumerable<Order> orders)
{
// 订单可能引用在解析时已不存在的角色/实体
// 为确保本地和远程客户端行为一致,最简单的方法是
// 始终序列化/反序列化订单,而不是在本地客户端直接存储Order对象
data = new MemoryStream();
foreach (var o in orders)
data.Write(o.Serialize()); // 将每个订单序列化并写入内存流
}
/// <summary>
/// 从已有的内存流创建订单数据包
/// </summary>
/// <param name="data">包含序列化订单的内存流</param>
public OrderPacket(MemoryStream data)
{
this.data = data;
}
/// <summary>
/// 获取数据包中的所有订单
/// </summary>
/// <param name="world">游戏世界对象,用于反序列化订单</param>
/// <returns>订单集合</returns>
public IEnumerable<Order> GetOrders(World world)
{
if (data.Length == 0)
yield break; // 如果没有数据,直接返回
// 订单反序列化依赖于当前世界状态
// 因此必须延迟到准备好消费它们时才进行
data.Position = 0; // 重置流位置
var reader = new BinaryReader(data);
while (data.Position < data.Length)
{
var o = Order.Deserialize(world, reader); // 反序列化订单
if (o != null)
yield return o; // 返回有效订单
}
}
/// <summary>
/// 将订单数据包序列化为字节数组,并添加帧号
/// </summary>
/// <param name="frame">帧号</param>
/// <returns>序列化后的字节数组</returns>
public byte[] Serialize(int frame)
{
var ms = new MemoryStream((int)data.Length + 4); // 为帧号预留4字节空间
ms.Write(frame); // 写入帧号
data.Position = 0; // 重置流位置
data.CopyTo(ms); // 复制订单数据
return ms.GetBuffer(); // 获取底层缓冲区
}
/// <summary>
/// 合并多个订单数据包
/// </summary>
/// <param name="packets">要合并的数据包集合</param>
/// <returns>合并后的订单数据包</returns>
public static OrderPacket Combine(IEnumerable<OrderPacket> packets)
{
var ms = new MemoryStream();
foreach (var packet in packets)
{
packet.data.Position = 0; // 重置流位置
packet.data.CopyTo(ms); // 复制数据到新流
}
return new OrderPacket(ms); // 使用合并的流创建新数据包
}
}
/// <summary>
/// 订单输入输出静态类,处理各种网络消息的序列化和反序列化
/// </summary>
public static class OrderIO
{
// 空订单数据包,用于优化性能
static readonly OrderPacket NoOrders = new(Array.Empty<Order>());
/// <summary>
/// 序列化同步数据
/// </summary>
/// <param name="data">包含帧号、同步哈希和失败状态的元组</param>
/// <returns>序列化后的字节数组</returns>
public static byte[] SerializeSync((int Frame, int SyncHash, ulong DefeatState) data)
{
var ms = new MemoryStream(4 + Order.SyncHashOrderLength);
ms.Write(data.Frame); // 写入帧号
ms.WriteByte((byte)OrderType.SyncHash); // 写入订单类型
ms.Write(data.SyncHash); // 写入同步哈希
ms.Write(data.DefeatState); // 写入失败状态
return ms.GetBuffer();
}
/// <summary>
/// 序列化Ping响应
/// </summary>
/// <param name="timestamp">时间戳</param>
/// <param name="queueLength">队列长度</param>
/// <returns>序列化后的字节数组</returns>
public static byte[] SerializePingResponse(long timestamp, byte queueLength)
{
var ms = new MemoryStream(14);
ms.Write(0); // 帧号为0
ms.WriteByte((byte)OrderType.Ping); // 写入订单类型
ms.Write(timestamp); // 写入时间戳
ms.WriteByte(queueLength); // 写入队列长度
return ms.GetBuffer();
}
/// <summary>
/// 尝试解析断开连接数据包
/// </summary>
/// <param name="packet">包含来源客户端ID和数据的元组</param>
/// <param name="disconnect">输出包含帧号和客户端ID的元组</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParseDisconnect((int FromClient, byte[] Data) packet, out (int Frame, int ClientId) disconnect)
{
// 有效的断开连接数据包只能由服务器生成
if (packet.FromClient != 0 || packet.Data.Length != Order.DisconnectOrderLength + 4 || packet.Data[4] != (byte)OrderType.Disconnect)
{
disconnect = (0, 0);
return false;
}
var frame = BitConverter.ToInt32(packet.Data, 0); // 解析帧号
var clientId = BitConverter.ToInt32(packet.Data, 5); // 解析客户端ID
disconnect = (frame, clientId);
return true;
}
/// <summary>
/// 尝试解析同步数据包
/// </summary>
/// <param name="packet">数据包字节数组</param>
/// <param name="data">输出包含帧号、同步哈希和失败状态的元组</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParseSync(byte[] packet, out (int Frame, int SyncHash, ulong DefeatState) data)
{
if (packet.Length != 4 + Order.SyncHashOrderLength || packet[4] != (byte)OrderType.SyncHash)
{
data = (0, 0, 0);
return false;
}
var frame = BitConverter.ToInt32(packet, 0); // 解析帧号
var syncHash = BitConverter.ToInt32(packet, 5); // 解析同步哈希
var defeatState = BitConverter.ToUInt64(packet, 9); // 解析失败状态
data = (frame, syncHash, defeatState);
return true;
}
/// <summary>
/// 尝试解析Tick缩放数据包
/// </summary>
/// <param name="packet">包含来源客户端ID和数据的元组</param>
/// <param name="scale">输出缩放值</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParseTickScale((int FromClient, byte[] Data) packet, out float scale)
{
// 有效的tick缩放命令只能由服务器生成
if (packet.FromClient != 0 || packet.Data.Length != 9 || packet.Data[4] != (byte)OrderType.TickScale)
{
scale = 1;
return false;
}
// 有效的tick缩放数据包帧号始终为0
var frame = BitConverter.ToInt32(packet.Data, 0);
if (frame != 0)
{
scale = 1;
return false;
}
scale = BitConverter.ToSingle(packet.Data, 5); // 解析缩放值
return true;
}
/// <summary>
/// 尝试解析Ping请求数据包
/// </summary>
/// <param name="packet">包含来源客户端ID和数据的元组</param>
/// <param name="timestamp">输出时间戳</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParsePingRequest((int FromClient, byte[] Data) packet, out long timestamp)
{
// 有效的Ping请求只能由服务器生成
if (packet.FromClient != 0 || packet.Data.Length != 13 || packet.Data[4] != (byte)OrderType.Ping)
{
timestamp = 0;
return false;
}
// 有效的Ping数据包帧号始终为0
var frame = BitConverter.ToInt32(packet.Data, 0);
if (frame != 0)
{
timestamp = 0;
return false;
}
timestamp = BitConverter.ToInt64(packet.Data, 5); // 解析时间戳
return true;
}
/// <summary>
/// 尝试解析确认数据包
/// </summary>
/// <param name="packet">包含来源客户端ID和数据的元组</param>
/// <param name="frame">输出帧号</param>
/// <param name="count">输出计数</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParseAck((int FromClient, byte[] Data) packet, out int frame, out byte count)
{
// 确认数据包只接受来自服务器的
if (packet.FromClient != 0 || packet.Data.Length != 6 || packet.Data[4] != (byte)OrderType.Ack)
{
frame = count = 0;
return false;
}
frame = BitConverter.ToInt32(packet.Data, 0); // 解析帧号
count = packet.Data[5]; // 解析计数
return true;
}
/// <summary>
/// 尝试解析订单数据包
/// </summary>
/// <param name="packet">数据包字节数组</param>
/// <param name="data">输出包含帧号和订单数据包的元组</param>
/// <returns>如果解析成功则返回true,否则返回false</returns>
public static bool TryParseOrderPacket(byte[] packet, out (int Frame, OrderPacket Orders) data)
{
// 不是有效的数据包
if (packet.Length < 4)
{
data = (0, null);
return false;
}
// 错误的数据包类型
if (packet.Length >= 5 && (packet[4] == (byte)OrderType.Disconnect || packet[4] == (byte)OrderType.SyncHash))
{
data = (0, null);
return false;
}
var frame = BitConverter.ToInt32(packet, 0); // 解析帧号
// 性能优化:跳过空订单帧,通常每个客户端每帧都会有
var orders = packet.Length > 4 ? new OrderPacket(new MemoryStream(packet, 4, packet.Length - 4)) : NoOrders;
data = (frame, orders);
return true;
}
}
}
代码逻辑解读
这段代码是一个游戏网络通信框架的一部分,主要负责处理游戏中的命令(Order)在网络上的传输。代码分为两个主要部分:
OrderPacket类:负责订单(命令)数据的序列化和反序列化
将游戏命令集合序列化为二进制数据
从二进制数据反序列化出游戏命令
合并多个命令数据包
OrderIO静态类:提供各种网络消息的序列化和解析功能
处理同步消息
处理ping请求和响应
处理断开连接消息
处理tick速率缩放消息
处理确认消息
解析普通订单数据包
整体逻辑是为了确保在网络游戏中,客户端和服务器之间能够可靠地传输游戏命令,并保持游戏状态的同步。
流程图
特殊语法与技巧解读
yield return语法
public IEnumerable<Order> GetOrders(World world)
{
if (data.Length == 0)
yield break;
// ...
while (data.Position < data.Length)
{
var o = Order.Deserialize(world, reader);
if (o != null)
yield return o;
}
}
这是C#的迭代器模式实现,允许延迟执行和惰性求值
yield return逐个返回元素而不是一次性构建完整集合
yield break提前终止迭代器
好处是内存效率高,只在需要时才处理数据
元组语法
public static bool TryParseDisconnect((int FromClient, byte[] Data) packet, out (int Frame, int ClientId) disconnect)
C# 7.0引入的元组特性,允许方法返回或接收多个值而无需创建自定义类
命名元组(FromClient, Data)提高了代码可读性
out参数与元组结合使用,简化了返回多个值的方式
模式匹配与解构
public static byte[] SerializeSync((int Frame, int SyncHash, ulong DefeatState) data)
参数直接解构元组,使代码更简洁
访问元组成员时使用data.Frame这样的语法
静态只读字段优化
static readonly OrderPacket NoOrders = new(Array.Empty<Order>());
使用静态只读字段避免重复创建相同对象
Array.Empty<T>()返回空数组的单例实例,比new T[0]更高效
Try模式方法
public static bool TryParseSync(byte[] packet, out (int Frame, int SyncHash, ulong DefeatState) data)
遵循.NET的TryParse模式,避免异常处理开销
返回布尔值表示操作是否成功,通过out参数返回结果
在验证失败时设置默认值并返回false
内存流管理
var ms = new MemoryStream((int)data.Length + 4);
// ...
return ms.GetBuffer();
预分配内存流大小提高性能
使用GetBuffer()而不是ToArray()避免额外的内存复制
重置流位置(data.Position = 0)以便重用
条件短路逻辑
if (packet.FromClient != 0 || packet.Data.Length != 6 || packet.Data[4] != (byte)OrderType.Ack)
使用逻辑短路(短路求值)进行高效验证
一旦发现任何条件不满足,立即停止后续条件检查
二进制数据处理
var frame = BitConverter.ToInt32(packet.Data, 0);
使用BitConverter类在字节数组和基本数据类型之间转换
处理网络通信中的二进制协议
性能优化
// PERF: Skip empty order frames, often per client each frame
var orders = packet.Length > 4 ? new OrderPacket(new MemoryStream(packet, 4, packet.Length - 4)) : NoOrders;
针对常见情况进行特殊优化
使用条件运算符避免不必要的对象创建
使用预定义的空对象模式减少内存分配
内存片段重用
new MemoryStream(packet, 4, packet.Length - 4)
直接使用现有字节数组的一部分创建MemoryStream
避免不必要的内存复制,提高性能
这段代码展示了一个高效的网络通信框架,特别适合实时游戏中的命令传输。它通过精心设计的二进制协议、内存管理优化和现代C#语言特性,实现了高效的网络通信。
智能农业设备软件工程师如何确保设备的高可靠性和可用性
python的Plotly库如何使用
c#自定义异常
C#进行串口应用开发如何优化串口通信的实时性能
使用Python使不稳定的API变得可靠
python如何检测一篇文章是不是由chatGPT生成的
python进行局部线性嵌入(LLE)LocallyLinearEmbedding
C#进行串口应用开发如何实现串口数据的校验
python用于创建和管理 IoT 物联网设备的工作流程库aiobotocore_iotthingsgraph
microPython的源码解析之 unicode.c
python的生成器和迭代器
智能农业设备软件工程师如何处理设备的通信协议栈开发
量化交易系统中+如何利用市场深度和流动性信息来优化交易?
数据降维技术和算法
Python的opencv库使用SIFT 进行特征检测
python的任务调度库 Advanced Python Scheduler (APScheduler)
python web应用开发神器 入门二十一
量化交易策略 行业板块选择
microPython的源码解析之 sequence.c
microPython的源码解析之 objint_mpz.c
microPython的源码解析之 builtinevex.c
python如何简单实现重试逻辑
python的ftplib库如何使用
c#视觉应用开发中如何在C#中进行图像去色散?
python的装饰器模式
Python开源的字体解析库FreeType
microPython的源码解析之 objobject.c
如何应聘数据处理专员,年薪大致在78000元到156000元之间
如何反汇编和分析Python字节码,了解代码的执行过程
python的sympy库介绍
NI-Motion 如何使用圆弧插补功能来移动控制器上的轴,C语言示例代码
量化交易系统中+如何处理网络的稳定性和可靠性?
研究人员发现了一种影响支持推测执行的现代CPU架构的新数据泄露攻击。
openai的 ada,Babbage,Curie,Davinci模型分别介绍一下
详细解读一下c++模版编程,并举例
python的unittest库如何使用功能
microPython的源码解析之 modcmath.c
Python如何编写一个钢琴弹奏软件,操作MIDI设备的详细流程
python如何操作word文档
python web应用开发神器 入门一
在 C++ 和 Qt 中如何利用GPU加速计算
车载系统软件工程师如何实现车载系统的车内空气质量监控
Python的高性能web框架库Tornado
microPython的源码解析之 scheduler.c
智能农业设备软件工程师如何集成和管理农业设备的网络安全措施
智能农业设备软件工程师如何集成和管理农业设备的能源管理系统
车载系统软件工程师如何实现车载系统的多用户支持和管理
windows的PC如何进行分布式AI计算
NI-Motion如何使用模拟电压反馈来控制运动控制器的速度的C语言示例代码
microPython的源码解析之 objmodule.c
收费10万美元的10行代码解读
c#视觉应用开发中如何在C#中进行图像批处理?
车载系统软件工程师如何确保车载系统的法规和标准符合性
C#进行串口应用开发如何快速验证和调试串口通信的程序
python如何操作git库
智能农业设备软件工程师如何实现传感器数据的校准和验证
量化交易系统中+如何实现组合的动态调整和再平衡?
智能农业设备软件工程师如何实现农业设备的用户权限管理
microPython的源码解析之 gc.c
智能农业设备软件工程师如何集成和管理农业设备的远程更新系统
openai的plaNet 如何使用,请给出示例代码,并解读
量化交易系统中如何处理大数据量的存储问题?
未来十年国产替代是程序猿的黄金赛道
python的xmlrpc库如何使用
量化交易系统中如何处理回测中的数据一致性问题?
量化交易系统中+如何设计高效的数据库架构?
自动化工具软件详细列举
C#进行串口应用开发如何实现串口通信的断线重连
车载系统软件工程师如何处理车载系统的车辆状态监控和报告
车载系统软件工程师如何实现车载系统的远程锁定和解锁
如何使用Python开发一个度假租赁市场平台。
量化交易系统中+如何实现智能订单路由(SOR)?
量子计算Bernstein-Vazirani算法
智能农业设备软件工程师如何实现农业设备的动态配置和更新
车载系统软件工程师如何实现车载系统的实时交通管理
车载系统软件工程师如何处理车载系统的用户身份验证和授权
C#进行串口应用开发如何实现串口通信性能和稳定性的优化
智能农业设备软件工程师如何实现农业数据的云存储和备份
openai和alphago什么关系
microPython的源码解析之 parsenumbase.c
智能农业设备软件工程师如何处理和分析农作物病虫害数据
用莎士比亚风格写程序
量化对冲交易系统设计二
c#视觉应用开发中如何在C#中进行图像修复?
【无标题】
NI-Motion运动控制混合直线移动和圆弧移动c语言示例
NI-Motion如何在一个运动控制器上创建并运行一个简单的板载程序的C语言示例代码
python标准库列表
c#视觉应用开发中如何在C#中使用K-means算法进行图像聚类?
量化交易系统中+如何优化算法的执行速度?
C#进行串口应用开发如何管理同时打开多个串口的通信
C#进行串口应用开发如何修改Windows下串口参数的默认配置
jupyter项目深度理解一
量化交易系统中如何处理分布式计算中的数据同步问题?
C#进行串口应用开发如何实现串口通信的安全访问与权限控制
车载系统软件工程师如何集成车载系统与第三方服务(如音乐、天气)
开源的仿红色警戒OpenRA经典RPG游戏, 源码解读game.cs
详细介绍一下红黑树,如何快速搜索
python的内置函数
如何加速计算

















暂无评论内容