#minecraft #async-io #io #io-write #async #future #io-read

craftio-rs

一个用于读取和写入mcproto-rs中定义的数据包到I/O接收器/发送器的crate

1 个不稳定版本

0.1.0 2021年1月9日

#586异步

每月21次下载

Apache-2.0

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> 类型的一个别名,它封装了由 CraftReaderCraftWriter 支持的任何 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::Readcraftio_rs::AsyncWriteExact/std::io::Write特征的类型R/W时实现CraftAsyncReader/CraftSyncReaderCraftAsyncWriter/CraftSyncWriter特征。

当启用tokio-iofutures-io功能时,此crate为实现tokio::io::AsyncRead/tokio::io::AsyncWritefutures::AsyncRead/futures::AsyncWrite特征的实现提供了craftio_rs::AsyncReadExactcraftio_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::Readstd::io::Write)或者对于异步实现,您可以实现由该 crate 提供的特性(AsyncReadExactAsyncWriteExact)。

待办事项

  • 允许用户为其已分配的 raw_buf 提供缓冲区
  • 看看我们是否可以停止自己管理 Vec<u8> 并仅使用已存在的 BufReader 特性?
  • CraftReader 结构中提取偏移量跟踪。

依赖关系

~2–11MB
~128K SLoC