ProxLib

ProxLib

使附近的玩家能够自主相互通信。本质上是一个通过mc服务器发送的CustomPayloadC2CPacket,无需任何集成。

基础库

ProxLib

Enviroment Discord Modrinth

Fabric API Minecraft

ProxLib 是一个库,用于在 Minecraft 中实现玩家之间基于距离的通信。它允许附近的玩家通过服务器直接相互交换数据,而服务器无需安装任何模组或插件来中继这些数据。

概述

ProxLib 为 ProxChat 提供支持,也可被其他模组用于实现基于距离的通信功能。它提供了一个简单的 API,用于在大约 26 格范围内的玩家之间发送和接收数据包。

该库的工作原理是巧妙地将数据编码到 Minecraft 现有的数据包系统中,通过方块破坏操作数据包(仅中止操作)实现,这些数据包会被中继给附近所有玩家,且无需服务器验证(没错,即使在空气中也能工作!)。

工作原理

  • 通信方式:使用 PlayerActionC2SPacket 配合 ABORT_DESTROY_BLOCK 操作
  • 数据编码:数据被编码到玩家周围的相对方块位置中
  • 范围:大约 26 格(技术上受 Minecraft 32 格操作数据包中继限制)
  • 数据容量:每个 Minecraft 数据包 9 比特
    • 发送的数据前会附加 2 个 Minecraft 数据包作为魔数(用于识别为 Prox 数据包),2 字节的 Vendor 和 Packet Id,以及 3 字节的数据包长度。
      • 因此,如果你发送一个包含 4 字节数据的数据包,这将会变成 2 + (2 + 2 + 3 + 4) * (8 / 9) » 11.77 » 12 个 Minecraft 数据包
  • 可靠性:几乎在所有服务器上都能工作,包括那些装有反作弊系统的服务器
    • 请注意,反作弊系统可能会将其视为数据包刷屏或某种奇怪的 nuker 尝试。这可能导致玩家被标记为作弊。
      • 我的测试主要是在 2b2t 上进行的。反作弊系统没有阻止任何数据包,没有将我踢出,但最近的一次更新导致发送数据并移动时会出现回弹(数据仍然正常发送)。
    • 当你距离垂直建筑高度上限和下限小于约 6 格时会停止工作(可能在世界边界附近也会)
    • 即使发送玩家处于创造模式也能工作!

免责声明: 由于此模组会发送大量上述的 ABORT_DESTROY_BLOCK 数据包,反作弊系统可能会将玩家标记为数据包刷屏甚至 nuking(尽管任何 nuker 都不会发送中止操作)。 因此,请确保在玩家启用相应的发送功能之前,让他们知晓可能会发生这种情况。

安装

对于模组用户

ProxLib 是一个库模组,应与其依赖的模组一起安装。

模组可能会自带此库,因此除非你在游戏内模组菜单的 Libraries 中查看,否则你可能永远不会意识到它被包含了。

对于模组开发者

在你的 build.gradle 中将 ProxLib 添加为依赖项:

exclusiveContent {
    forRepository {
        maven {
            name = "Modrinth"
            url = "https://api.modrinth.com/maven"
        }
    }
    filter {
        includeGroup "maven.modrinth"
    }
}

dependencies {
    // 例如 MC 版本 1.21.4(请确保检查最新的版本)。
    // 如果你不希望将此模组直接打包到你的模组中(目前 <30 KiB),请移除 "include"。
    include modApi("maven.modrinth:proxlib:0.2.4+1.21")
}

在你的 fabric.mod.json 中添加:

"depends": {
    "proxlib": ">=0.2.0"
}

构建

如果你希望构建此模组,你应该注意:

  • 此项目使用 Stonecutter 来同时生成多个版本
  • 你不应运行 build 任务,而应运行 chiseledBuild(在 project 分类中)
  • 查看他们的文档以获取更多信息

版本管理

总的来说,我尝试将版本视为 SemVer(主版本号.次版本号.修订号)。

这个库仍处于 1.0.0 之前。所以稳定性可能并不完美。然而,这个库可能最终永远不会达到 1.0.0,而当前版本仍然相当稳定。

