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
TypeScript
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