Test mod
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
// what server scripts get, see "Server scripts" in MODS.md
|
||||
import type { EventSignal, Face, Id, ItemStack, ModInfo, Position } from "./shared.ts";
|
||||
|
||||
export type { EventSignal, Face, Id, ItemStack, ModInfo, Position } from "./shared.ts";
|
||||
|
||||
export interface ServerContext {
|
||||
mod: ModInfo;
|
||||
components: ComponentRegistry; // only during setup
|
||||
commands: CommandRegistry; // only during setup
|
||||
events: { before: ServerBeforeEvents; after: ServerAfterEvents };
|
||||
system: System;
|
||||
world: ServerWorld;
|
||||
players: PlayerList;
|
||||
containers: ContainerApi;
|
||||
recipes: RecipeApi;
|
||||
ui: ServerUi;
|
||||
net: ServerNet;
|
||||
storage: ModStorage;
|
||||
log(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
// components
|
||||
|
||||
export interface BlockRef {
|
||||
readonly id: Id;
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
readonly z: number;
|
||||
// tile data, any json value. saved with the world, never sent to clients
|
||||
// deno-lint-ignore no-explicit-any
|
||||
data: any;
|
||||
}
|
||||
|
||||
// P is the component's params from the block json
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export interface BlockComponent<P = any> {
|
||||
on_create?(block: BlockRef, params: P): void;
|
||||
on_break?(block: BlockRef, params: P, player: Player | undefined): void;
|
||||
on_click?(block: BlockRef, params: P, player: Player): void;
|
||||
// return true if it did something, so no block gets placed
|
||||
on_interact?(block: BlockRef, params: P, player: Player): boolean;
|
||||
on_tick?(block: BlockRef, params: P, dt: number): void;
|
||||
on_second?(block: BlockRef, params: P, dt: number): void;
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export interface ItemComponent<P = any> {
|
||||
on_create?(item: ItemStack, params: P): void;
|
||||
get_lore?(item: ItemStack, params: P): string;
|
||||
on_use?(item: ItemStack, params: P, player: Player): void;
|
||||
}
|
||||
|
||||
export interface ComponentRegistry {
|
||||
register_block<P>(id: Id, component: BlockComponent<P>): void;
|
||||
register_item<P>(id: Id, component: ItemComponent<P>): void;
|
||||
}
|
||||
|
||||
// commands
|
||||
|
||||
export interface Command {
|
||||
description: string;
|
||||
usage: string;
|
||||
run(args: string[], player: Player): void;
|
||||
}
|
||||
|
||||
export interface CommandRegistry {
|
||||
register(name: string, command: Command): void;
|
||||
}
|
||||
|
||||
// events
|
||||
|
||||
export interface Cancelable {
|
||||
cancel: boolean;
|
||||
}
|
||||
|
||||
export interface BlockBreakEvent {
|
||||
readonly player: Player;
|
||||
readonly block: BlockRef;
|
||||
readonly item: ItemStack | undefined;
|
||||
}
|
||||
|
||||
export interface BlockPlaceEvent {
|
||||
readonly player: Player;
|
||||
readonly block: { readonly id: Id } & Position;
|
||||
readonly face: Face;
|
||||
readonly item: ItemStack | undefined;
|
||||
}
|
||||
|
||||
export interface BlockInteractEvent {
|
||||
readonly player: Player;
|
||||
readonly block: BlockRef;
|
||||
readonly item: ItemStack | undefined;
|
||||
}
|
||||
|
||||
export interface ChatSendEvent {
|
||||
readonly player: Player;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ServerBeforeEvents {
|
||||
block_break: EventSignal<BlockBreakEvent & Cancelable>;
|
||||
block_place: EventSignal<BlockPlaceEvent & Cancelable>;
|
||||
block_interact: EventSignal<BlockInteractEvent & Cancelable>;
|
||||
chat_send: EventSignal<ChatSendEvent & Cancelable>;
|
||||
}
|
||||
|
||||
export interface ServerAfterEvents {
|
||||
block_break: EventSignal<BlockBreakEvent>;
|
||||
block_place: EventSignal<BlockPlaceEvent>;
|
||||
block_interact: EventSignal<BlockInteractEvent>;
|
||||
chat_send: EventSignal<Readonly<ChatSendEvent>>;
|
||||
player_join: EventSignal<{ readonly player: Player }>;
|
||||
player_leave: EventSignal<{ readonly player: Player }>;
|
||||
server_start: EventSignal<Record<never, never>>;
|
||||
tick: EventSignal<{ readonly dt: number }>;
|
||||
}
|
||||
|
||||
// world and players
|
||||
|
||||
export interface ServerWorld {
|
||||
get_block(x: number, y: number, z: number): Id | undefined; // undefined when the chunk isn't loaded
|
||||
set_block(x: number, y: number, z: number, id: Id): boolean; // runs on_break / on_create, synced to everyone
|
||||
get_state(x: number, y: number, z: number, name: string): number | undefined;
|
||||
set_state(x: number, y: number, z: number, name: string, value: number): boolean;
|
||||
get_block_data<T>(x: number, y: number, z: number): T | undefined;
|
||||
is_loaded(x: number, z: number): boolean;
|
||||
readonly seed: string;
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly position: Readonly<Position>;
|
||||
readonly inventory: Container; // 36 slots, hotbar is 0-8
|
||||
readonly selected_slot: number;
|
||||
readonly held_item: ItemStack | undefined;
|
||||
give_item(id: Id, count?: number, data?: unknown): void;
|
||||
send_message(text: string): void;
|
||||
teleport(x: number, y: number, z: number): void;
|
||||
}
|
||||
|
||||
export interface PlayerList {
|
||||
all(): Player[];
|
||||
get(id: string): Player | undefined;
|
||||
by_name(name: string): Player | undefined;
|
||||
}
|
||||
|
||||
export interface System {
|
||||
run_timeout(fn: () => void, ticks: number): number;
|
||||
run_interval(fn: () => void, ticks: number): number;
|
||||
clear_run(handle: number): void;
|
||||
readonly current_tick: number;
|
||||
}
|
||||
|
||||
// small key value store per mod, saved with the world
|
||||
export interface ModStorage {
|
||||
get<T>(key: string): T | undefined;
|
||||
set(key: string, value: unknown): void; // json values only
|
||||
delete(key: string): void;
|
||||
}
|
||||
|
||||
// containers and recipes
|
||||
|
||||
export interface Container {
|
||||
readonly id: string;
|
||||
readonly size: number;
|
||||
get(slot: number): ItemStack | undefined;
|
||||
set(slot: number, item: ItemStack | undefined): void;
|
||||
add(item: ItemStack): ItemStack | undefined; // returns what didn't fit
|
||||
on_change(fn: (slot: number) => void): () => void;
|
||||
}
|
||||
|
||||
export interface ContainerApi {
|
||||
create(size: number): Container; // saved with the world
|
||||
get(id: string): Container | undefined;
|
||||
delete(id: string): void;
|
||||
}
|
||||
|
||||
export interface RecipeApi {
|
||||
furnace_result(input: Id): { output: ItemStack; cook_time: number } | undefined;
|
||||
fuel_value(item: Id): number; // burn time in ticks, 0 if it isn't fuel
|
||||
is_fuel(item: Id): boolean;
|
||||
is_smeltable(item: Id): boolean;
|
||||
}
|
||||
|
||||
// guis
|
||||
|
||||
export interface ActionForm {
|
||||
title: string;
|
||||
body?: string;
|
||||
buttons: { text: string; icon?: Id }[];
|
||||
}
|
||||
|
||||
export interface MessageForm {
|
||||
title: string;
|
||||
body: string;
|
||||
buttons: [string, string];
|
||||
}
|
||||
|
||||
export type FormField =
|
||||
| { type: "toggle"; label: string; default?: boolean }
|
||||
| { type: "slider"; label: string; min: number; max: number; step?: number; default?: number }
|
||||
| { type: "dropdown"; label: string; options: string[]; default?: number }
|
||||
| { type: "text"; label: string; placeholder?: string; default?: string; max_length?: number };
|
||||
|
||||
export interface ModalForm {
|
||||
title: string;
|
||||
fields: FormField[];
|
||||
}
|
||||
|
||||
export type FormResult<T> = ({ canceled: true } & Partial<T>) | ({ canceled: false } & T);
|
||||
|
||||
export type SlotFilter = "smeltable" | "fuel" | ((item: ItemStack) => boolean);
|
||||
|
||||
export interface ContainerScreenOptions {
|
||||
title: string;
|
||||
container: Container;
|
||||
// x and y in slot units
|
||||
layout: { slot: number; x: number; y: number; filter?: SlotFilter; output_only?: boolean }[];
|
||||
player_inventory?: boolean;
|
||||
bars?: { id: string; x: number; y: number; texture: Id }[];
|
||||
labels?: { x: number; y: number; property: string }[];
|
||||
}
|
||||
|
||||
export interface ScreenHandle<Props = unknown> {
|
||||
readonly player: Player;
|
||||
set_property(id: string, value: number): void;
|
||||
update(props: Props): void; // custom screens only
|
||||
close(): void;
|
||||
on_close(fn: () => void): void;
|
||||
}
|
||||
|
||||
export interface ServerUi {
|
||||
message_form(player: Player, form: MessageForm): Promise<FormResult<{ selection: 0 | 1 }>>;
|
||||
action_form(player: Player, form: ActionForm): Promise<FormResult<{ selection: number }>>;
|
||||
modal_form(player: Player, form: ModalForm): Promise<FormResult<{ values: (boolean | number | string)[] }>>;
|
||||
open_container(player: Player, options: ContainerScreenOptions): ScreenHandle;
|
||||
open_screen<Props>(
|
||||
player: Player,
|
||||
id: Id,
|
||||
props: Props,
|
||||
options?: { containers?: Record<string, Container> },
|
||||
): ScreenHandle<Props>;
|
||||
}
|
||||
|
||||
// mod channels
|
||||
|
||||
export interface ServerNet {
|
||||
on<T = unknown>(channel: Id, handler: (player: Player, data: T) => void): void;
|
||||
send(player: Player, channel: Id, data: unknown): void;
|
||||
broadcast(channel: Id, data: unknown): void;
|
||||
}
|
||||
Reference in New Issue
Block a user