1 个不稳定版本
0.1.0 | 2021年1月9日 |
---|
#586 在 异步
每月21次下载
70KB
1.5K SLoC
craftio-rs
版本 0.1.0,由 Twister915 编写!
craftio-rs 是一个库,允许您读取和写入在 mcproto-rs 中定义的数据包到真实的Minecraft服务器/客户端。
您可以使用这个库来实现从简单的服务器状态ping客户端、BungeeCord-like代理、加入您最喜欢的服务器的机器人,到整个Minecraft服务器或客户端实现。
协议定义由上面提到的单独的crate管理,称为 mcproto-rs,它定义了一系列特质以支持自定义协议实现,并定义了Minecraft的一些版本的包。
此crate可选实现以下功能
compression
(使用 flate2 crate)encryption
(使用 aes crate)带有快速CFB-8实现futures-io
使您可以读取/写入来自 futures crate 的AsyncRead
/AsyncWrite
特质的实现者tokio-io
使您可以读取/写入来自 tokio crate 的AsyncRead
/AsyncWrite
特质的实现者
用法
[dependencies]
craftio-rs = "0.1"
这个库可以用来连接到服务器或托管客户端连接。它实现了Minecraft协议的所有功能,这些功能可以禁用以简化使用场景(例如,击打服务器以收集状态信息)。
您还可以使用基于异步的I/O实现或阻塞的I/O实现。
连接到服务器
要连接到Minecraft服务器,您可以编写如下内容
let mut conn = CraftTokioConnection::connect_server_tokio("localhost:25565").await?;
conn.write_packet_async(Packet578::Handshake(HandshakeSpec { ... })).await?;
conn.set_state(State::Login);
...
这个 CraftTokioConnection
结构实际上是对更通用的 CraftConnection<R, W>
类型的一个别名,它封装了由 CraftReader
和 CraftWriter
支持的任何 R
(读取器)和 W
(写入器)类型。以下是对这些类型的更多详细说明。
您还可以使用std::net
中的阻塞套接字以这种方式连接:
let mut conn = CraftTcpConnection::connect_server_std("localhost:25565")?;
conn.write_packet(Packet578::Handshake(HandshakeSpec { ... }))?;
conn.set_state(State::Login);
...
服务客户端
您可以使用CraftConnection::from_std_with_state(your_client, PacketDirection::ServerBound, State::Handshaking)
来包装阻塞的TcpStream
,并且可以使用CraftConnection::from_async_with_state((client_read_half, client_write_half), PacketDirection::ServerBound, State::Handshaking)
来包装异步的TcpStream
。在异步的情况下,您必须在传递给CraftConnection
之前将连接分成读取器/写入器两部分。
在所有情况下,都建议首先使用您选择的缓冲读取器实现来包装读取器。这是因为该crate通常将数据包长度(前5个字节)作为一个调用读取,然后作为另一个调用读取整个数据包主体。如果您选择不使用缓冲实现,这两个调用可能会产生不希望的开销,因为这两个调用实际上可能需要操作系统调用。
类型
有两个结构体实现了此crate的行为:CraftReader<R>
和CraftWriter<W>
。
它们被定义为在包装实现craftio_rs::AsyncReadExact
/std::io::Read
和craftio_rs::AsyncWriteExact
/std::io::Write
特征的类型R
/W
时实现CraftAsyncReader
/CraftSyncReader
和CraftAsyncWriter
/CraftSyncWriter
特征。
当启用tokio-io
和futures-io
功能时,此crate为实现tokio::io::AsyncRead
/tokio::io::AsyncWrite
和futures::AsyncRead
/futures::AsyncWrite
特征的实现提供了craftio_rs::AsyncReadExact
和craftio_rs::AsyncWriteExact
的实现。
性能
CraftReader<R>
和CraftWriter<W>
都包含一些缓冲区,这两个缓冲区都是延迟分配的Vec<u8>
。
raw_buf
是数据包字节的缓冲区compress_buf
/decompress_buf
。当启用压缩功能时(无论是作为名为compression
的 crate 功能,还是在调用.set_compression_threshold
并传入一个Some(> 0)
的值后),该缓冲区用于存储压缩后的数据包(对于写入者而言)或解压后的数据包(对于读取者而言)。
这些缓冲区可以通过调用 .ensure_buf_capacity(usize)
和 .ensure_compression_buf_capacity(usize)
来积极分配,但它们目前还不能由用户提供。
动机
当我在进行以下三个项目时设计了该库:BungeeCord 的替代品、一个可以为我加入服务器的机器人客户端以及一个快速ping服务器列表并打印它们状态的工具。这个 crate 尽量避免动态分配,但确实有一些缓冲区来使序列化和反序列化变得快速。这些分配默认是惰性的,但如果需要,也可以积极进行(如下面所述)。
当实现类似于游戏服务器或像 BungeeCord 这样的代理时,在最大情况下,您每秒需要处理数十到数百次的加入操作,因此动态分配不会对性能产生重大影响。因此,缓冲区的惰性分配和增长不会影响您的火焰图。
然而,在尝试ping服务器的情况下,我确实希望确保我们只为每个连接的一半分配一次。为此,您可以积极分配一个足够大的缓冲区,并且还可以通过设置最大数据包大小来限制其进一步增长(调用 .set_max_packet_size
和 .ensure_buf_capacity
)。
一个很棒的功能是允许用户提供一个 &mut Vec<u8>
,该类型可以在连接关闭之前由包装类型使用。这样,在多工作者模型(如ping工具)中,您可以简单地为每个工作者分配一个缓冲区,并在后续连接中重用它。目前尚不存在此功能。
适应不同的 I/O 实现
要添加您最喜欢的 I/O 库,您可以实现 std I/O 特性(std::io::Read
和 std::io::Write
)或者对于异步实现,您可以实现由该 crate 提供的特性(AsyncReadExact
和 AsyncWriteExact
)。
待办事项
- 允许用户为其已分配的
raw_buf
提供缓冲区 - 看看我们是否可以停止自己管理
Vec<u8>
并仅使用已存在的BufReader
特性? - 从
CraftReader
结构中提取偏移量跟踪。
依赖关系
~2–11MB
~128K SLoC