A lightweight cross-behavior-pack communication protocol for Minecraft Bedrock Script API, inspired by UDP's fire-and-forget transmission model.
SEUDP (ScriptEventUserDatagramProtocol) is a method that allows Minecraft scripts to communicate across behavior packs. It features a send-and-go data transmission mode, similar to the UDP protocol.
Unlike traditional approaches that rely on scoreboards, tags, or entity properties, SEUDP leverages Minecraft's scriptevent command to transmit JSON-serialized data packets directly between scripts, enabling clean and structured inter-pack communication.
┌─────────────────────────────────────────────────────────────┐
│ SEUDP Network Topology │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Client A │◄───────►│ Client B │ │
│ │ (Behavior │ C2C │ (Behavior │ │
│ │ Pack #2) │ Direct │ Pack #3) │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ └───►│ Server │◄──┘ │
│ │ (Behavior │ │
│ │ Pack #1) │ Central Hub │
│ └──────────────┘ │
│ │
│ • Only ONE Server per world (central processing unit) │
│ • Multiple Clients allowed (one per behavior pack) │
│ • C2C: Clients can communicate directly without server │
│ • C2S: Clients can query server-managed entity data │
│ │
└─────────────────────────────────────────────────────────────┘
- Server — There is only one server per world, acting as the central hub. It handles plugin registration, entity property queries, and centralized data management. The server runs inside its own behavior pack (e.g.,
server.mcpack). - Client — Multiple clients can exist, each residing in a separate behavior pack. Clients register themselves with the server and can communicate with each other directly (C2C) or via the server (C2S).
- C2C Communication — Clients can send packets directly to each other by setting the
receiverfield to another client's ID (e.g.,namespace:name), completely bypassing the server. - C2S Communication — Clients can query server-managed entity properties that are normally inaccessible from other behavior packs.
- 🔥 Fire-and-Forget — Send packets without waiting for acknowledgment
- 🔄 Request-Response — Built-in Promise-based request/response pattern with timeout handling
- 🔌 Plugin System — Server-side plugin registration and lifecycle management
- 🛡️ Type Safety — Strict serialization checks with circular reference detection
- 🎯 Entity Property Query — Remote query of entity properties across packs via the central server
- 📡 Event-Driven — Subscribe to incoming packets with
afterEvents.packetReceive - ⚡ Lightweight — Minimal overhead, pure JSON over script events
Place seudp_server.js in your single central behavior pack.
import { seNetwork } from "./seudp_server.js";
// The server auto-initializes and listens for incoming packets.
// Plugins register themselves automatically via the client API.
// Enable/disable plugins dynamically
seNetwork.setPluginEnabled("my_namespace:my_plugin", false);Place seudp_client.js in each behavior pack that needs to communicate.
import { seudpClient } from "./seudp_client.js";
const client = new seudpClient({
namespace: "my_namespace",
name: "my_plugin",
description: "My awesome plugin"
});
// Register with the server
await client.registerPlugin();
// Send a packet and wait for response (C2S)
const response = await client.sendPacket({
type: "custom_action",
receiver: "seudp:server",
value: { action: "do_something" }
});
// Or fire-and-forget to another client (C2C)
client.sendPacketNoWait({
type: "broadcast",
receiver: "other_namespace:other_plugin",
value: { msg: "Hello from Client A!" }
});An anti-cheat behavior pack (Client) registers itself with the central API pack (Server). The server can dynamically enable/disable the anti-cheat via commands without restarting the world.
// Client: AntiCheat Pack (anticheat.mcpack)
const antiCheat = new seudpClient({
namespace: "blarion",
name: "blarion_main",
description: "Blarion AntiCheat Main"
});
const status = await antiCheat.registerPlugin();
if (status === false) {
console.warn("Server disabled this plugin. Shutting down...");
}Entity properties set in one behavior pack are isolated — other packs cannot access them directly, and their modifications do not affect the original. SEUDP solves this by routing property queries through the central server.
Scenario: The server pack (server.mcpack) assigns a custom profile property to a player. An anti-cheat pack (anticheat.mcpack) needs to read this profile to check permissions.
// Inside server.mcpack — set a custom property
player.profile = { rank: "admin", level: 5 };
// Inside anticheat.mcpack — query it via SEUDP
const profile = await client.queryEntityProperty(
player.id,
"profile" // Returns the profile object from server.mcpack
);
console.log(profile.rank); // "admin"How it works:
- The server pack holds the authoritative
profiledata on the player entity. - The client sends a
quest_entity_propertypacket toseudp:server. - The server resolves the entity, walks the dot-notation path (e.g.,
profile.settings.pvp), and returns the value. - The client receives the data without ever touching the original entity property directly.
Clients can subscribe to afterEvents.packetReceive to handle packets from both the server and other clients.
// Subscribe to incoming packets
seNetwork.afterEvents.packetReceive.subscribe(({ sender, type, value }) => {
world.sendMessage(`[SEUDP Client] Received packet from "${sender}" of "${type}"\n${JSON.stringify(value)}`);
});
// Another client sends a packet to this client
client.sendPacketNoWait({
type: "chat_broadcast",
receiver: "my_namespace:my_plugin",
value: { message: "Hello there!" }
});
// → Output: [SEUDP Client] Received packet from "other_namespace:other_plugin" of "chat_broadcast"
// {"message":"Hello there!"}| Method | Description |
|---|---|
client.registerPlugin() |
Register this pack with the SEUDP server |
client.sendPacket(data, timeout) |
Send data to any receiver and await response |
client.sendPacketNoWait(data) |
Send data without waiting (true UDP style) |
client.queryEntityProperty(id, property) |
Query an entity property remotely via the server |
client.afterEvents.packetReceive.subscribe(cb) |
Listen for incoming packets (C2C or C2S) |
client.afterEvents.packetReceive.unsubscribe(uuid) |
Remove a packet listener |
seNetwork.setPluginEnabled(id, bool) |
Enable/disable a registered plugin (server-side) |
SEUDP(ScriptEventUserDatagramProtocol,脚本事件用户数据报协议)是一种让 Minecraft 脚本在行为包之间进行通信的方法。它采用类似 UDP 协议的发送即走数据传输模式。
不同于依赖记分板、标签或实体属性的传统方案,SEUDP 利用 Minecraft 的 scriptevent 命令直接在脚本之间传输 JSON 序列化的数据包,实现干净、结构化的跨包通信。
┌─────────────────────────────────────────────────────────────┐
│ SEUDP 网络拓扑结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 客户端 A │◄───────►│ 客户端 B │ │
│ │ (行为包 #2) │ C2C │ (行为包 #3) │ │
│ │ │ 直连 │ │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ └───►│ 服务端 │◄──┘ │
│ │ (行为包 #1) │ │
│ │ │ 中央处理节点 │
│ └──────────────┘ │
│ │
│ • 每个世界只有 一个 服务端(集中处理单元) │
│ • 允许多个客户端(每个行为包一个) │
│ • C2C:客户端可直接通信,无需经过服务端 │
│ • C2S:客户端可通过服务端查询集中管理的实体数据 │
│ │
└─────────────────────────────────────────────────────────────┘
- 服务端 — 每个世界只有一个服务端,作为中央处理节点。负责插件注册、实体属性查询和集中式数据管理。服务端运行在其独立的行为包中(例如
server.mcpack)。 - 客户端 — 可以存在多个客户端,每个位于独立的行为包中。客户端向服务端注册自身,并可以直接相互通信(C2C),或通过服务端通信(C2S)。
- C2C 通信 — 客户端可以通过将
receiver字段设置为另一客户端的 ID(如namespace:name)直接发送数据包给对方,完全绕过服务端。 - C2S 通信 — 客户端可以查询由服务端管理的实体属性,这些属性通常对其他行为包不可见。
- 🔥 发送即走 — 发送数据包无需等待确认
- 🔄 请求-响应 — 内置基于 Promise 的请求/响应模式,支持超时处理
- 🔌 插件系统 — 服务端插件注册与生命周期管理
- 🛡️ 类型安全 — 严格的序列化检查,支持循环引用检测
- 🎯 实体属性查询 — 通过中央服务端实现跨包远程查询实体属性
- 📡 事件驱动 — 通过
afterEvents.packetReceive订阅传入的数据包 - ⚡ 轻量级 — 开销极小,基于脚本事件的纯 JSON 传输
将 seudp_server.js 放入你唯一的中央行为包中。
import { seNetwork } from "./seudp_server.js";
// 服务端自动初始化并监听传入的数据包。
// 插件通过客户端 API 自动向服务端注册。
// 动态启用/禁用插件
seNetwork.setPluginEnabled("my_namespace:my_plugin", false);将 seudp_client.js 放入每个需要通信的行为包中。
import { seudpClient } from "./seudp_client.js";
const client = new seudpClient({
namespace: "my_namespace",
name: "my_plugin",
description: "我的超棒插件"
});
// 向服务端注册
await client.registerPlugin();
// 发送数据包并等待响应(C2S)
const response = await client.sendPacket({
type: "custom_action",
receiver: "seudp:server",
value: { action: "do_something" }
});
// 或直接发送给另一个客户端(C2C)
client.sendPacketNoWait({
type: "broadcast",
receiver: "other_namespace:other_plugin",
value: { msg: "来自客户端 A 的问候!" }
});一个反作弊行为包(客户端)向中央 API 包(服务端)注册自身。服务端可以通过命令动态启用/禁用反作弊功能,无需重启世界。
// 客户端:反作弊包 (anticheat.mcpack)
const antiCheat = new seudpClient({
namespace: "blarion",
name: "blarion_main",
description: "Blarion 反作弊主模块"
});
const status = await antiCheat.registerPlugin();
if (status === false) {
console.warn("服务端已禁用此插件,正在关闭...");
}在一个行为包中设置的实体属性是隔离的 —— 其他包无法直接访问,且它们的修改不会影响原始数据。SEUDP 通过中央服务端路由属性查询来解决这个问题。
场景: 服务端包(server.mcpack)给玩家赋值了一个自定义的 profile 属性。反作弊包(anticheat.mcpack)需要读取这个 profile 来检查权限。
// 在 server.mcpack 内 —— 设置自定义属性
player.profile = { rank: "admin", level: 5 };
// 在 anticheat.mcpack 内 —— 通过 SEUDP 查询
const profile = await client.queryEntityProperty(
player.id,
"profile" // 返回来自 server.mcpack 的 profile 对象
);
console.log(profile.rank); // "admin"工作原理:
- 服务端包持有玩家实体上的权威
profile数据。 - 客户端向
seudp:server发送quest_entity_property数据包。 - 服务端解析实体,遍历点号路径(如
profile.settings.pvp),并返回值。 - 客户端接收数据,而无需直接触碰原始实体属性。
客户端可以订阅 afterEvents.packetReceive 来处理来自服务端和其他客户端的数据包。
// 订阅传入的数据包
seNetwork.afterEvents.packetReceive.subscribe(({ sender, type, value }) => {
world.sendMessage(`[SEUDP 客户端] 收到来自 "${sender}" 的 "${type}" 数据包\n${JSON.stringify(value)}`);
});
// 另一个客户端向此客户端发送数据包
client.sendPacketNoWait({
type: "chat_broadcast",
receiver: "my_namespace:my_plugin",
value: { message: "你好!" }
});
// → 输出:[SEUDP 客户端] 收到来自 "other_namespace:other_plugin" 的 "chat_broadcast" 数据包
// {"message":"你好!"}| 方法 | 说明 |
|---|---|
client.registerPlugin() |
向 SEUDP 服务端注册此包 |
client.sendPacket(data, timeout) |
向任意接收者发送数据并等待响应 |
client.sendPacketNoWait(data) |
发送数据,不等待响应(纯 UDP 风格) |
client.queryEntityProperty(id, property) |
通过服务端远程查询实体属性 |
client.afterEvents.packetReceive.subscribe(cb) |
监听传入的数据包(C2C 或 C2S) |
client.afterEvents.packetReceive.unsubscribe(uuid) |
移除数据包监听器 |
seNetwork.setPluginEnabled(id, bool) |
启用/禁用已注册的插件(服务端) |
📦 server.mcpack/ ← 唯一的服务端行为包
┃ ┗ 📜 seudp_server.js # 服务端 API
┃
📦 anticheat.mcpack/ ← 客户端行为包 #1
┃ ┗ 📜 seudp_client.js # 客户端 API
┃
📦 minigames.mcpack/ ← 客户端行为包 #2
┗ 📜 seudp_client.js # 客户端 API
- Minecraft Bedrock Edition 1.21.30+ Script API Stable or Beta Experimental (WARNING: could throw unexpected error while using Aplha Experimental API)
@minecraft/servermodule- Behavior packs must have
scriptingenabled inmanifest.json - Only one behavior pack should include
seudp_server.js
MIT License — feel free to use in your own packs.
By NyaCrasin