UNPKG

hytopia

Version:

The HYTOPIA SDK makes it easy for developers to create massively multiplayer games using JavaScript or TypeScript.

1,671 lines (1,581 loc) 375 kB
import type { AnyPacket } from '@hytopia.com/server-protocol'; import type { ErrorEvent as ErrorEvent_2 } from 'ws'; import EventEmitter from 'eventemitter3'; import http from 'http'; import type { InputSchema } from '@hytopia.com/server-protocol'; import type { LobbyMembershipDto } from '@hytopia.com/creative-lib/dist/impl/getSession'; import protocol from '@hytopia.com/server-protocol'; import RAPIER from '@dimforge/rapier3d-simd-compat'; import { SdpMatrix3 } from '@dimforge/rapier3d-simd-compat'; import * as Sentry from '@sentry/node'; import type { Socket } from 'net'; import { WebSocket as WebSocket_2 } from 'ws'; import type { WebTransportSessionImpl } from '@fails-components/webtransport/dist/lib/types'; /** * Manages the assets library and synchronization of assets * to the local assets directory in development. * * When to use: pulling assets from the shared library during local development. * Do NOT use for: production asset loading; the library is disabled in production. * * @remarks * The AssetsLibrary is created internally as a global * singleton accessible via `AssetsLibrary.instance`. * * Assets automatically sync to local assets in development mode the first * time an asset in the library is requested by the client. You generally do * not need to call `AssetsLibrary.syncAsset` unless you have a specific reason to. * * @example * ```typescript * import { AssetsLibrary } from 'hytopia'; * * const assetsLibrary = AssetsLibrary.instance; * assetsLibrary.syncAsset('assets/models/player.gltf'); * ``` * * **Category:** Assets * @public */ export declare class AssetsLibrary { /** * The global AssetsLibrary instance as a singleton. * * **Category:** Assets */ static readonly instance: AssetsLibrary; /** * The path to the assets library package. Null if assets library is not available. * * **Category:** Assets */ static readonly assetsLibraryPath: string | null; /** * Synchronizes an asset from the assets library to the local assets directory. * * @remarks * Syncs an asset from the assets library to local assets in development. * The assets library is unavailable in production, so assets must be local to the project. * * @param assetPath - The path of the asset to copy to local assets. * * **Requires:** Assets library must be available (development only). * * **Side effects:** Writes files into the local `assets/` directory. * * **Category:** Assets */ syncAsset(assetPath: string): void; } /** * Represents a audio playback in a world. * * @remarks * Audio instances are created directly as instances. * They support a variety of configuration options through * the `AudioOptions` constructor argument. * * <h2>Events</h2> * * This class is an EventRouter, and instances of it emit * events with payloads listed under `AudioEventPayloads` * * @example * ```typescript * (new Audio({ * uri: 'music/song.mp3', // relative to the server's assets directory in the project root, resolves to assets/music/song.mp3 * loop: true, * volume: 0.5, * })).play(world); * ``` * * @eventProperty * * **Category:** Audio * @public */ export declare class Audio extends EventRouter implements protocol.Serializable { /** * @param options - The options for the Audio instance. */ constructor(options: AudioOptions); /** The unique identifier for the audio. */ get id(): number | undefined; /** The entity to which the audio is attached if explicitly set. */ get attachedToEntity(): Entity | undefined; /** The cutoff distance where the audio will be reduced to 0 volume. */ get cutoffDistance(): number; /** The duration of the audio in seconds if explicitly set. */ get duration(): number | undefined; /** The detune of the audio in cents if explicitly set. */ get detune(): number | undefined; /** The amount of distortion to apply to the audio if explicitly set. */ get distortion(): number | undefined; /** Whether the audio is looped. */ get loop(): boolean; /** The offset time in seconds from which the audio should start playing if explicitly set. */ get offset(): number | undefined; /** Whether the audio has loaded into the world. Audio is loaded the first time play() is called. */ get isLoaded(): boolean; /** Whether the audio is currently playing. */ get isPlaying(): boolean; /** Whether the audio is positional (Entity or position attached). */ get isPositional(): boolean; /** The position of the audio in the world if explicitly set. */ get position(): Vector3Like | undefined; /** The playback rate of the audio if explicitly set. */ get playbackRate(): number | undefined; /** The reference distance of the audio if explicitly set. */ get referenceDistance(): number; /** The server tick at which the audio started playing. */ get startTick(): number | undefined; /** The URI of the audio asset. */ get uri(): string; /** The volume of the audio if explicitly set. */ get volume(): number | undefined; /** The world the audio is in if already loaded. */ get world(): World | undefined; /** * Plays or resumes the audio. * * @param world - The world to play the audio in. * @param restart - If true, the audio will restart from the beginning if it is already playing. */ play(world: World, restart?: boolean): void; /** * Pauses the audio. */ pause(): void; /** * Sets the entity to which the audio is attached, following its position. * * @remarks * **Clears position:** Setting an attached entity clears any previously set `position`. * Audio can be entity-attached or position-based, not both. * * @param entity - The entity to attach the Audio to. */ setAttachedToEntity(entity: Entity): void; /** * Sets the cutoff distance of the audio. * * @remarks * The cutoff distance defines the maximum range at which the audio can be heard. * Beyond this distance, the audio volume becomes zero. As the listener moves * from the reference distance toward the cutoff distance, the volume decreases * linearly, providing a natural spatial audio experience with smooth volume * falloff based on distance. * * @param cutoffDistance - The cutoff distance. */ setCutoffDistance(cutoffDistance: number): void; /** * Sets the detune of the audio. * * @param detune - The detune in cents. */ setDetune(detune: number): void; /** * Sets the distortion of the audio. * * @param distortion - The distortion amount. */ setDistortion(distortion: number): void; /** * Sets the position of the audio. * * @remarks * **Detaches from entity:** Setting a position clears any `attachedToEntity`. * Audio can be position-based or entity-attached, not both. * * @param position - The position in the world. */ setPosition(position: Vector3Like): void; /** * Sets the playback rate of the audio. * * @param playbackRate - The playback rate. */ setPlaybackRate(playbackRate: number): void; /** * Sets the reference distance of the audio. * * @remarks * The reference distance defines the range within which the audio plays at * full volume. When a listener is within this distance from the audio source, * they will hear the sound at its maximum volume. Beyond this distance, the * volume decreases linearly until reaching the cutoff distance, where the * sound becomes inaudible. This creates a natural spatial audio experience * with smooth volume falloff based on distance. * * @param referenceDistance - The reference distance. */ setReferenceDistance(referenceDistance: number): void; /** * Sets the volume of the audio. * * @param volume - The volume level. */ setVolume(volume: number): void; } /** * Event types an Audio instance can emit. * * See `AudioEventPayloads` for the payloads. * * **Category:** Events * @public */ export declare enum AudioEvent { PAUSE = "AUDIO.PAUSE", PLAY = "AUDIO.PLAY", PLAY_RESTART = "AUDIO.PLAY_RESTART", SET_ATTACHED_TO_ENTITY = "AUDIO.SET_ATTACHED_TO_ENTITY", SET_CUTOFF_DISTANCE = "AUDIO.SET_CUTOFF_DISTANCE", SET_DETUNE = "AUDIO.SET_DETUNE", SET_DISTORTION = "AUDIO.SET_DISTORTION", SET_POSITION = "AUDIO.SET_POSITION", SET_PLAYBACK_RATE = "AUDIO.SET_PLAYBACK_RATE", SET_REFERENCE_DISTANCE = "AUDIO.SET_REFERENCE_DISTANCE", SET_VOLUME = "AUDIO.SET_VOLUME" } /** * Event payloads for Audio emitted events. * * **Category:** Events * @public */ export declare interface AudioEventPayloads { /** Emitted when the audio is paused. */ [AudioEvent.PAUSE]: { audio: Audio; }; /** Emitted when the audio is played. */ [AudioEvent.PLAY]: { audio: Audio; }; /** Emitted when the audio is restarted. */ [AudioEvent.PLAY_RESTART]: { audio: Audio; }; /** Emitted when the audio is attached to an entity. */ [AudioEvent.SET_ATTACHED_TO_ENTITY]: { audio: Audio; entity: Entity | undefined; }; /** Emitted when the audio's cutoff distance is set. */ [AudioEvent.SET_CUTOFF_DISTANCE]: { audio: Audio; cutoffDistance: number; }; /** Emitted when the audio's detune is set. */ [AudioEvent.SET_DETUNE]: { audio: Audio; detune: number; }; /** Emitted when the audio's distortion is set. */ [AudioEvent.SET_DISTORTION]: { audio: Audio; distortion: number; }; /** Emitted when the audio's position is set. */ [AudioEvent.SET_POSITION]: { audio: Audio; position: Vector3Like; }; /** Emitted when the audio's playback rate is set. */ [AudioEvent.SET_PLAYBACK_RATE]: { audio: Audio; playbackRate: number; }; /** Emitted when the audio's reference distance is set. */ [AudioEvent.SET_REFERENCE_DISTANCE]: { audio: Audio; referenceDistance: number; }; /** Emitted when the audio's volume is set. */ [AudioEvent.SET_VOLUME]: { audio: Audio; volume: number; }; } /** * Manages audio instances in a world. * * When to use: querying or bulk-controlling audio in a specific world. * Do NOT use for: individual playback configuration; use `Audio` instances. * * @remarks * The AudioManager is created internally per `World` instance. * Audio is loaded on first `Audio.play`; this manager tracks loaded instances. * Pattern: call `AudioManager.unregisterEntityAttachedAudios` when despawning entities with positional audio. * * @example * ```typescript * // Stop all audio in the world * const audioManager = world.audioManager; * audioManager.getAllAudios().forEach(audio => audio.pause()); * ``` * * **Category:** Audio * @public */ export declare class AudioManager { /** * The world the audio manager is for. * * **Category:** Audio */ get world(): World; /** * Retrieves all loaded audio instances for the world. * * @returns An array of audio instances. * * **Category:** Audio */ getAllAudios(): Audio[]; /** * Retrieves all loaded audio instances attached to a specific entity. * * Use for: cleanup when despawning an entity with positional audio. * * @param entity - The entity to get attached audio instances for. * @returns An array of audio instances. * * **Requires:** Entity should belong to this world for meaningful results. * * @see `AudioManager.unregisterEntityAttachedAudios` * * **Category:** Audio */ getAllEntityAttachedAudios(entity: Entity): Audio[]; /** * Retrieves all looped audio instances for the world. * * @returns An array of audio instances. * * @see `AudioManager.getAllOneshotAudios` * * **Category:** Audio */ getAllLoopedAudios(): Audio[]; /** * Retrieves all oneshot (non-looped) audio instances for the world. * * @returns An array of audio instances. * * @see `AudioManager.getAllLoopedAudios` * * **Category:** Audio */ getAllOneshotAudios(): Audio[]; /** * Unregisters and stops an audio instance from the audio manager. * * Use for: explicit cleanup of one-shot or temporary sounds. * Do NOT use for: pausing/resuming; use `Audio.pause` or `Audio.play` instead. * * @remarks * **Pauses audio:** Calls `audio.pause()` before removing from the manager. * * @param audio - The audio instance to pause and unregister. * * **Requires:** Audio must be loaded (have an id) or an error is logged. * * **Side effects:** Pauses the audio and removes it from manager tracking. * * @see `AudioManager.unregisterEntityAttachedAudios` * * **Category:** Audio */ unregisterAudio(audio: Audio): void; /** * Unregisters and stops all audio instances attached to a specific entity. * * Use for: entity despawn or cleanup scenarios. * * @remarks * **Pauses all:** Calls `AudioManager.unregisterAudio` for each attached audio, which pauses them. * * @param entity - The entity to pause and unregister audio instances for. * * **Requires:** Entity should belong to this world for meaningful results. * * **Side effects:** Pauses and unregisters any attached audio instances. * * @see `AudioManager.getAllEntityAttachedAudios` * * **Category:** Audio */ unregisterEntityAttachedAudios(entity: Entity): void; } /** * Options for creating an Audio instance. * * Positional audio can be configured via `AudioOptions.attachedToEntity` or `AudioOptions.position`. * * Use for: configuring audio before calling `Audio.play`. * Do NOT use for: runtime updates after playback starts; use `Audio.set*` methods. * * **Category:** Audio * @public */ export declare interface AudioOptions { /** If set, audio playback will follow the entity's position. */ attachedToEntity?: Entity; /** The cutoff distance between the audio source and the listener where the audio will be reduced to 0 volume. Must be greater than reference distance. Defaults to reference distance + 10. */ cutoffDistance?: number; /** The duration of the audio in seconds. Defaults to full duration. */ duration?: number; /** The detuning of the audio in cents. */ detune?: number; /** The amount of distortion to apply to the audio. */ distortion?: number; /** Whether the audio should loop when it reaches the end. Defaults to false. */ loop?: boolean; /** The offset time in seconds from which the audio should start playing. */ offset?: number; /** The position in the world where the audio is played. */ position?: Vector3Like; /** The playback speed of the audio. Defaults to 1. */ playbackRate?: number; /** The maximum reference distance between the audio source and the listener where the audio will still be max volume. Defaults to 10. */ referenceDistance?: number; /** The URI or path to the audio asset to be played. */ uri: string; /** The volume level of the audio. Defaults to 0.5. */ volume?: number; } /** * The options for a ball collider. @public * * Use for: sphere-shaped colliders. * Do NOT use for: other shapes; use the matching collider option type. * * **Category:** Physics */ export declare interface BallColliderOptions extends BaseColliderOptions { shape: ColliderShape.BALL; /** * The radius of the ball collider. * * **Category:** Physics */ radius?: number; } /** * The base options for a collider. @public * * Use for: configuring colliders when creating entities or rigid bodies. * Do NOT use for: runtime changes; use `Collider` methods instead. * * **Category:** Physics */ export declare interface BaseColliderOptions { /** * The shape of the collider. * * **Category:** Physics */ shape: ColliderShape; /** * The bounciness of the collider. * * **Category:** Physics */ bounciness?: number; /** * The bounciness combine rule of the collider. * * **Category:** Physics */ bouncinessCombineRule?: CoefficientCombineRule; /** * The collision groups the collider belongs to. * * **Category:** Physics */ collisionGroups?: CollisionGroups; /** * Whether the collider is enabled. * * **Category:** Physics */ enabled?: boolean; /** * The flags of the collider if the shape is a trimesh * * **Category:** Physics */ flags?: number; /** * The friction of the collider. * * **Category:** Physics */ friction?: number; /** * The friction combine rule of the collider. * * **Category:** Physics */ frictionCombineRule?: CoefficientCombineRule; /** * Whether the collider is a sensor. * * **Category:** Physics */ isSensor?: boolean; /** * The mass of the collider. * * **Category:** Physics */ mass?: number; /** * The on collision callback for the collider. * * **Category:** Physics */ onCollision?: CollisionCallback; /** * The parent rigid body of the collider. * * **Category:** Physics */ parentRigidBody?: RigidBody; /** * The relative position of the collider. Relative to parent rigid body. * * **Category:** Physics */ relativePosition?: Vector3Like; /** * The relative rotation of the collider. Relative to parent rigid body. * * **Category:** Physics */ relativeRotation?: QuaternionLike; /** * The simulation the collider is in, if provided the collider will automatically be added to the simulation. * * **Category:** Physics */ simulation?: Simulation; /** * An arbitrary identifier tag of the collider. Useful for your own logic. * * **Category:** Physics */ tag?: string; } /** * A base class for entity controller implementations. * * When to use: implementing custom entity behavior and movement logic. * Do NOT use for: one-off entity changes; prefer direct entity APIs. * * @remarks * Controllers are typically one instance per entity, but can be shared across * entities if you manage state carefully. * * <h2>Lifecycle</h2> * * 1) `attach()` — called during `Entity` construction when a controller is provided. * 2) `spawn()` — called after the entity is added to the physics simulation. * 3) `tickWithPlayerInput()` — called each world tick for `PlayerEntity` before `tick()`. * 4) `tick()` — called each world tick before physics stepping. * 5) `detach()` → `despawn()` — called during `Entity.despawn`. * * <h2>Events</h2> * * This class is an EventRouter, and instances of it emit events with payloads listed under * `BaseEntityControllerEventPayloads`. * * **Category:** Controllers * @public */ export declare abstract class BaseEntityController extends EventRouter { /** * Override this method to handle the attachment of an entity * to your entity controller. * * @remarks * **Called by:** `Entity` constructor when a controller is provided in options. * * **Super call:** Call `super.attach(entity)` to emit the `ATTACH` event. * * @param entity - The entity to attach the controller to. * * **Category:** Controllers */ attach(entity: Entity): void; /** * Override this method to handle the despawn of an entity * from your entity controller. * * @remarks * **Called by:** `Entity.despawn()` after `detach()` is called. * * **Super call:** Call `super.despawn(entity)` to emit the `DESPAWN` event. * * @param entity - The entity being despawned. * * **Category:** Controllers */ despawn(entity: Entity): void; /** * Override this method to handle the detachment of an entity * from your entity controller. * * @remarks * **Called by:** `Entity.despawn()` before `despawn()` is called. * * **Super call:** Call `super.detach(entity)` to emit the `DETACH` event. * * @param entity - The entity being detached. * * **Category:** Controllers */ detach(entity: Entity): void; /** * Override this method to handle the spawning of an entity * to your entity controller. * * @remarks * **Called by:** `Entity.spawn()` after the entity is added to the physics simulation. * * **Super call:** Call `super.spawn(entity)` to emit the `SPAWN` event. * * @param entity - The entity being spawned. * * **Category:** Controllers */ spawn(entity: Entity): void; /** * Override this method to handle entity movements * based on player input for your entity controller. * * @remarks * **Called by:** `PlayerEntity.tick()` every tick when `isTickWithPlayerInputEnabled` is true. * Called before `tick()`. * * **Super call:** Call `super.tickWithPlayerInput(...)` to emit the `TICK_WITH_PLAYER_INPUT` event. * * @param entity - The player entity being ticked. * @param input - The current input state of the player. * @param cameraOrientation - The current camera orientation state of the player. * @param deltaTimeMs - The delta time in milliseconds since the last tick. * * **Category:** Controllers */ tickWithPlayerInput(entity: PlayerEntity, input: PlayerInput, cameraOrientation: PlayerCameraOrientation, deltaTimeMs: number): void; /** * Override this method to handle entity movements * based on your entity controller. * * @remarks * **Called by:** `Entity.tick()` every tick for non-environmental entities. * For `PlayerEntity`, this is called after `tickWithPlayerInput()`. * * **Super call:** Call `super.tick(entity, deltaTimeMs)` to emit the `TICK` event. * * @param entity - The entity being ticked. * @param deltaTimeMs - The delta time in milliseconds since the last tick. * * **Category:** Controllers */ tick(entity: Entity, deltaTimeMs: number): void; } /** * Event types a BaseEntityController instance can emit. * * See `BaseEntityControllerEventPayloads` for the payloads. * * **Category:** Events * @public */ export declare enum BaseEntityControllerEvent { ATTACH = "BASE_ENTITY_CONTROLLER.ATTACH", DESPAWN = "BASE_ENTITY_CONTROLLER.DESPAWN", DETACH = "BASE_ENTITY_CONTROLLER.DETACH", SPAWN = "BASE_ENTITY_CONTROLLER.SPAWN", TICK = "BASE_ENTITY_CONTROLLER.TICK", TICK_WITH_PLAYER_INPUT = "BASE_ENTITY_CONTROLLER.TICK_WITH_PLAYER_INPUT" } /** * Event payloads for BaseEntityController emitted events. * * **Category:** Events * @public */ export declare interface BaseEntityControllerEventPayloads { /** Emitted when an entity is attached to the controller. */ [BaseEntityControllerEvent.ATTACH]: { entity: Entity; }; /** Emitted when an entity is despawned. */ [BaseEntityControllerEvent.DESPAWN]: { entity: Entity; }; /** Emitted when an entity is detached from the controller. */ [BaseEntityControllerEvent.DETACH]: { entity: Entity; }; /** Emitted when an entity is spawned. */ [BaseEntityControllerEvent.SPAWN]: { entity: Entity; }; /** Emitted when an entity is ticked. */ [BaseEntityControllerEvent.TICK]: { entity: Entity; deltaTimeMs: number; }; /** Emitted when an entity is ticked with player input. */ [BaseEntityControllerEvent.TICK_WITH_PLAYER_INPUT]: { entity: PlayerEntity; input: PlayerInput; cameraOrientation: PlayerCameraOrientation; deltaTimeMs: number; }; } /** * The base options for an entity. * * Use for: common entity configuration shared by block and model entities. * Do NOT use for: runtime changes after spawn; use `Entity` setters instead. * * **Category:** Entities * @public */ export declare interface BaseEntityOptions { /** The entity controller to use for the entity. */ controller?: BaseEntityController; /** The emissive color of the entity. */ emissiveColor?: RgbColor; /** The emissive intensity of the entity. Use a value over 1 for brighter emissive effects. */ emissiveIntensity?: number; /** The opacity of the entity between 0 and 1. 0 is fully transparent, 1 is fully opaque. */ opacity?: number; /** The outline rendering options for the entity. */ outline?: Outline; /** Whether the entity is environmental, if true it will not invoke its tick function or change position. Defaults to false. */ isEnvironmental?: boolean; /** The parent entity of the entity, entities with a parent will ignore creating their own colliders. */ parent?: Entity; /** The name of the parent's node (if parent is a model entity) to attach the entity to. */ parentNodeName?: string; /** The interpolation time in milliseconds applied to position changes. */ positionInterpolationMs?: number; /** The rigid body options for the entity. */ rigidBodyOptions?: RigidBodyOptions; /** The interpolation time in milliseconds applied to rotation changes. */ rotationInterpolationMs?: number; /** An arbitrary identifier tag of the entity. Useful for your own logic. */ tag?: string; /** The tint color of the entity as a hex code. */ tintColor?: RgbColor; /** The name of the entity. */ name?: string; } /** * The base options for a rigid body. @public * * Use for: initial rigid body configuration when creating entities or bodies. * Do NOT use for: runtime changes; use `RigidBody` setter methods instead. * * **Category:** Physics */ export declare interface BaseRigidBodyOptions { /** * The type of the rigid body, defaults to `RigidBodyType.DYNAMIC`. * * **Category:** Physics */ type?: RigidBodyType; /** * The colliders of the rigid body, provided as `ColliderOptions`. * * **Category:** Physics */ colliders?: ColliderOptions[]; /** * Whether the rigid body is enabled. * * **Category:** Physics */ enabled?: boolean; /** * The position of the rigid body. * * **Category:** Physics */ position?: Vector3Like; /** * The rotation of the rigid body. * * **Category:** Physics */ rotation?: QuaternionLike; /** * The simulation the rigid body is in. If provided, the rigid body will be automatically added to the simulation. * * **Category:** Physics */ simulation?: Simulation; } /** * Represents a block in a world. * * When to use: reading block data from queries like raycasts or chunk lookups. * Do NOT use for: creating or placing blocks directly; use `ChunkLattice.setBlock`. * * @remarks * Instances are created internally and surfaced by API methods. * Block coordinates are **world coordinates** (global block grid), not local chunk coordinates. * * **Category:** Blocks * @public */ export declare class Block { /** * The global coordinate of the block. * * **Category:** Blocks */ readonly globalCoordinate: Vector3Like; /** * The block type of the block. * * **Category:** Blocks */ readonly blockType: BlockType; /** * Gets the most adjacent neighbor global coordinate of this block * based on a relative hit point, typically from a raycast. * * Use for: placing a new block on the face that was hit. * * @param hitPoint - The hit point on this block (global coordinates). * @returns The adjacent block coordinate in world space. * * **Category:** Blocks */ getNeighborGlobalCoordinateFromHitPoint(hitPoint: Vector3Like): Vector3Like; } /** * All valid block rotations, named as `{face pointing up}_{Y rotation degrees}`. * * N prefix = negative axis (e.g. `NZ_90` = -Z face up, rotated 90° around global Y). * * **Category:** Blocks * @public */ export declare const BLOCK_ROTATIONS: { readonly Y_0: { readonly enumIndex: 0; readonly matrix: readonly [1, 0, 0, 0, 1, 0, 0, 0, 1]; }; readonly Y_90: { readonly enumIndex: 1; readonly matrix: readonly [0, 0, -1, 0, 1, 0, 1, 0, 0]; }; readonly Y_180: { readonly enumIndex: 2; readonly matrix: readonly [-1, 0, 0, 0, 1, 0, 0, 0, -1]; }; readonly Y_270: { readonly enumIndex: 3; readonly matrix: readonly [0, 0, 1, 0, 1, 0, -1, 0, 0]; }; readonly NY_0: { readonly enumIndex: 4; readonly matrix: readonly [-1, 0, 0, 0, -1, 0, 0, 0, 1]; }; readonly NY_90: { readonly enumIndex: 5; readonly matrix: readonly [0, 0, -1, 0, -1, 0, -1, 0, 0]; }; readonly NY_180: { readonly enumIndex: 6; readonly matrix: readonly [1, 0, 0, 0, -1, 0, 0, 0, -1]; }; readonly NY_270: { readonly enumIndex: 7; readonly matrix: readonly [0, 0, 1, 0, -1, 0, 1, 0, 0]; }; readonly X_0: { readonly enumIndex: 8; readonly matrix: readonly [0, -1, 0, 1, 0, 0, 0, 0, 1]; }; readonly X_90: { readonly enumIndex: 9; readonly matrix: readonly [0, 0, -1, 1, 0, 0, 0, -1, 0]; }; readonly X_180: { readonly enumIndex: 10; readonly matrix: readonly [0, 1, 0, 1, 0, 0, 0, 0, -1]; }; readonly X_270: { readonly enumIndex: 11; readonly matrix: readonly [0, 0, 1, 1, 0, 0, 0, 1, 0]; }; readonly NX_0: { readonly enumIndex: 12; readonly matrix: readonly [0, 1, 0, -1, 0, 0, 0, 0, 1]; }; readonly NX_90: { readonly enumIndex: 13; readonly matrix: readonly [0, 0, -1, -1, 0, 0, 0, 1, 0]; }; readonly NX_180: { readonly enumIndex: 14; readonly matrix: readonly [0, -1, 0, -1, 0, 0, 0, 0, -1]; }; readonly NX_270: { readonly enumIndex: 15; readonly matrix: readonly [0, 0, 1, -1, 0, 0, 0, -1, 0]; }; readonly Z_0: { readonly enumIndex: 16; readonly matrix: readonly [1, 0, 0, 0, 0, 1, 0, -1, 0]; }; readonly Z_90: { readonly enumIndex: 17; readonly matrix: readonly [0, 1, 0, 0, 0, 1, 1, 0, 0]; }; readonly Z_180: { readonly enumIndex: 18; readonly matrix: readonly [-1, 0, 0, 0, 0, 1, 0, 1, 0]; }; readonly Z_270: { readonly enumIndex: 19; readonly matrix: readonly [0, -1, 0, 0, 0, 1, -1, 0, 0]; }; readonly NZ_0: { readonly enumIndex: 20; readonly matrix: readonly [1, 0, 0, 0, 0, -1, 0, 1, 0]; }; readonly NZ_90: { readonly enumIndex: 21; readonly matrix: readonly [0, -1, 0, 0, 0, -1, 1, 0, 0]; }; readonly NZ_180: { readonly enumIndex: 22; readonly matrix: readonly [-1, 0, 0, 0, 0, -1, 0, -1, 0]; }; readonly NZ_270: { readonly enumIndex: 23; readonly matrix: readonly [0, 1, 0, 0, 0, -1, -1, 0, 0]; }; }; /** * The options for a block collider. @public * * Use for: axis-aligned box colliders. * Do NOT use for: other shapes; use the matching collider option type. * * **Category:** Physics */ export declare interface BlockColliderOptions extends BaseColliderOptions { shape: ColliderShape.BLOCK; /** * The half extents of the block collider. * * **Category:** Physics */ halfExtents?: Vector3Like; } /** * The options for creating a block entity. * * Use for: entities rendered as blocks with a `BlockType` texture. * Do NOT use for: model entities; use `ModelEntityOptions`. * * **Category:** Entities * @public */ export declare interface BlockEntityOptions extends BaseEntityOptions { /** The half extents of the visual size of the block entity when blockTextureUri is set. If no rigidBodyOptions.colliders are provided, a block collider with the size of the half extents will be created. */ blockHalfExtents?: Vector3Like; /** The texture uri of a entity if the entity is a block entity, if set rigidBodyOptions collider shape [0] must be a block */ blockTextureUri?: string; } /** * A block placement in world coordinates. * * **Category:** Blocks * @public */ export declare interface BlockPlacement { globalCoordinate: Vector3Like; blockRotation?: BlockRotation; } declare type BlockPlacementEntry = { globalCoordinate: Vector3Like; blockTypeId: number; blockRotation?: BlockRotation; }; /** * A block rotation from `BLOCK_ROTATIONS`. * * **Category:** Blocks * @public */ export declare type BlockRotation = typeof BLOCK_ROTATIONS[keyof typeof BLOCK_ROTATIONS]; /** * Block texture metadata including UVs and rendering hints. * * **Category:** Textures * @public */ export declare type BlockTextureMetadata = { u0: number; v0: number; u1: number; v1: number; averageRGB: [number, number, number]; isTransparent: boolean; needsAlphaTest: boolean; transparencyRatio: number; }; /** * Manages block textures and block texture atlas generation of the game. * * When to use: querying texture atlas UVs and transparency hints for blocks. * Do NOT use for: runtime texture modifications; regenerate atlas offline in dev. * * @remarks * The BlockTextureRegistry is created internally as a global * singleton accessible via `BlockTextureRegistry.instance`. * The atlas is preloaded during server startup and cached in memory. * * Pattern: call `BlockTextureRegistry.hasBlockTexture` before lookup to avoid warnings. * Anti-pattern: assuming missing textures are silently ignored. * * @example * ```typescript * import { BlockTextureRegistry } from 'hytopia'; * * const blockTextureRegistry = BlockTextureRegistry.instance; * const metadata = blockTextureRegistry.getBlockTextureMetadata('blocks/stone.png'); * ``` * * **Category:** Textures * @public */ export declare class BlockTextureRegistry { /** * The global BlockTextureRegistry instance as a singleton. * * **Category:** Textures */ static readonly instance: BlockTextureRegistry; /** * Whether to generate the atlas if needed. * * Defaults to `true` in development, `false` in production. * * **Category:** Textures */ generate: boolean; /** * Checks if a block texture is registered in the atlas. * * @param textureUri - The URI of the texture (e.g., 'blocks/stone.png' or 'blocks/grass' for cubemaps). * @returns Whether the texture is registered. * * **Requires:** Atlas must be preloaded (server startup). * * **Category:** Textures */ hasBlockTexture(textureUri: string): boolean; /** * Retrieves metadata for a block texture. Returns array for cubemaps (6 faces) or standard textures (1 face). * * @param textureUri - The URI of the texture (e.g., 'blocks/stone.png' or 'blocks/grass'). * @returns Array of texture metadata, or undefined if not found. * * **Requires:** Atlas must be preloaded (server startup). * * **Category:** Textures */ getBlockTextureMetadata(textureUri: string): BlockTextureMetadata[] | undefined; } /** * Represents a block type definition. * * When to use: defining new block types (textures, colliders, liquid behavior). * Do NOT use for: placing blocks directly; use `ChunkLattice.setBlock`. * * @remarks * Block types are created as instances and registered with a `BlockTypeRegistry` * for a specific world. Liquids are treated as sensors in physics. * * <h2>Events</h2> * * This class is an EventRouter, and instances of it emit events with payloads listed under * `BlockTypeEventPayloads`. * * @example * ```typescript * const stoneBlockTypeId = 10; * world.blockTypeRegistry.registerBlockType(new BlockType({ * id: stoneBlockTypeId, * textureUri: 'textures/stone.png', * name: 'Stone', * })); * * // Create a stone block at coordinate 0, 1, 0 * world.chunkLattice.setBlock({ x: 0, y: 1, z: 0 }, stoneBlockTypeId); * ``` * * **Category:** Blocks * @public */ export declare class BlockType extends EventRouter implements protocol.Serializable { /** * Creates a new block type instance. * * Use for: defining a block type before registering it with a `BlockTypeRegistry`. * * @param options - The options for the block type. * * **Category:** Blocks */ constructor(options?: BlockTypeOptions); /** * The unique identifier for the block type. * * **Category:** Blocks */ get id(): number; /** * The collider options for the block type. * * **Category:** Blocks */ get colliderOptions(): VoxelsColliderOptions | TrimeshColliderOptions; /** * Whether the block type is a liquid. * * **Category:** Blocks */ get isLiquid(): boolean; /** * Whether the block type is meshable (voxel-based). * * **Category:** Blocks */ get isMeshable(): boolean; /** * Whether the block type uses a trimesh collider. * * **Category:** Blocks */ get isTrimesh(): boolean; /** * Whether the block type uses a voxel collider. * * **Category:** Blocks */ get isVoxel(): boolean; /** * The light emission level (0-15). * * **Category:** Blocks */ get lightLevel(): number; /** * The name of the block type. * * **Category:** Blocks */ get name(): string; /** * The URI of the texture for the block type. * * **Category:** Blocks */ get textureUri(): string; /** * Triggers an interaction on the block type from a player. * * Use for: programmatic interactions that should mimic player clicks. * * @remarks * This is automatically called when a player clicks or taps a block of this block type, but can also be called directly * for programmatic interactions. Emits `BlockTypeEvent.INTERACT`. * * @param player - The player interacting with the block type. * @param raycastHit - The raycast hit result, if the interaction was triggered by a client-side click/tap. * * **Side effects:** Emits `BlockTypeEvent.INTERACT`. * * **Category:** Blocks */ interact(player: Player, raycastHit?: RaycastHit): void; } /** * Event types a BlockType instance can emit. * * See `BlockTypeEventPayloads` for the payloads. * * **Category:** Events * @public */ export declare enum BlockTypeEvent { ENTITY_COLLISION = "BLOCK_TYPE.ENTITY_COLLISION", ENTITY_CONTACT_FORCE = "BLOCK_TYPE.ENTITY_CONTACT_FORCE", INTERACT = "BLOCK_TYPE.INTERACT" } /** * Event payloads for BlockType emitted events. * * **Category:** Events * @public */ export declare interface BlockTypeEventPayloads { /** Emitted when an entity collides with a block type. */ [BlockTypeEvent.ENTITY_COLLISION]: { blockType: BlockType; entity: Entity; started: boolean; colliderHandleA: number; colliderHandleB: number; }; /** Emitted when an entity's contact force is applied to a block type. */ [BlockTypeEvent.ENTITY_CONTACT_FORCE]: { blockType: BlockType; entity: Entity; contactForceData: ContactForceData; }; /** Emitted when a player interacts with a block type. */ [BlockTypeEvent.INTERACT]: { blockType: BlockType; player: Player; raycastHit?: RaycastHit; }; } /** * Options for creating a block type instance. * * Use for: defining new block types to register in a `BlockTypeRegistry`. * Do NOT use for: placing blocks; use `ChunkLattice.setBlock`. * * **Category:** Blocks * @public */ export declare interface BlockTypeOptions { /** The unique numeric identifier for the block type. */ id: number; /** The custom collider options for the block type. */ customColliderOptions?: VoxelsColliderOptions | TrimeshColliderOptions; /** Whether the block type is a liquid. */ isLiquid?: boolean; /** The light emission level, between 0 and 15. */ lightLevel?: number; /** The name of the block type. */ name: string; /** The URI of the texture asset for the block type. */ textureUri: string; } /** * Manages known block types in a world. * * When to use: registering and retrieving block types for a specific world. * Do NOT use for: placing blocks; use `ChunkLattice.setBlock`. * * @remarks * Each `World` has its own registry. Block type IDs are unique per world. * * <h2>Events</h2> * * This class is an EventRouter, and instances of it emit events with payloads listed under * `BlockTypeRegistryEventPayloads`. * * @example * ```typescript * world.blockTypeRegistry.registerGenericBlockType({ * id: 15, * textureUri: 'textures/dirt.png', * name: 'Dirt', * }); * ``` * * **Category:** Blocks * @public */ export declare class BlockTypeRegistry extends EventRouter implements protocol.Serializable { /** * The world the block type registry is for. * * **Category:** Blocks */ get world(): World; /** * Get all registered block types. * @returns An array of all registered block types. * * **Category:** Blocks */ getAllBlockTypes(): BlockType[]; /** * Get a registered block type by its id. * * @remarks * Throws a fatal error if the block type is not registered. * * @param id - The id of the block type to get. * @returns The block type with the given id. * * **Category:** Blocks */ getBlockType(id: number): BlockType; /** * Register a generic block type. * * @remarks * **Creates anonymous class:** Internally creates an anonymous class extending `BlockType` with the * provided options, then calls `registerBlockType()`. * * @param blockTypeOptions - The options for the block type. * @returns The registered block type. * * **Side effects:** Emits `BlockTypeRegistryEvent.REGISTER_BLOCK_TYPE`. * * **Category:** Blocks */ registerGenericBlockType(blockTypeOptions: BlockTypeOptions): BlockType; /** * Register a block type. * @param blockType - The block type to register. * * **Side effects:** Emits `BlockTypeRegistryEvent.REGISTER_BLOCK_TYPE`. * * **Category:** Blocks */ registerBlockType(blockType: BlockType): void; } /** * Event types a BlockTypeRegistry instance can emit. * * See `BlockTypeRegistryEventPayloads` for the payloads. * * **Category:** Events * @public */ export declare enum BlockTypeRegistryEvent { REGISTER_BLOCK_TYPE = "BLOCK_TYPE_REGISTRY.REGISTER_BLOCK_TYPE" } /** * Event payloads for BlockTypeRegistry emitted events. * * **Category:** Events * @public */ export declare interface BlockTypeRegistryEventPayloads { /** Emitted when a block type is registered. */ [BlockTypeRegistryEvent.REGISTER_BLOCK_TYPE]: { blockTypeRegistry: BlockTypeRegistry; id: number; blockType: BlockType; }; } /** * The options for a capsule collider. @public * * Use for: capsule-shaped colliders. * Do NOT use for: other shapes; use the matching collider option type. * * **Category:** Physics */ export declare interface CapsuleColliderOptions extends BaseColliderOptions { shape: ColliderShape.CAPSULE; /** * The half height of the capsule collider. * * **Category:** Physics */ halfHeight?: number; /** * The radius of the capsule collider. * * **Category:** Physics */ radius?: number; } /** * Event types a ChatManager instance can emit. * * See `ChatEventPayloads` for the payloads. * * **Category:** Events * @public */ export declare enum ChatEvent { BROADCAST_MESSAGE = "CHAT.BROADCAST_MESSAGE", PLAYER_MESSAGE = "CHAT.PLAYER_MESSAGE" } /** * Event payloads for ChatManager emitted events. * * **Category:** Events * @public */ export declare interface ChatEventPayloads { /** Emitted when a broadcast message is sent. */ [ChatEvent.BROADCAST_MESSAGE]: { player: Player | undefined; message: string; color?: string; }; /** Emitted when a message is sent to a specific player. */ [ChatEvent.PLAYER_MESSAGE]: { player: Player; message: string; color?: string; }; } /** * Manages chat and commands in a world. * * When to use: broadcasting chat, sending system messages, or registering chat commands. * Do NOT use for: player HUD/menus; use `PlayerUI` for rich UI. * * @remarks * The ChatManager is created internally as a singleton * for each `World` instance in a game server. * The ChatManager allows you to broadcast messages, * send messages to specific players, and register * commands that can be used in chat to execute game * logic. * * Pattern: register commands during world initialization and keep callbacks fast. * Anti-pattern: assuming commands are permission-checked; always validate access in callbacks. * * <h2>Events</h2> * * This class is an EventRouter, and instances of it emit * events with payloads listed under `ChatEventPayloads` * * @example * ```typescript * world.chatManager.registerCommand('/kick', (player, args, message) => { * const admins = [ 'arkdev', 'testuser123' ]; * if (admins.includes(player.username)) { * const targetUsername = args[0]; * const targetPlayer = world.playerManager.getConnectedPlayerByUsername(targetUsername); * * if (targetPlayer) { * targetPlayer.disconnect(); * } * } * }); * ``` * * **Category:** Chat * @public */ export declare class ChatManager extends EventRouter { /** * Register a command and its callback. * * @remarks * Commands are matched by exact string equality against the first token in a chat message. * * @param command - The command to register. * @param callback - The callback function to execute when the command is used. * * **Requires:** Use a consistent command prefix (for example, `/kick`) if you want slash commands. * * @see `ChatManager.unregisterCommand` * * **Category:** Chat */ registerCommand(command: string, callback: CommandCallback): void; /** * Unregister a command. * * @param command - The command to unregister. * * @see `ChatManager.registerCommand` * * **Category:** Chat */ unregisterCommand(command: string): void; /** * Send a system broadcast message to all players in the world. * * @param message - The message to send. * @param color - The color of the message as a hex color code, excluding #. * * @example * ```typescript * chatManager.sendBroadcastMessage('Hello, world!', 'FF00AA'); * ``` * * **Side effects:** Emits `ChatEvent.BROADCAST_MESSAGE` for network sync. * * @see `ChatManager.sendPlayerMessage` * * **Category:** Chat */ sendBroadcastMessage(message: string, color?: string): void; /** * Handle a command if it exists. * * @param player - The player that sent the command. * @param message - The full message. * @returns True if a command was handled, false otherwise. * * @remarks * The command is parsed as the first space-delimited token in the message. * * **Category:** Chat */ handleCommand(player: Player, message: string): boolean; /** * Send a system message to a specific player, only visible to them. * * @param player - The player to send the message to. * @param message - The message to send. * @param color - The color of the message as a hex color code, excluding #. * * @example * ```typescript * chatManager.sendPlayerMessage(player, 'Hello, player!', 'FF00AA'); * ``` * * **Side effects:** Emits `ChatEvent.PLAYER_MESSAGE` for network sync. * * @see `ChatManager.sendBroadcastMessage` * * **Category:** Chat */ sendPlayerMessage(player: Player, message: string, color?: string): void; } /** * A 16^3 chunk of blocks representing a slice of world terrain. * * When to use: reading chunk data or working with bulk block operations. * Do NOT use for: creating terrain directly; prefer `ChunkLattice`. * * @remarks * Chunks are fixed-size (16×16×16) and store block IDs by local coordinates. * * <h2>Coordinate System</h2> * * - **Global (world) coordinates:** integer block positions in world space. * - **Chunk origin:** the world coordinate at the chunk's minimum corner (multiples of 16). * - **Local coordinates:** 0..15 per axis within the chunk. * * **Category:** Blocks * @public */ export declare class Chunk implements protocol.Serializable { /** * Creates a new chunk instance. */ constructor(originCoordinate