@discord-mcbe/client 0.0.1 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 tutinoko2048/RetoRuto9900K
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/bds.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { n as ClientType, t as BaseClient } from "./index-CzbgOaig.js";
2
+ import { ScriptBridgeClient } from "@script-bridge/client";
3
+
4
+ //#region src/bds.d.ts
5
+ interface BridgeClientOptions {
6
+ host?: string;
7
+ port?: number;
8
+ clientId?: string | (() => string);
9
+ }
10
+ declare class BridgeClient extends BaseClient<ScriptBridgeClient> {
11
+ readonly type = ClientType.BDS;
12
+ constructor(options?: BridgeClientOptions);
13
+ start(): Promise<void>;
14
+ }
15
+ //#endregion
16
+ export { BridgeClient, BridgeClientOptions };
package/dist/bds.js ADDED
@@ -0,0 +1,33 @@
1
+ import { n as ClientType, t as BaseClient } from "./client-DgUd3XAf.js";
2
+ import { world } from "@minecraft/server";
3
+ import { ScriptBridgeClient } from "@script-bridge/client";
4
+
5
+ //#region src/bds.ts
6
+ const defaultOptions = {
7
+ host: "localhost",
8
+ port: 23191,
9
+ clientId: () => world.getDynamicProperty("clientId") ?? "discord-mcbe-bds"
10
+ };
11
+ var BridgeClient = class extends BaseClient {
12
+ type = ClientType.BDS;
13
+ constructor(options = {}) {
14
+ const mergedOptions = {
15
+ ...defaultOptions,
16
+ ...options
17
+ };
18
+ const bridge = new ScriptBridgeClient({
19
+ url: `http://${mergedOptions.host}:${mergedOptions.port}`,
20
+ clientId: mergedOptions.clientId
21
+ });
22
+ super(bridge);
23
+ }
24
+ async start() {
25
+ console.log("[discord-mcbe] Connecting to discord-mcbe server...");
26
+ const requestedAt = Date.now();
27
+ await this.bridge.connect();
28
+ console.log(`[discord-mcbe] Connection established! (${Date.now() - requestedAt}ms)`);
29
+ }
30
+ };
31
+
32
+ //#endregion
33
+ export { BridgeClient };
@@ -0,0 +1,360 @@
1
+ import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, ObjectiveSortOrder, Player, ScoreboardIdentityType, system, world } from "@minecraft/server";
2
+ import { ActionId } from "@discord-mcbe/shared";
3
+ import { ModalFormData } from "@minecraft/server-ui";
4
+
5
+ //#region src/client/descriptors.ts
6
+ function createPlayerDescriptor(player) {
7
+ return {
8
+ name: player.name,
9
+ nameTag: player.nameTag,
10
+ uniqueId: player.id,
11
+ platformType: player.clientSystemInfo.platformType
12
+ };
13
+ }
14
+ function createDimensionDescriptor(dimension) {
15
+ return {
16
+ id: dimension.id,
17
+ heightRange: dimension.heightRange
18
+ };
19
+ }
20
+ function createScoreboardObjectiveDescriptor(objective) {
21
+ return {
22
+ id: objective.id,
23
+ displayName: objective.displayName
24
+ };
25
+ }
26
+ function createScoreboardIdentityDescriptor(identity) {
27
+ let entityUniqueId;
28
+ if ((identity.type === ScoreboardIdentityType.Entity || identity.type === ScoreboardIdentityType.Player) && identity.displayName !== "commands.scoreboard.players.offlinePlayerName") entityUniqueId = identity.getEntity()?.id;
29
+ return {
30
+ type: identity.type,
31
+ id: identity.id,
32
+ displayName: identity.displayName,
33
+ entityUniqueId
34
+ };
35
+ }
36
+ function createScoreboardScoreInfoDescriptor(info) {
37
+ return {
38
+ score: info.score,
39
+ participant: createScoreboardIdentityDescriptor(info.participant)
40
+ };
41
+ }
42
+
43
+ //#endregion
44
+ //#region src/client/util.ts
45
+ let lastTick;
46
+ const deltaTimes = [];
47
+ system.runInterval(() => {
48
+ const now = Date.now();
49
+ if (lastTick) {
50
+ if (deltaTimes.length > 60) deltaTimes.shift();
51
+ deltaTimes.push(now - lastTick);
52
+ }
53
+ lastTick = now;
54
+ });
55
+ function getTPS() {
56
+ if (deltaTimes.length === 0) return 20;
57
+ const tps = 1e3 / (deltaTimes.reduce((sum, delta) => sum + delta, 0) / deltaTimes.length);
58
+ return Math.floor(tps * 100) / 100;
59
+ }
60
+
61
+ //#endregion
62
+ //#region src/client/handler.ts
63
+ function registerHandlers(bridge) {
64
+ bridge.registerHandler(ActionId.SendMessage, (action) => {
65
+ const { message, playerUniqueId } = action.data;
66
+ if (playerUniqueId) {
67
+ const player = world.getEntity(playerUniqueId);
68
+ if (!(player instanceof Player)) throw new Error("Player not found");
69
+ player.sendMessage(message);
70
+ } else world.sendMessage(message);
71
+ action.respond();
72
+ });
73
+ bridge.registerHandler(ActionId.RunCommand, (action) => {
74
+ const { command } = action.data;
75
+ const { successCount } = world.getDimension("overworld").runCommand(command);
76
+ action.respond({ successCount });
77
+ });
78
+ bridge.registerHandler(ActionId.SendScriptEvent, (action) => {
79
+ const { id, message } = action.data;
80
+ system.sendScriptEvent(id, message);
81
+ action.respond();
82
+ });
83
+ bridge.registerHandler(ActionId.GetTPS, (action) => {
84
+ const tps = getTPS();
85
+ action.respond({ tps });
86
+ });
87
+ bridge.registerHandler(ActionId.GetEntityLocation, (action) => {
88
+ const { entityUniqueId } = action.data;
89
+ const entity = world.getEntity(entityUniqueId);
90
+ if (!entity) throw new Error("Entity not found");
91
+ action.respond({ location: entity.location });
92
+ });
93
+ bridge.registerHandler(ActionId.GetEntityDimension, (action) => {
94
+ const { entityUniqueId } = action.data;
95
+ const entity = world.getEntity(entityUniqueId);
96
+ if (!entity) throw new Error("Entity not found");
97
+ action.respond({ dimension: createDimensionDescriptor(entity.dimension) });
98
+ });
99
+ bridge.registerHandler(ActionId.GetGameMode, (action) => {
100
+ const { playerUniqueId } = action.data;
101
+ const player = world.getEntity(playerUniqueId);
102
+ if (!(player instanceof Player)) throw new Error("Player not found");
103
+ action.respond({ gameMode: player.getGameMode() });
104
+ });
105
+ bridge.registerHandler(ActionId.SetGameMode, (action) => {
106
+ const { playerUniqueId, gameMode } = action.data;
107
+ const player = world.getEntity(playerUniqueId);
108
+ if (!(player instanceof Player)) throw new Error("Player not found");
109
+ player.setGameMode(gameMode);
110
+ action.respond();
111
+ });
112
+ bridge.registerHandler(ActionId.SetTitle, (action) => {
113
+ const { playerUniqueId, title, options } = action.data;
114
+ const player = world.getEntity(playerUniqueId);
115
+ if (!(player instanceof Player)) throw new Error("Player not found");
116
+ player.onScreenDisplay.setTitle(title, options);
117
+ action.respond();
118
+ });
119
+ bridge.registerHandler(ActionId.UpdateSubtitle, (action) => {
120
+ const { playerUniqueId, subtitle } = action.data;
121
+ const player = world.getEntity(playerUniqueId);
122
+ if (!(player instanceof Player)) throw new Error("Player not found");
123
+ player.onScreenDisplay.updateSubtitle(subtitle);
124
+ action.respond();
125
+ });
126
+ bridge.registerHandler(ActionId.SetActionBar, (action) => {
127
+ const { playerUniqueId, text } = action.data;
128
+ const player = world.getEntity(playerUniqueId);
129
+ if (!(player instanceof Player)) throw new Error("Player not found");
130
+ player.onScreenDisplay.setActionBar(text);
131
+ action.respond();
132
+ });
133
+ bridge.registerHandler(ActionId.KickPlayer, (action) => {
134
+ const { playerUniqueId, reason } = action.data;
135
+ const player = world.getEntity(playerUniqueId);
136
+ if (!(player instanceof Player)) throw new Error("Player not found");
137
+ player.runCommand(`kick @s ${reason ? reason : ""}`);
138
+ action.respond();
139
+ });
140
+ bridge.registerHandler(ActionId.GetScore, (action) => {
141
+ const { objectiveId, participant } = action.data;
142
+ const objective = world.scoreboard.getObjective(objectiveId);
143
+ if (!objective) throw new Error(`Objective '${objectiveId}' not found`);
144
+ const target = participant.uniqueId ? world.getEntity(participant.uniqueId) : participant.fakePlayer;
145
+ if (!target) throw new Error(`Participant '${participant.uniqueId}' not found`);
146
+ action.respond({ value: target ? objective.getScore(target) ?? null : null });
147
+ });
148
+ bridge.registerHandler(ActionId.UpdateScore, (action) => {
149
+ const { objectiveId, participant, type, score } = action.data;
150
+ const objective = world.scoreboard.getObjective(objectiveId);
151
+ if (!objective) throw new Error(`Objective '${objectiveId}' not found`);
152
+ const target = participant.uniqueId ? world.getEntity(participant.uniqueId) : participant.fakePlayer;
153
+ if (!target) throw new Error(`Participant '${participant.uniqueId}' not found`);
154
+ let newScore = score;
155
+ if (type === "set") objective.setScore(target, score);
156
+ else if (type === "add") newScore = objective.addScore(target, score);
157
+ action.respond({ value: newScore });
158
+ });
159
+ bridge.registerHandler(ActionId.GetAllScores, (action) => {
160
+ const { objectiveId } = action.data;
161
+ const objective = world.scoreboard.getObjective(objectiveId);
162
+ if (!objective) throw new Error(`Objective '${objectiveId}' not found`);
163
+ const scores = objective.getScores().map(createScoreboardScoreInfoDescriptor);
164
+ action.respond({ scores });
165
+ });
166
+ bridge.registerHandler(ActionId.RemoveParticipant, (action) => {
167
+ const { objectiveId, participant } = action.data;
168
+ const objective = world.scoreboard.getObjective(objectiveId);
169
+ if (!objective) throw new Error(`Objective '${objectiveId}' not found`);
170
+ const target = participant.uniqueId ? world.getEntity(participant.uniqueId) : participant.fakePlayer;
171
+ if (!target) throw new Error(`Participant '${participant.uniqueId}' not found`);
172
+ action.respond({ result: objective.removeParticipant(target) });
173
+ });
174
+ bridge.registerHandler(ActionId.GetObjective, (action) => {
175
+ const { objectiveId } = action.data;
176
+ const objective = world.scoreboard.getObjective(objectiveId);
177
+ action.respond({ objective: objective ? createScoreboardObjectiveDescriptor(objective) : void 0 });
178
+ });
179
+ bridge.registerHandler(ActionId.GetAllObjectives, (action) => {
180
+ const objectiveDescriptors = world.scoreboard.getObjectives().map(createScoreboardObjectiveDescriptor);
181
+ action.respond({ objectives: objectiveDescriptors });
182
+ });
183
+ bridge.registerHandler(ActionId.UpdateObjective, (action) => {
184
+ const { type, objectiveId, displayName } = action.data;
185
+ let objective;
186
+ if (type === "add") objective = world.scoreboard.addObjective(objectiveId, displayName);
187
+ else if (type === "remove") world.scoreboard.removeObjective(objectiveId);
188
+ action.respond({ objective: objective ? createScoreboardObjectiveDescriptor(objective) : void 0 });
189
+ });
190
+ bridge.registerHandler(ActionId.SetObjectiveDisplay, (action) => {
191
+ const { displaySlotId, objectiveId, sortOrder } = action.data;
192
+ if (objectiveId) {
193
+ const objective = world.scoreboard.getObjective(objectiveId);
194
+ if (!objective) throw new Error(`Objective '${objectiveId}' not found`);
195
+ world.scoreboard.setObjectiveAtDisplaySlot(displaySlotId, {
196
+ objective,
197
+ sortOrder: sortOrder ? ObjectiveSortOrder[sortOrder] : void 0
198
+ });
199
+ } else world.scoreboard.clearObjectiveAtDisplaySlot(displaySlotId);
200
+ action.respond();
201
+ });
202
+ }
203
+
204
+ //#endregion
205
+ //#region src/client/event.ts
206
+ function registerEvents(bridge) {
207
+ world.afterEvents.playerSpawn.subscribe((ev) => {
208
+ if (!bridge.isConnected || !ev.initialSpawn) return;
209
+ bridge.send(ActionId.PlayerJoin, { player: createPlayerDescriptor(ev.player) });
210
+ });
211
+ world.afterEvents.playerLeave.subscribe((ev) => {
212
+ if (!bridge.isConnected) return;
213
+ bridge.send(ActionId.PlayerLeave, { playerUniqueId: ev.playerId });
214
+ });
215
+ world.afterEvents.chatSend.subscribe((ev) => {
216
+ if (!bridge.isConnected) return;
217
+ bridge.send(ActionId.ChatSend, {
218
+ senderName: ev.sender.name,
219
+ senderUniqueId: ev.sender.id,
220
+ message: ev.message
221
+ });
222
+ });
223
+ }
224
+
225
+ //#endregion
226
+ //#region src/client/features/settings.ts
227
+ var SettingsForm = class SettingsForm {
228
+ constructor(player, client) {
229
+ this.player = player;
230
+ this.client = client;
231
+ }
232
+ static async show(player, client) {
233
+ await new SettingsForm(player, client).main();
234
+ }
235
+ async main() {
236
+ const res = await new ModalFormData().title("discord-mcbe settings").textField("clientId", "Client ID", { defaultValue: this.client.getClientId() }).show(this.player);
237
+ if (res.canceled || !res.formValues) return;
238
+ const newClientId = res.formValues[0];
239
+ this.client.setClientId(newClientId);
240
+ }
241
+ };
242
+
243
+ //#endregion
244
+ //#region src/client/command.ts
245
+ function registerCommands(registry, client) {
246
+ for (const commandName of ["dmc:dmc", "dmc:discord-mcbe"]) registry.registerCommand({
247
+ name: commandName,
248
+ description: "Open discord-mcbe settings",
249
+ permissionLevel: CommandPermissionLevel.Admin
250
+ }, (origin) => {
251
+ const player = origin.sourceEntity;
252
+ if (!(player instanceof Player)) return {
253
+ status: CustomCommandStatus.Failure,
254
+ message: "This command can only be used by players."
255
+ };
256
+ system.run(() => SettingsForm.show(player, client));
257
+ return { status: CustomCommandStatus.Success };
258
+ });
259
+ registry.registerCommand({
260
+ name: "dmc:setid",
261
+ description: "Set the clientId for discord-mcbe (requires reconnect)",
262
+ permissionLevel: CommandPermissionLevel.Host,
263
+ mandatoryParameters: [{
264
+ type: CustomCommandParamType.String,
265
+ name: "clientId"
266
+ }]
267
+ }, (_, clientId) => {
268
+ client.setClientId(clientId);
269
+ return {
270
+ status: CustomCommandStatus.Success,
271
+ message: `Updated clientId to "${clientId}".`
272
+ };
273
+ });
274
+ registry.registerCommand({
275
+ name: "dmc:disconnect",
276
+ description: "Disconnect from discord-mcbe server",
277
+ permissionLevel: CommandPermissionLevel.Admin
278
+ }, () => {
279
+ if (client.bridge.isConnected) {
280
+ client.bridge.disconnect();
281
+ return {
282
+ status: CustomCommandStatus.Success,
283
+ message: "Disconnecting from discord-mcbe server..."
284
+ };
285
+ } else return {
286
+ status: CustomCommandStatus.Failure,
287
+ message: "discord-mcbe is not connected now."
288
+ };
289
+ });
290
+ }
291
+
292
+ //#endregion
293
+ //#region src/utils/logger.ts
294
+ var Logger = class {
295
+ constructor(name) {
296
+ this.name = name;
297
+ }
298
+ log(...message) {
299
+ console.log(`[${this.name}] ${this.formatMessage(message)}`);
300
+ }
301
+ info(...message) {
302
+ console.info(`[${this.name}] ${this.formatMessage(message)}`);
303
+ }
304
+ warn(...message) {
305
+ console.warn(`[${this.name}] ${this.formatMessage(message)}`);
306
+ }
307
+ error(...message) {
308
+ console.error(`[${this.name}] ${this.formatMessage(message)}`);
309
+ }
310
+ debug(...message) {}
311
+ formatMessage(message) {
312
+ return message.map((msg) => {
313
+ if (msg instanceof Error) return msg.stack ?? msg;
314
+ else if (typeof msg !== "string") return JSON.stringify(msg, null, 2);
315
+ return msg;
316
+ }).join(" ");
317
+ }
318
+ };
319
+
320
+ //#endregion
321
+ //#region src/client/base-client.ts
322
+ let ClientType = /* @__PURE__ */ function(ClientType$1) {
323
+ ClientType$1["Local"] = "Local";
324
+ ClientType$1["BDS"] = "BDS";
325
+ return ClientType$1;
326
+ }({});
327
+ var BaseClient = class {
328
+ bridge;
329
+ logger = new Logger("discord-mcbe");
330
+ constructor(bridge) {
331
+ this.bridge = bridge;
332
+ registerHandlers(this.bridge);
333
+ registerEvents(this.bridge);
334
+ system.beforeEvents.startup.subscribe((ev) => registerCommands(ev.customCommandRegistry, this));
335
+ this.bridge.on("connect", this.onConnect.bind(this));
336
+ }
337
+ setClientId(clientId) {
338
+ world.setDynamicProperty("clientId", clientId);
339
+ }
340
+ getClientId() {
341
+ return world.getDynamicProperty("clientId");
342
+ }
343
+ async onConnect() {
344
+ const players = world.getPlayers();
345
+ try {
346
+ await this.bridge.send(ActionId.WorldInitialize, { players: players.map(createPlayerDescriptor) });
347
+ } catch (error) {
348
+ this.logger.error("Failed to send WorldInitializeAction:", error);
349
+ }
350
+ }
351
+ isLocal() {
352
+ return this.type === ClientType.Local;
353
+ }
354
+ isBDS() {
355
+ return this.type === ClientType.BDS;
356
+ }
357
+ };
358
+
359
+ //#endregion
360
+ export { ClientType as n, Logger as r, BaseClient as t };
@@ -0,0 +1,112 @@
1
+ import { ActionId } from "@discord-mcbe/shared";
2
+ import { ActionHandler, ScriptBridgeClient } from "@script-bridge/client";
3
+ import { BaseAction, DisconnectReason } from "@script-bridge/protocol";
4
+
5
+ //#region src/utils/emitter.d.ts
6
+ type Listener$1<T> = (data: T) => void;
7
+ declare class Emitter<T> {
8
+ private listeners;
9
+ on<K extends keyof T>(event: K, listener: Listener$1<T[K]>): void;
10
+ once<K extends keyof T>(event: K, listener: Listener$1<T[K]>): void;
11
+ emit<K extends keyof T>(event: K, data: T[K]): void;
12
+ off<K extends keyof T>(event: K, listener: Listener$1<T[K]>): void;
13
+ removeAllListeners<K extends keyof T>(event: K): void;
14
+ }
15
+ //#endregion
16
+ //#region src/utils/logger.d.ts
17
+ declare class Logger {
18
+ private readonly name;
19
+ constructor(name: string);
20
+ log(...message: unknown[]): void;
21
+ info(...message: unknown[]): void;
22
+ warn(...message: unknown[]): void;
23
+ error(...message: unknown[]): void;
24
+ debug(...message: unknown[]): void;
25
+ private formatMessage;
26
+ }
27
+ //#endregion
28
+ //#region src/transport/interfaces.d.ts
29
+ type Listener<T> = (data: T) => void;
30
+ type IResponse<T = unknown> = {
31
+ error?: false;
32
+ data: T;
33
+ } | {
34
+ error: true;
35
+ message: string;
36
+ };
37
+ interface IBridgeClient {
38
+ isConnected: boolean;
39
+ send<T extends BaseAction = BaseAction>(channelId: ActionId, data?: T['request']): Promise<IResponse<T['response']>>;
40
+ registerHandler<A extends BaseAction = BaseAction>(channelId: A['id'], handler: ActionHandler<A>): void;
41
+ disconnect(reason?: DisconnectReason): Promise<void>;
42
+ on(event: 'connect', listener: Listener<{
43
+ sessionId: string;
44
+ }>): void;
45
+ on(event: 'disconnect', listener: Listener<{
46
+ reason: DisconnectReason;
47
+ }>): void;
48
+ }
49
+ //#endregion
50
+ //#region src/transport/socket.d.ts
51
+ interface SocketEvents {
52
+ ready: {};
53
+ connect: {
54
+ sessionId: string;
55
+ };
56
+ disconnect: {
57
+ reason: DisconnectReason;
58
+ };
59
+ }
60
+ interface ClientOptions {
61
+ clientId: string | (() => string);
62
+ }
63
+ declare class SocketBridgeClient extends Emitter<SocketEvents> implements IBridgeClient {
64
+ static readonly PROTOCOL_VERSION = 1;
65
+ private readonly _clientId;
66
+ private readonly sendQueue;
67
+ private readonly awaitingResponses;
68
+ private readonly actionHandlers;
69
+ private readonly deltaTimes;
70
+ private readonly logger;
71
+ private previousRequestId;
72
+ private currentSessionId;
73
+ private lastQueryReceivedAt;
74
+ constructor(options: ClientOptions);
75
+ get isConnected(): boolean;
76
+ get clientId(): string;
77
+ send<A extends BaseAction = BaseAction>(channelId: A['id'], data?: A['request']): Promise<IResponse<A['response']>>;
78
+ registerHandler<A extends BaseAction = BaseAction>(channelId: A['id'], handler: ActionHandler<A>): void;
79
+ disconnect(reason?: DisconnectReason): Promise<void>;
80
+ destroy(): void;
81
+ private clearResponses;
82
+ private handleConnection;
83
+ private handleResponse;
84
+ private handleRequest;
85
+ private getQueue;
86
+ private onQuery;
87
+ /** Process incoming messages from server */
88
+ private onMessage;
89
+ /**
90
+ * Register internal commands
91
+ */
92
+ private onStartup;
93
+ }
94
+ //#endregion
95
+ //#region src/client/base-client.d.ts
96
+ declare enum ClientType {
97
+ Local = "Local",
98
+ BDS = "BDS",
99
+ }
100
+ declare abstract class BaseClient<T extends IBridgeClient = IBridgeClient> {
101
+ readonly bridge: T;
102
+ readonly logger: Logger;
103
+ abstract readonly type: ClientType;
104
+ constructor(bridge: T);
105
+ setClientId(clientId: string): void;
106
+ getClientId(): string | undefined;
107
+ private onConnect;
108
+ isLocal(): this is BaseClient<SocketBridgeClient>;
109
+ isBDS(): this is BaseClient<ScriptBridgeClient>;
110
+ }
111
+ //#endregion
112
+ export { ClientType as n, SocketBridgeClient as r, BaseClient as t };
@@ -0,0 +1,12 @@
1
+ import { n as ClientType, r as SocketBridgeClient, t as BaseClient } from "./index-CzbgOaig.js";
2
+
3
+ //#region src/local.d.ts
4
+ interface BridgeClientOptions {
5
+ clientId?: string | (() => string);
6
+ }
7
+ declare class BridgeClient extends BaseClient<SocketBridgeClient> {
8
+ readonly type = ClientType.Local;
9
+ constructor(options?: BridgeClientOptions);
10
+ }
11
+ //#endregion
12
+ export { BridgeClient, BridgeClientOptions };
package/dist/local.js ADDED
@@ -0,0 +1,277 @@
1
+ import { n as ClientType, r as Logger, t as BaseClient } from "./client-DgUd3XAf.js";
2
+ import { CommandPermissionLevel, CustomCommandParamType, CustomCommandStatus, system, world } from "@minecraft/server";
3
+ import { SocketBridge } from "@discord-mcbe/shared";
4
+ import { DisconnectReason, InternalAction, PayloadType, ResponseErrorReason } from "@script-bridge/protocol";
5
+
6
+ //#region src/utils/emitter.ts
7
+ var Emitter = class {
8
+ listeners = /* @__PURE__ */ new Map();
9
+ on(event, listener) {
10
+ let listeners = this.listeners.get(event);
11
+ if (!listeners) {
12
+ listeners = [];
13
+ this.listeners.set(event, listeners);
14
+ }
15
+ listeners.push(listener);
16
+ }
17
+ once(event, listener) {
18
+ const onceListener = (data) => {
19
+ listener(data);
20
+ this.off(event, onceListener);
21
+ };
22
+ this.on(event, onceListener);
23
+ }
24
+ emit(event, data) {
25
+ for (const listener of this.listeners.get(event) ?? []) listener(data);
26
+ }
27
+ off(event, listener) {
28
+ const currentListeners = this.listeners.get(event);
29
+ if (currentListeners) this.listeners.set(event, currentListeners.filter((l) => l !== listener));
30
+ }
31
+ removeAllListeners(event) {
32
+ this.listeners.set(event, []);
33
+ }
34
+ };
35
+
36
+ //#endregion
37
+ //#region src/transport/socket.ts
38
+ var SocketBridgeClient = class SocketBridgeClient extends Emitter {
39
+ static PROTOCOL_VERSION = SocketBridge.PROTOCOL_VERSION;
40
+ _clientId;
41
+ sendQueue = [];
42
+ awaitingResponses = /* @__PURE__ */ new Map();
43
+ actionHandlers = /* @__PURE__ */ new Map();
44
+ deltaTimes = [];
45
+ logger = new Logger("discord-mcbe");
46
+ previousRequestId = 0;
47
+ currentSessionId = null;
48
+ lastQueryReceivedAt = null;
49
+ constructor(options) {
50
+ super();
51
+ this._clientId = options.clientId;
52
+ system.beforeEvents.startup.subscribe(this.onStartup.bind(this));
53
+ system.afterEvents.scriptEventReceive.subscribe((event) => {
54
+ if (event.id === "bridge:message") this.onMessage(event.message.trim());
55
+ }, { namespaces: ["bridge"] });
56
+ world.afterEvents.worldLoad.subscribe(() => {
57
+ this.emit("ready", {});
58
+ });
59
+ }
60
+ get isConnected() {
61
+ return this.currentSessionId !== null;
62
+ }
63
+ get clientId() {
64
+ return typeof this._clientId === "function" ? this._clientId() : this._clientId;
65
+ }
66
+ async send(channelId, data) {
67
+ if (!this.currentSessionId) throw new Error("No active session");
68
+ const requestId = ++this.previousRequestId;
69
+ this.sendQueue.push({
70
+ type: PayloadType.Request,
71
+ channelId,
72
+ data,
73
+ sessionId: this.currentSessionId,
74
+ requestId
75
+ });
76
+ return new Promise((resolve) => {
77
+ this.awaitingResponses.set(requestId, (response) => {
78
+ resolve(response);
79
+ });
80
+ });
81
+ }
82
+ registerHandler(channelId, handler) {
83
+ if (this.actionHandlers.has(channelId)) console.warn("[SocketBridge] Overwriting existing handler for channel:", channelId);
84
+ this.actionHandlers.set(channelId, handler);
85
+ }
86
+ async disconnect(reason = DisconnectReason.Disconnect) {
87
+ try {
88
+ await this.send(InternalAction.Disconnect, { reason });
89
+ } catch (e) {
90
+ this.logger.warn("Failed to send disconnect message", e);
91
+ }
92
+ this.destroy();
93
+ this.emit("disconnect", { reason });
94
+ }
95
+ destroy() {
96
+ this.clearResponses();
97
+ this.sendQueue.length = 0;
98
+ this.deltaTimes.length = 0;
99
+ this.lastQueryReceivedAt = null;
100
+ this.currentSessionId = null;
101
+ }
102
+ clearResponses() {
103
+ for (const [requestId, resolve] of this.awaitingResponses.entries()) resolve({
104
+ type: PayloadType.Response,
105
+ error: true,
106
+ message: "Session disconnected before response was received",
107
+ errorReason: ResponseErrorReason.Abort,
108
+ sessionId: this.currentSessionId,
109
+ requestId
110
+ });
111
+ this.awaitingResponses.clear();
112
+ }
113
+ handleConnection(protocolVersion, sessionId) {
114
+ let body;
115
+ if (SocketBridgeClient.PROTOCOL_VERSION > protocolVersion) body = {
116
+ error: true,
117
+ errorReason: DisconnectReason.OutdatedServer
118
+ };
119
+ else if (SocketBridgeClient.PROTOCOL_VERSION < protocolVersion) body = {
120
+ error: true,
121
+ errorReason: DisconnectReason.OutdatedClient
122
+ };
123
+ else {
124
+ body = {
125
+ protocolVersion: SocketBridgeClient.PROTOCOL_VERSION,
126
+ clientId: this.clientId
127
+ };
128
+ this.destroy();
129
+ this.currentSessionId = sessionId;
130
+ this.emit("connect", { sessionId });
131
+ }
132
+ return {
133
+ status: CustomCommandStatus.Success,
134
+ message: JSON.stringify(body)
135
+ };
136
+ }
137
+ async handleResponse(response) {
138
+ const { requestId } = response;
139
+ const resolve = this.awaitingResponses.get(requestId);
140
+ if (resolve) {
141
+ resolve(response);
142
+ this.awaitingResponses.delete(requestId);
143
+ }
144
+ }
145
+ async handleRequest(request) {
146
+ const { requestId, sessionId, channelId } = request;
147
+ const handler = this.actionHandlers.get(channelId);
148
+ if (!handler) {
149
+ console.error("[SocketBridge] No handler for channel:", channelId);
150
+ this.sendQueue.push({
151
+ type: PayloadType.Response,
152
+ error: true,
153
+ errorReason: ResponseErrorReason.UnhandledRequest,
154
+ message: `No handler found for channel: ${channelId}`,
155
+ requestId,
156
+ sessionId
157
+ });
158
+ return;
159
+ }
160
+ try {
161
+ await handler({
162
+ data: request.data,
163
+ respond: (data) => {
164
+ this.sendQueue.push({
165
+ type: PayloadType.Response,
166
+ data,
167
+ requestId,
168
+ sessionId
169
+ });
170
+ }
171
+ });
172
+ } catch (err) {
173
+ console.error("[SocketBridge] Error while handling request:", channelId, err);
174
+ this.sendQueue.push({
175
+ type: PayloadType.Response,
176
+ error: true,
177
+ errorReason: ResponseErrorReason.InternalError,
178
+ message: `An error occurred while handling the request\n${String(err)}`,
179
+ requestId,
180
+ sessionId
181
+ });
182
+ }
183
+ }
184
+ getQueue() {
185
+ const queue = this.sendQueue.slice();
186
+ this.sendQueue.length = 0;
187
+ return queue;
188
+ }
189
+ onQuery(sessionId) {
190
+ let result;
191
+ if (this.currentSessionId === sessionId) {
192
+ result = {
193
+ error: false,
194
+ data: this.getQueue()
195
+ };
196
+ const now = Date.now();
197
+ if (this.lastQueryReceivedAt !== null) {
198
+ this.deltaTimes.push(now - this.lastQueryReceivedAt);
199
+ if (this.deltaTimes.length > 20) this.deltaTimes.shift();
200
+ }
201
+ this.lastQueryReceivedAt = now;
202
+ } else result = {
203
+ error: true,
204
+ errorReason: ResponseErrorReason.InvalidSession
205
+ };
206
+ return {
207
+ status: CustomCommandStatus.Success,
208
+ message: JSON.stringify(result)
209
+ };
210
+ }
211
+ /** Process incoming messages from server */
212
+ onMessage(rawMessage) {
213
+ let message;
214
+ try {
215
+ message = JSON.parse(rawMessage);
216
+ } catch (e) {
217
+ console.error("[SocketBridge] Failed to parse message from bridge:", e);
218
+ return;
219
+ }
220
+ if (message.type === PayloadType.Response) this.handleResponse(message).catch((error) => {
221
+ console.error("[SocketBridge] Failed to handle response:", error);
222
+ });
223
+ else if (message.type === PayloadType.Request) this.handleRequest(message).catch((error) => {
224
+ console.error("[SocketBridge] Failed to handle request:", error);
225
+ });
226
+ }
227
+ /**
228
+ * Register internal commands
229
+ */
230
+ onStartup(ev) {
231
+ const registry = ev.customCommandRegistry;
232
+ registry.registerCommand({
233
+ name: "dmc:__query__",
234
+ description: "§8[internal] query messages for SocketBridge",
235
+ permissionLevel: CommandPermissionLevel.Host,
236
+ mandatoryParameters: [{
237
+ name: "sessionId",
238
+ type: CustomCommandParamType.String
239
+ }]
240
+ }, (_, sessionId) => this.onQuery(sessionId));
241
+ registry.registerCommand({
242
+ name: "dmc:__connect__",
243
+ description: "§8[internal] initialize connection for SocketBridge",
244
+ permissionLevel: CommandPermissionLevel.Host,
245
+ mandatoryParameters: [{
246
+ name: "protocolVersion",
247
+ type: CustomCommandParamType.Integer
248
+ }, {
249
+ name: "sessionId",
250
+ type: CustomCommandParamType.String
251
+ }]
252
+ }, (_, protocolVersion, sessionId) => this.handleConnection(protocolVersion, sessionId));
253
+ }
254
+ };
255
+
256
+ //#endregion
257
+ //#region src/local.ts
258
+ const defaultOptions = { clientId: () => world.getDynamicProperty("clientId") ?? "discord-mcbe-local" };
259
+ var BridgeClient = class extends BaseClient {
260
+ type = ClientType.Local;
261
+ constructor(options = {}) {
262
+ const bridge = new SocketBridgeClient({
263
+ ...defaultOptions,
264
+ ...options
265
+ });
266
+ super(bridge);
267
+ bridge.on("ready", () => {
268
+ console.info("[discord-mcbe] Listening connection from discord-mcbe server...");
269
+ });
270
+ bridge.on("connect", () => {
271
+ console.info("[discord-mcbe] Connection established!");
272
+ });
273
+ }
274
+ };
275
+
276
+ //#endregion
277
+ export { BridgeClient };
package/package.json CHANGED
@@ -1,10 +1,43 @@
1
1
  {
2
2
  "name": "@discord-mcbe/client",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @discord-mcbe/client",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
10
- }
3
+ "version": "4.0.0-beta.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/tutinoko2048/discord-mcbe.git",
7
+ "directory": "packages/client"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "type": "module",
13
+ "exports": {
14
+ "./bds": {
15
+ "default": "./dist/bds.js",
16
+ "types": "./dist/bds.d.ts"
17
+ },
18
+ "./local": {
19
+ "default": "./dist/local.js",
20
+ "types": "./dist/local.d.ts"
21
+ }
22
+ },
23
+ "dependencies": {
24
+ "@minecraft/server": "2.5.0-beta.1.21.130-stable",
25
+ "@minecraft/server-ui": "2.0.0",
26
+ "@script-bridge/client": "^0.5.0",
27
+ "@script-bridge/protocol": "^0.5.0",
28
+ "@discord-mcbe/shared": "4.0.0-beta.0"
29
+ },
30
+ "devDependencies": {
31
+ "@bedrock-apis/env-types": "1.0.0-beta.6",
32
+ "@discord-mcbe/internal-config": "0.1.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsdown",
36
+ "watch": "tsdown --watch",
37
+ "lint": "vp lint",
38
+ "lint:fix": "vp lint --fix",
39
+ "format": "vp fmt",
40
+ "check": "vp check",
41
+ "clean": "rimraf dist .turbo tsconfig.tsbuildinfo"
42
+ }
43
+ }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @discord-mcbe/client
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@discord-mcbe/client`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**