特别是对于 ProxLib 类,我会尽力不破坏现有公共方法的兼容性。如果确实破坏了,我会提升次版本号。然而,如果添加了大量新功能,我也可能会提升次版本号。所以这并不一定意味着破坏性变更。

关于更新日志,请查看 GitHub 上的 releases 或 Modrinth 上的 changelog。

协议兼容性

我在 2024 年 1 月 ProxChat 首次发布时就确定了协议,此后未做修改。

我的目标是永远不需要破坏兼容性。我也尝试对我的数据包做到同样的事情。

到目前为止唯一的变更:

  • ID(u16)已被拆分为 10 位的 Vendor ID 和 6 位的 Packet ID。这不是破坏性变更,但改变了 ID 的解释方式。

开发者用法

数据包 ID

数据包由 2 字节的 id 标识,该 id 被拆分为:

  • 10 位:Vendor ID(0 到 1023)
  • 6 位:Packet ID(0 到 63)

因此,如果你想创建自己的数据包,你应该:

  • 选择你自己的 Vendor ID。最好使用随机数生成器生成一个供你的模组使用的 ID。
  • 然后开始为任何数据包编号

示例:

final int VENDOR_ID = 932; // 使用随机数生成器生成
final ProxPacketIdentifier AWESOME_PACKET_ID = ProxPacketIdentifier.of(VENDOR_ID, 0);
final ProxPacketIdentifier ANOTHER_PACKET_ID = ProxPacketIdentifier.of(VENDOR_ID, 1);

发送和接收数据包

你可能需要的所有方法都可以从 ProxLib 类轻松调用。

发送示例

public void sendAwesomePacket(String message) throws IOException {
  // 为你的数据包创建数据(byte[])
  ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
  DataOutputStream dataOut = new DataOutputStream(bytesOut);
  dataOut.writeByte(1); // 类型
  dataOut.writeUTF(message);

  // 发送数据包
  int packets = ProxLib.sendPacket(MinecraftClient.getInstance(), AWESOME_PACKET_ID, bytesOut.toByteArray());
  LOGGER.info("Sent my awesome packet using {} packets!", packets);
}

接收示例

public class MyAwesomeMod implements ClientModInitializer {

  @Override
  public void onInitializeClient() {
    ProxLib.addHandlerFor(AWESOME_PACKET_ID, (sender, identifier, data) -> {
      try {
        DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(data));
        int type = dataIn.readUnsignedByte(1);
        if(type == 1) {
            String message = dataIn.readUTF();
            // 对 message 做一些处理...
        }else {
            LOGGER.warn("Received unsupported type in my awesome packet: {}", type);
        }
      } catch (IOException ex) {
        LOGGER.error("Failed to proccess my awesome packet!", ex);
      }
    });
    ProxLib.addHandlerFor(ANOTHER_PACKET_ID, (sender, identifier, data) -> {
      // ...
    });
  }

}

已知的数据包 ID:

模组 Vendor ID Packet ID 名称 描述 链接
ProxChat 0 1 Chat 聊天中的简单聊天消息(使用模组输入 % <msg>)。 PC-Chat
ProxChat 0 2 PatPat-PatEntity 抚摸某人(旧版) PC-Pat
ProxChat 0 3 EmoteCraft 播放 / 重复 / 停止自己的表情 PC-Emote
ProxChat 0 4 TextDisplay 向他人显示一个或多个可自定义的文本显示(使用模组输入 %% <msg>)。 PC-TextDisplay
PatPat 2 0 Pat 抚摸某人 PP-Pat

注意:ProxChat 的 Vendor ID 为 0,因为当时 ID 尚未拆分为 Vendor/Packet ID(而且此库曾是 ProxChat 的一部分)。因此使用了从 1 开始的编号,这实际上就是 Vendor ID 0,并且保留它是为了向后兼容旧版本的 ProxChat。

与其他语言的集成

ItzN00bPvP 开发了一个与此兼容的基础 Rust 库(crates.io:proxchat,仓库:proxchat-rs)。因此,如果你想让你例如 azalea 机器人发送 ProxChat 消息,这会很方便。

致谢

由 EnderKill98 开发,基础是在与 ItzN00bPvP 一起检查他自己的 N00bBots 日志时偶然发现的。没有他,这一切都不会存在!