Repository navigation
API
本页按 Krypt04Mcg 0.29.0 的公开 API 文档核对。0.28/0.29 的聊天 HUD 与大分片修改未更改此流式 API 的 wire format。公开入口位于
dev.krypt04mcg.api。本页描述的是 0.29.0 raw encrypted channel API,而不是 0.18/0.19 的旧 Reliable Data API。
Important
Breaking change: DataTransfer、TransferResult、DATA/ACK/NACK envelope、completion receipt、应用层 retransmission,以及旧 krypt04mcg:data / krypt04mcg:file_share payload 已经移除。旧 Wiki 中关于 DELIVERED / TIMEOUT / BACKPRESSURE 的说明不再适用于当前代码。
公开数据 / stream API 要求:
- 双方客户端安装兼容版本 Krypt04Mcg;
- 双方都已经导入对方当前公钥,且没有把对方标记为
DISTRUSTED; - 普通 Mod API 设置
enableDataApi=true; - 服务器 Relay 正确声明并转发
krypt04mcg_stream:control和双方共同支持的 raw data slots; -
connect(...)、打开 stream、listener/control 生命周期调用发生在 Minecraft client thread。
文件分享是特殊内部 channel:它使用同一套 raw stream transport,但是否允许由 enableFileSending / enableFileReceiving 控制,不要求 enableDataApi=true。文件分享命令本身仍要求 chatSendMode=CUSTOM_PAYLOAD。
apiChannelCount 默认 16,范围 1..256。客户端启动时预注册:
krypt04mcg_stream:control
krypt04mcg_stream:data/0
krypt04mcg_stream:data/1
...
krypt04mcg_stream:data/(n-1)
修改 apiChannelCount 后必须重启,因为 Minecraft payload type 在初始化时注册。
ControlPayload.Kind 当前有:
EXCHANGE
OPEN
ASSIGNED
READY
END
RESET
ABORT
control payload 包含:
kind
peer
stream UUID
slot
application channel
sessionId
sequence
body <= 30000 bytes
EXCHANGE 的 body 是现有 SESSION_EXCHANGE 加密包;其余客户端控制消息使用从 API Session 派生的 HMAC-SHA256 tag。ASSIGNED 和 ABORT 是 Relay 生命周期通知。
每个 raw data payload 只有:
XChaCha20-Poly1305 ciphertext || 16-byte tag
明确 没有:
- sender / receiver
- application channel
- stream UUID
- 显式 sequence
- 显式 nonce
- inner length
- Base64 / JSON
- fragment ID
- per-record ACK / NACK
Minecraft Custom Payload 本身提供有序、可靠传输和 record boundary;Krypt04Mcg 只把该 boundary 当 AEAD record boundary。
每条 record 最多 16 KiB 明文。nonce 为 24 字节,由 stream UUID 与方向本地隐式 record counter 构造;counter 不在 data channel 上传输。
import dev.krypt04mcg.api.Krypt04McgApi;
import dev.krypt04mcg.api.KryptSocket;
Krypt04McgApi.registerSocketReceiver("example:stream", socket -> {
// listener 在 Minecraft client thread 执行。
// read() 可能阻塞,因此把读取交给你自己的后台 executor。
executor.execute(() -> {
try {
byte[] bytes = socket.getInputStream().readAllBytes();
// 这里得到认证后的完整字节流;再按你的应用协议解析。
} catch (java.io.IOException failure) {
// reset / auth failure / truncation / timeout / disconnect 等。
}
});
// 本端没有数据要回写:半关闭输出;输入仍可继续读到认证 EOF。
socket.close();
});
KryptSocket socket = Krypt04McgApi.connect("Bob", "example:stream");
// 发送方应跨 tick 保存 socket + bytes + offset,并根据容量推进。
int count = Math.min(socket.writableBytes(), bytes.length - offset);
if (count > 0) {
socket.getOutputStream().write(bytes, offset, count);
offset += count;
}
if (offset == bytes.length) {
socket.close(); // queued bytes 发完后再发送认证 END
}KryptSession session = Krypt04McgApi.connect("Bob");connect(player) 只返回本地 handle;它不会暴露 session secret。
session.peer();
session.isReady();
session.sessionId();
session.ready();
session.send("example:channel", bytes);
session.close();语义:
-
ready():等待本地 API Session 交换完成。 -
isReady():当前 handle 是否仍有效且 exchange 已成功。 -
sessionId():仅 ready 时返回当前 ID,否则null。 -
send(...):convenience one-shot stream;即使 Session 尚未 ready,也可先排入 bounded channel pool。 -
close():只关闭这个本地 API connection handle 并取消它的 queued sends;不会清除聊天 Session。
KryptSocket socket = Krypt04McgApi.connect("Bob", "example:stream");application channel 必须匹配:
[A-Za-z0-9_.:-]{1,256}Krypt04McgApi.registerSocketReceiver("example:stream", socket -> {
// client-thread callback
});
Krypt04McgApi.unregisterSocketReceiver("example:stream");同一个 channel 如果同时存在 socket receiver 和 byte receiver,socket receiver 优先。
Krypt04McgApi.send("Bob", "example:event", bytes);
Krypt04McgApi.send("example:event", bytes); // peer 来自 apiReceiver当前 send(...):
- 返回
void; - 内部打开一个 stream、写入 bytes、然后 half-close output;
- 单次 bytes 不能超过
KryptSocket.MAX_BUFFERED_BYTES = 1 MiB;更大的数据应自己使用KryptSocket分批写; - 没有 delivery receipt,也没有“对方持久化成功”的语义。
Krypt04McgApi.registerReceiver("example:event", (sender, bytes) -> {
// client-thread callback
});也有单参数 overload:
registerReceiver(String channel, Consumer<byte[]> receiver)
registerReceiver(String channel, BiConsumer<String, byte[]> receiver)
unregisterReceiver(String channel)Warning
当前 byte receiver 收到的是 stream portion,不是“完整的一条应用消息”。底层每次解密一个 raw record 就可能调用一次 callback。不要假设一次 send(bytes) 必然对应一次 receiver callback。需要消息边界、长度、类型、请求/响应或事务语义时,请使用 KryptSocket 并定义自己的 framing。
公开属性:
peer()
channel()
streamId()
getInputStream()
getOutputStream()
isClosed()
isFailed()
outputEnded()
writableBytes()
close()关键规则:
-
InputStream.read(...)可能阻塞,绝不能在 Minecraft client thread 上等待。 -
OutputStream.write(...)只是把复制后的字节放入本地 bounded queue;本身不等待网络。 - 每个方向的 buffer 上限都是 1 MiB。
- 写入长度如果超过当前剩余容量,会抛
IOException("Stream backpressure"),不会部分写入。 -
writableBytes()只是瞬时容量查询,不会“预留”空间;多个 producer 必须自己串行化“检查 + 写入”。 -
close()是 half-close output:等已排队字节发完,再发送认证END;input 仍保持可读。 -
InputStream.close()会让整个 stream fail / reset,而不是普通 half-close input。 - 成功结束要求双方完成方向性的认证 EOF;掉 record、错误 tag、RESET、ABORT 或 disconnect 都不能伪装成成功 EOF。
当前代码中的主要边界:
| 项目 | 当前值 |
|---|---|
apiChannelCount |
默认 16,范围 1..256
|
| 同时活动 stream | 最多等于 channel pool 大小 |
| API connections | 最多 64
|
每方向 KryptSocket buffer |
1 MiB |
| 单个 raw record 明文 | 16 KiB |
| raw record AEAD tag | 16 bytes |
| 每 client tick 全局发送预算 |
4 个 raw records |
| connection / allocation / stream idle timeout | 60 s |
| control body | 最多 30000 bytes
|
convenience send
|
最多 1 MiB
|
活动 stream 的调度会轮转,避免固定让第一个 stream 永久占用每 tick 的 4-record 预算。
当前 Loader 初始化时为 Mod API 创建独立:
accounts/<uuid>/stream-api/
并使用独立的 SessionService / SessionHandshakeService。
因此:
- API 可以复用 API 自己已经存在且未过期的 Session;
- 不会直接拿聊天
/k04m exchange的 Session record 当 stream Session; - API exchange 不应轮换聊天 Session;
- 身份仍然复用同一套长期 KEM / 签名公钥与 trust state。
API exchange 使用现有签名 KEM handshake machinery,并固定以 ChaCha20-Poly1305 作为该 handshake 的 envelope AEAD 参数;真正 raw data record 固定使用 XChaCha20-Poly1305,与聊天 aeadAlgorithm 配置无关。
ChannelCrypto 从 API Session secret 通过 Bouncy Castle HKDF-SHA256 派生不同用途的 32-byte keys,并绑定:
- session ID
- stream UUID
- slot
- application channel
- 方向(local → peer / peer → local)
- data / control purpose
raw data 使用 Bouncy Castle XChaCha20Poly1305。stream UUID + 方向本地 record counter 形成 24-byte nonce;AAD 绑定 krypt04mcg_stream/v1、session / stream / slot / application channel context。
这使跨 stream、跨 slot、跨 application channel、跨方向替换或乱序通常表现为认证失败,而不是被当作另一条合法数据。
当前仓库 不包含服务器 Relay 实现。兼容 Relay 至少需要:
- 从真实 Minecraft connection 得到 source player,不能相信客户端声明的 source;
- 只解析
krypt04mcg_stream:control,raw data slot 应原样转发; - OPEN 初始使用
slot=-1,Relay 选择双方都注册的空闲 slot; - 给 opener 发送
ASSIGNED,再把 OPEN 用选定 slot 转发给 peer; - peer 验证 OPEN 后发送认证
READY; - READY 前、非分配双方、错误 slot 的 raw data 必须拒绝;
-
END是认证 EOF,RESET是 peer failure,ABORT是 Relay failure;ABORT 绝不能当作 EOF; - 双向 END、RESET、disconnect 或 timeout 后释放 slot;
- 保持 control / data 转发顺序,并对 allocation / traffic 做有界限制。
文件分享内部注册 socket receiver:
krypt04mcg_file:stream
FileStreamCodec 在加密 byte stream 内编码:
modified-UTF filename
int fileSize
file bytes
EOF
文件最大 10 MiB。文件名和内容都位于加密 stream 内。内置文件接收从接纳到完整数据及认证 EOF 有固定 2 分钟总时限,任何成功收到的字节都不能续期;这不同于通用 KryptSocket 的 60 秒空闲时限。接收端只有在完整 stream + 认证 EOF 成功后才产生用户确认提示;用户接受时再次检查 identity / trust,然后保存到 received-files。文件不会自动打开或执行。
依赖旧 0.18/0.19 API 的第三方 Mod 必须修改代码并重新编译。尤其需要删除或替换:
DataTransfer
TransferResult
DELIVERED / REJECTED / TIMEOUT / BACKPRESSURE
krypt04mcg:data
krypt04mcg:file_share
当前 API 的核心契约是 bounded authenticated byte stream,不是消息队列,也不提供 exactly-once、delivery receipt、数据库事务或远端持久化保证。
0.28.0 的 HUD 只显示聊天分片进度,不对 KryptSocket 字节流提供进度事件。0.29.0 的 256 KiB / 2048 聊天片段限制不会提高单个 Raw API record 的 16 KiB 明文容量、每方向 1 MiB 缓冲或 control body 30,000 bytes 限制;文件和数据流应用仍须自行定义 framing 与接收上限。参见 升级指南。