OpenRA开源红色警戒游戏RPG源码OrderIO.cs解读

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的内置函数
如何加速计算

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容