@block_factory/lib 0.0.1

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.
@@ -0,0 +1,124 @@
1
+ import {
2
+ CustomCommand,
3
+ CommandPermissionLevel,
4
+ CustomCommandParameter,
5
+ CustomCommandOrigin,
6
+ CustomCommandResult,
7
+ system,
8
+ StartupEvent
9
+ } from "@minecraft/server";
10
+
11
+ /**
12
+ * Command utilities and base classes for defining and registering
13
+ * Minecraft Bedrock custom commands.
14
+ *
15
+ * Provides:
16
+ * - `ICustomCommand` base class
17
+ * - `RegisterCustomCommand` decorator/function
18
+ * - Internal command registry used during startup
19
+ */
20
+ export namespace Command {
21
+ /**
22
+ * Base class for all custom commands.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { Command } from "@block_factory/lib";
27
+ * import {
28
+ * world,
29
+ * system,
30
+ * CommandPermissionLevel,
31
+ * CustomCommandParameter,
32
+ * CustomCommandParamType,
33
+ * CustomCommandOrigin,
34
+ * CustomCommandResult,
35
+ * CustomCommandStatus
36
+ * } from "@minecraft/server";
37
+ *
38
+ * @Command.RegisterCustomCommand
39
+ * export default class TestCommand extends Command.ICustomCommand {
40
+ * public name = "bf_sd:test_cmd";
41
+ * public description = "Test command for debugging purposes.";
42
+ * public permissionLevel = CommandPermissionLevel.Admin;
43
+ *
44
+ * public optionalParameters: CustomCommandParameter[] = [
45
+ * { type: CustomCommandParamType.Integer, name: "size" }
46
+ * ];
47
+ *
48
+ * public execute(
49
+ * origin: CustomCommandOrigin,
50
+ * value: number
51
+ * ): CustomCommandResult | undefined {
52
+ * const entity = origin.sourceEntity;
53
+ * if (!entity)
54
+ * return {
55
+ * status: CustomCommandStatus.Failure,
56
+ * message: "Command fail"
57
+ * };
58
+ *
59
+ * system.run(() => {
60
+ * world.sendMessage(`Running Test: ${value}`);
61
+ * });
62
+ *
63
+ * return {
64
+ * status: CustomCommandStatus.Success,
65
+ * message: "Completed Test"
66
+ * };
67
+ * }
68
+ * }
69
+ * ```
70
+ */
71
+ export abstract class ICustomCommand implements CustomCommand {
72
+
73
+ /** Command identifier (e.g. `bf_sd:test_cmd`) */
74
+ public abstract name: string;
75
+
76
+ /** Short description shown in help */
77
+ public abstract description: string;
78
+
79
+ /** Required permission level to execute */
80
+ public abstract permissionLevel: CommandPermissionLevel;
81
+
82
+ /** Whether cheats must be enabled */
83
+ public cheatsRequired?: boolean;
84
+
85
+ /** Required command parameters */
86
+ public mandatoryParameters?: CustomCommandParameter[];
87
+
88
+ /** Optional command parameters */
89
+ public optionalParameters?: CustomCommandParameter[];
90
+
91
+ /**
92
+ * Executes the command.
93
+ * @param origin Command execution context
94
+ * @param args Parsed command arguments
95
+ */
96
+ public abstract execute(
97
+ origin: CustomCommandOrigin,
98
+ ...args: any[]
99
+ ): CustomCommandResult | undefined;
100
+ };
101
+
102
+ /** Internal list of registered command instances */
103
+ export const customCommands: ICustomCommand[] = [];
104
+
105
+ /**
106
+ * Registers a command class.
107
+ * Can be used as a decorator: `@RegisterCustomCommand`.
108
+ */
109
+ export function RegisterCustomCommand<
110
+ T extends { new(): ICustomCommand }
111
+ >(customCommand: T): void {
112
+ const instance = new customCommand();
113
+ customCommands.push(instance);
114
+ };
115
+ }
116
+
117
+ system.beforeEvents.startup.subscribe(
118
+ (event: StartupEvent) => {
119
+ const { customCommandRegistry } = event;
120
+ for (const command of Command.customCommands) {
121
+ customCommandRegistry.registerCommand(command, command.execute);
122
+ };
123
+ },
124
+ );
package/util/Form.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { Player, RawMessage } from "@minecraft/server";
2
+ import { ActionFormResponse, ModalFormResponse } from "@minecraft/server-ui";
3
+ declare class Form {
4
+ readonly isOccupied: (player: Player) => boolean;
5
+ }
6
+ export interface Button {
7
+ indexId: string | number;
8
+ text: string | RawMessage;
9
+ iconPath?: string;
10
+ }
11
+ export interface FormActionData {
12
+ response: ActionFormResponse;
13
+ indexId?: string | number;
14
+ }
15
+ export declare class ActionForm extends Form {
16
+ private readonly actionForm;
17
+ private buttonMap;
18
+ private buttons;
19
+ private titleText;
20
+ private bodyText;
21
+ title(title: string | RawMessage): void;
22
+ body(body: string | RawMessage): void;
23
+ button(indexId: string, text: string | RawMessage, iconPath?: string): void;
24
+ showForm(player: Player): Promise<FormActionData>;
25
+ private generateButtons;
26
+ }
27
+ export interface TextField {
28
+ indexId: string;
29
+ label: string | RawMessage;
30
+ placeholder: string | RawMessage;
31
+ defaultValue?: string;
32
+ }
33
+ export interface Dropdown {
34
+ indexId: string;
35
+ label: string | RawMessage;
36
+ options: string[];
37
+ defaultValueIndex?: number;
38
+ }
39
+ export interface Slider {
40
+ indexId: string;
41
+ label: string | RawMessage;
42
+ min: number;
43
+ max: number;
44
+ step: number;
45
+ defaultValue?: number;
46
+ }
47
+ export interface Toggle {
48
+ indexId: string;
49
+ label: string | RawMessage;
50
+ defaultValue?: boolean;
51
+ }
52
+ export type FormValue = string | boolean | number | undefined;
53
+ export interface FormModalData {
54
+ response: ModalFormResponse;
55
+ indexMap?: Map<string, FormValue>;
56
+ }
57
+ export declare class ModalForm extends Form {
58
+ private readonly modalForm;
59
+ private widgitMap;
60
+ private indexMap;
61
+ private widgets;
62
+ title(title: string | RawMessage): void;
63
+ label(label: string | RawMessage): void;
64
+ header(header: string | RawMessage): void;
65
+ divider(): void;
66
+ textField(indexId: string, label: string | RawMessage, placeholder: string | RawMessage, defaultValue?: string): void;
67
+ dropdown(indexId: string, label: string | RawMessage, options: string[], defaultValueIndex?: number): void;
68
+ slider(indexId: string, label: string | RawMessage, min: number, max: number, step: number, defaultValue?: number): void;
69
+ toggle(indexId: string, label: string | RawMessage, defaultValue?: boolean): void;
70
+ showForm(player: Player): Promise<FormModalData>;
71
+ private processWidgets;
72
+ private processValues;
73
+ }
74
+ export {};
75
+ //# sourceMappingURL=Form.d.ts.map
package/util/Form.ts ADDED
@@ -0,0 +1,246 @@
1
+ /*
2
+ **************************************************
3
+ Copyright (c) Block Factory - All rights reserved.
4
+ **************************************************
5
+ Author: Donthedev <https://github.com/voxeldon>
6
+ **************************************************
7
+ */
8
+ import { Player, RawMessage } from "@minecraft/server";
9
+ import { ActionFormData, ActionFormResponse, ModalFormData, ModalFormDataDropdownOptions, ModalFormDataSliderOptions, ModalFormDataTextFieldOptions, ModalFormDataToggleOptions, ModalFormResponse } from "@minecraft/server-ui";
10
+
11
+ const occupiedPlayers: Set<string> = new Set();
12
+
13
+ class Form {
14
+ public readonly isOccupied = ((player: Player): boolean => {
15
+ return occupiedPlayers.has(player.id);
16
+ })
17
+ }
18
+
19
+ export interface Button {
20
+ indexId: string | number;
21
+ text: string | RawMessage;
22
+ iconPath?: string;
23
+ }
24
+
25
+ export interface FormActionData {
26
+ response: ActionFormResponse;
27
+ indexId?: string | number;
28
+ }
29
+
30
+ export class ActionForm extends Form {
31
+ private readonly actionForm: ActionFormData = new ActionFormData();
32
+ private buttonMap: Map<number, string | number> = new Map<number, string | number>();
33
+ private buttons: Button[] = [];
34
+ private titleText: string | RawMessage | undefined;
35
+ private bodyText: string | RawMessage | undefined;
36
+
37
+ public title(title: string | RawMessage): void {
38
+ this.titleText = title;
39
+ }
40
+
41
+ public body(body: string | RawMessage): void {
42
+ this.bodyText = body;
43
+ }
44
+
45
+ public button(indexId: string, text: string | RawMessage, iconPath?: string): void {
46
+ this.buttons.push({ indexId, text, iconPath });
47
+ }
48
+
49
+ public async showForm(player: Player): Promise<FormActionData> {
50
+ if (this.titleText === undefined || this.bodyText === undefined) {
51
+ throw Error('Title and body must be set before showing the form');
52
+ }
53
+ this.actionForm.title(this.titleText);
54
+ this.actionForm.body(this.bodyText);
55
+ this.generateButtons();
56
+
57
+ occupiedPlayers.add(player.id);
58
+
59
+ const response: ActionFormResponse = await this.actionForm.show(player).finally(() => {
60
+ occupiedPlayers.delete(player.id);
61
+ });
62
+
63
+ const return_data: FormActionData = {
64
+ response: response,
65
+ indexId: undefined
66
+ };
67
+ if (response.selection !== undefined && response.selection !== null) {
68
+ const selection: string | number | undefined = this.buttonMap.get(response.selection);
69
+ return_data.indexId = selection;
70
+ }
71
+
72
+ return return_data;
73
+ }
74
+
75
+ private generateButtons() {
76
+ let buttonIndex: number = 0;
77
+ this.buttons.forEach(button => {
78
+ if (button.iconPath) {
79
+ this.actionForm.button(button.text, button.iconPath);
80
+ } else {
81
+ this.actionForm.button(button.text);
82
+ }
83
+ this.buttonMap.set(buttonIndex, button.indexId);
84
+ buttonIndex++;
85
+ });
86
+ }
87
+ }
88
+
89
+ enum WidgetType {
90
+ TextField = 'textField',
91
+ Dropdown = 'dropdown',
92
+ Slider = 'slider',
93
+ Toggle = 'toggle'
94
+ }
95
+
96
+ interface Widget {
97
+ typeId: string,
98
+ widget: any
99
+ };
100
+
101
+ export interface TextField {
102
+ indexId: string,
103
+ label: string | RawMessage,
104
+ placeholder: string | RawMessage,
105
+ defaultValue?: string
106
+ }
107
+
108
+ export interface Dropdown {
109
+ indexId: string,
110
+ label: string | RawMessage,
111
+ options: string[],
112
+ defaultValueIndex?: number
113
+ }
114
+
115
+ export interface Slider {
116
+ indexId: string,
117
+ label: string | RawMessage,
118
+ min: number,
119
+ max: number,
120
+ step: number,
121
+ defaultValue?: number
122
+ }
123
+
124
+ export interface Toggle {
125
+ indexId: string,
126
+ label: string | RawMessage,
127
+ defaultValue?: boolean
128
+ }
129
+
130
+ export type FormValue = string | boolean | number | undefined;
131
+
132
+ export interface FormModalData {
133
+ response: ModalFormResponse;
134
+ indexMap?: Map<string, FormValue>;
135
+ }
136
+
137
+ export class ModalForm extends Form {
138
+ private readonly modalForm: ModalFormData = new ModalFormData();
139
+ private widgitMap: Map<number, string> = new Map<number, string>();
140
+ private indexMap: Map<string, FormValue> = new Map<string, FormValue>();
141
+ private widgets: Widget[] = [];
142
+
143
+ public title(title: string | RawMessage): void {
144
+ this.modalForm.title(title);
145
+ }
146
+
147
+ public label(label: string | RawMessage): void {
148
+ this.modalForm.label(label);
149
+ }
150
+
151
+ public header(header: string | RawMessage): void {
152
+ this.modalForm.header(header);
153
+ }
154
+
155
+ public divider(): void {
156
+ this.modalForm.divider();
157
+ }
158
+
159
+ public textField(indexId: string, label: string | RawMessage, placeholder: string | RawMessage, defaultValue?: string): void {
160
+ this.widgets.push({ typeId: WidgetType.TextField, widget: { indexId, label, placeholder, defaultValue } });
161
+ }
162
+ public dropdown(indexId: string, label: string | RawMessage, options: string[], defaultValueIndex?: number): void {
163
+ this.widgets.push({ typeId: WidgetType.Dropdown, widget: { indexId, label, options, defaultValueIndex } });
164
+ }
165
+
166
+ public slider(indexId: string, label: string | RawMessage, min: number, max: number, step: number, defaultValue?: number): void {
167
+ this.widgets.push({ typeId: WidgetType.Slider, widget: { indexId, label, min, max, step, defaultValue } });
168
+ }
169
+
170
+ public toggle(indexId: string, label: string | RawMessage, defaultValue?: boolean): void {
171
+ this.widgets.push({ typeId: WidgetType.Toggle, widget: { indexId, label, defaultValue } });
172
+ }
173
+
174
+ public async showForm(player: Player): Promise<FormModalData> {
175
+ this.processWidgets();
176
+ occupiedPlayers.add(player.id);
177
+
178
+ const response: ModalFormResponse = await this.modalForm.show(player).finally(() => {
179
+ occupiedPlayers.delete(player.id);
180
+ });
181
+
182
+ const return_data: FormModalData = {
183
+ response: response,
184
+ indexMap: undefined
185
+ }
186
+
187
+ if (response.formValues !== null && response.formValues !== undefined) {
188
+ this.processValues(response.formValues);
189
+ return_data.indexMap = this.indexMap;
190
+ }
191
+
192
+ return return_data;
193
+ }
194
+
195
+ private processWidgets(): void {
196
+ let widgitIndex: number = 0;
197
+
198
+ for (const widget of this.widgets) {
199
+ if (widget.typeId === WidgetType.TextField) {
200
+ const textField: TextField = widget.widget;
201
+ const options: ModalFormDataTextFieldOptions = {
202
+ defaultValue: textField?.defaultValue
203
+ };
204
+ this.modalForm.textField(textField.label, textField.placeholder, options);
205
+ this.widgitMap.set(widgitIndex, textField.indexId);
206
+ }
207
+ else if (widget.typeId === WidgetType.Dropdown) {
208
+ const dropdown: Dropdown = widget.widget;
209
+ const options: ModalFormDataDropdownOptions = {
210
+ defaultValueIndex: dropdown?.defaultValueIndex
211
+ };
212
+ this.modalForm.dropdown(dropdown.label, dropdown.options, options);
213
+ this.widgitMap.set(widgitIndex, dropdown.indexId);
214
+
215
+ }
216
+ else if (widget.typeId === WidgetType.Slider) {
217
+ const slider: Slider = widget.widget;
218
+ const options: ModalFormDataSliderOptions = {
219
+ defaultValue: slider?.defaultValue,
220
+ valueStep: slider.step
221
+ };
222
+ this.modalForm.slider(slider.label, slider.min, slider.max, options);
223
+ this.widgitMap.set(widgitIndex, slider.indexId);
224
+ }
225
+ else if (widget.typeId === WidgetType.Toggle) {
226
+ const toggle: Toggle = widget.widget;
227
+ const options: ModalFormDataToggleOptions = {
228
+ defaultValue: toggle?.defaultValue
229
+ }
230
+ this.modalForm.toggle(toggle.label, options);
231
+ this.widgitMap.set(widgitIndex, toggle.indexId);
232
+ }
233
+ widgitIndex += 1;
234
+ }
235
+ }
236
+
237
+ private processValues(formValues: FormValue[]): void {
238
+ let widgitIndex: number = 0;
239
+ for (const i of formValues) {
240
+ const value: FormValue = i?.valueOf();
241
+ const indexId: string | undefined = this.widgitMap.get(widgitIndex);
242
+ if (indexId) this.indexMap.set(indexId, value);
243
+ widgitIndex++;
244
+ }
245
+ }
246
+ }
package/util/Math.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type { Vector2, Vector3 } from "@minecraft/server";
2
+
3
+ export declare namespace MathUtils {
4
+ function clamp(value: number, min: number, max: number): number;
5
+ function lerp(start: number, end: number, factor: number): number;
6
+ function toRadians(degrees: number): number;
7
+ function toDegrees(radians: number): number;
8
+ function fromRotation(rotation: Vector2): Vector3;
9
+ function rotateOffset(offset: Vector3, yawDegrees: number): Vector3;
10
+ function distance3D(pointA: Vector3, pointB: Vector3): number;
11
+ function randomInt(min: number, max: number): number;
12
+ function randomFloat(min: number, max: number): number;
13
+ }
14
+ //# sourceMappingURL=Math.d.ts.map
package/util/Math.ts ADDED
@@ -0,0 +1,68 @@
1
+ import { Vector2, Vector3 } from "@minecraft/server";
2
+
3
+ export namespace MathUtils {
4
+ /** Clamps a number between a minimum and maximum value */
5
+ export function clamp(value: number, min: number, max: number): number {
6
+ return Math.min(Math.max(value, min), max);
7
+ };
8
+
9
+ /** Linearly interpolates between two values based on a given factor (0 to 1) */
10
+ export function lerp(start: number, end: number, factor: number): number {
11
+ return start + (end - start) * clamp(factor, 0, 1);
12
+ };
13
+
14
+ /** Converts degrees to radians */
15
+ export function toRadians(degrees: number): number {
16
+ return degrees * (Math.PI / 180);
17
+ };
18
+
19
+ /** Converts radians to degrees */
20
+ export function toDegrees(radians: number): number {
21
+ return radians * (180 / Math.PI);
22
+ };
23
+
24
+ export function fromRotation(rotation: Vector2): Vector3 {
25
+ const rotationH = toRadians(rotation.y * -1);
26
+ const z0 = Math.cos(rotationH);
27
+ const x0 = Math.sin(rotationH);
28
+
29
+ const rotationV = toRadians(rotation.x * -1);
30
+ const h = Math.cos(rotationV);
31
+ const v = Math.sin(rotationV);
32
+
33
+ return {
34
+ x: h * x0,
35
+ y: v,
36
+ z: h * z0
37
+ };
38
+ };
39
+
40
+ export function rotateOffset(offset: Vector3, yawDegrees: number): Vector3 {
41
+ const yaw = -yawDegrees * Math.PI / 180;
42
+ const cos = Math.cos(yaw);
43
+ const sin = Math.sin(yaw);
44
+ return {
45
+ x: offset.x * cos - offset.z * sin,
46
+ y: offset.y,
47
+ z: offset.x * sin + offset.z * cos
48
+ };
49
+ };
50
+
51
+ /** Calculates the distance between two points in 3D space */
52
+ export function distance3D(pointA: Vector3, pointB: Vector3): number {
53
+ const dx = pointB.x - pointA.x;
54
+ const dy = pointB.y - pointA.y;
55
+ const dz = pointB.z - pointA.z;
56
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
57
+ };
58
+
59
+ /** Generates a random integer between min and max (inclusive) */
60
+ export function randomInt(min: number, max: number): number {
61
+ return Math.floor(Math.random() * (max - min + 1)) + min;
62
+ };
63
+
64
+ /** Generates a random float between min and max */
65
+ export function randomFloat(min: number, max: number): number {
66
+ return Math.random() * (max - min) + min;
67
+ };
68
+ }
@@ -0,0 +1,33 @@
1
+ import type { RawMessage } from "@minecraft/server";
2
+
3
+ export declare namespace RawText {
4
+ export const TEXT: (value: string) => RawMessage;
5
+ export const TRANSLATE: (key: string, ...params: string[]) => RawMessage;
6
+ export const SCORE: (name: string, objective: string) => RawMessage;
7
+ export const MESSAGE: (...rawText: RawMessage[]) => RawMessage;
8
+ export const FORMAT: {
9
+ DarkRed: RawMessage;
10
+ Red: RawMessage;
11
+ Gold: RawMessage;
12
+ Yellow: RawMessage;
13
+ Green: RawMessage;
14
+ Lime: RawMessage;
15
+ Aqua: RawMessage;
16
+ Cyan: RawMessage;
17
+ DarkBlue: RawMessage;
18
+ Blue: RawMessage;
19
+ Magenta: RawMessage;
20
+ Purple: RawMessage;
21
+ White: RawMessage;
22
+ Gray: RawMessage;
23
+ DarkGray: RawMessage;
24
+ Black: RawMessage;
25
+ Obfuscated: RawMessage;
26
+ Bold: RawMessage;
27
+ StrikeThrough: RawMessage;
28
+ Italic: RawMessage;
29
+ Reset: RawMessage;
30
+ NewLine: RawMessage;
31
+ };
32
+ }
33
+ //# sourceMappingURL=RawText.d.ts.map
@@ -0,0 +1,76 @@
1
+ import { RawMessage } from '@minecraft/server';
2
+
3
+ /**
4
+ * Utility helpers for building rawtext messages.
5
+ */
6
+ export namespace RawText {
7
+ /**
8
+ * Creates a simple text component.
9
+ * @param value - The text value.
10
+ * @returns Text component object.
11
+ */
12
+ export const TEXT = (value: string): RawMessage => ({
13
+ text: value
14
+ });
15
+
16
+ /**
17
+ * Creates a translation component with optional 'with' parameters.
18
+ * @param key - The translation key.
19
+ * @param params - The optional parameters for translation.
20
+ * @returns Translation component object.
21
+ */
22
+ export const TRANSLATE = (key: string, ...params: string[]): RawMessage => ({
23
+ translate: key,
24
+ with: params.length ? params : undefined
25
+ });
26
+
27
+ /**
28
+ * Creates a score component.
29
+ * @param name - The entity's name whose score is being displayed.
30
+ * @param objective - The name of the score objective.
31
+ * @returns Score component object.
32
+ */
33
+ export const SCORE = (name: string, objective: string): RawMessage => ({
34
+ score: {
35
+ name: name,
36
+ objective: objective
37
+ }
38
+ });
39
+
40
+ /**
41
+ * Wraps various text components into a rawtext structure.
42
+ * @param rawText - The raw text components.
43
+ * @returns Raw text object.
44
+ */
45
+ export const MESSAGE = (...rawText: RawMessage[]): RawMessage => ({
46
+ rawtext: rawText
47
+ });
48
+
49
+ /**
50
+ * Common formatting and color codes.
51
+ */
52
+ export const FORMAT = {
53
+ DarkRed: TEXT('§4'),
54
+ Red: TEXT('§c'),
55
+ Gold: TEXT('§6'),
56
+ Yellow: TEXT('§e'),
57
+ Green: TEXT('§2'),
58
+ Lime: TEXT('§a'),
59
+ Aqua: TEXT('§b'),
60
+ Cyan: TEXT('§3'),
61
+ DarkBlue: TEXT('§1'),
62
+ Blue: TEXT('§9'),
63
+ Magenta: TEXT('§d'),
64
+ Purple: TEXT('§5'),
65
+ White: TEXT('§f'),
66
+ Gray: TEXT('§7'),
67
+ DarkGray: TEXT('§8'),
68
+ Black: TEXT('§0'),
69
+ Obfuscated: TEXT('§k'),
70
+ Bold: TEXT('§l'),
71
+ StrikeThrough: TEXT('§m'),
72
+ Italic: TEXT('§o'),
73
+ Reset: TEXT('§r'),
74
+ NewLine: TEXT('\n')
75
+ }
76
+ }
@@ -0,0 +1,9 @@
1
+ export type Callback<T = void> = (data: T) => void;
2
+ export declare class Signal<T = void> {
3
+ private listeners;
4
+ connect(callback: Callback<T>): void;
5
+ disconnect(callback?: Callback<T>): boolean;
6
+ once(callback: Callback<T>): void;
7
+ emit(data: T): void;
8
+ }
9
+ //# sourceMappingURL=Signal.d.ts.map