Skip to main content

Utilities & NBT

Two namespaces cover general-purpose helpers: utils for math, events, scheduling and more, and nbt for reading NBT data.

The utils namespace​

utils is the host's own utility library, re-exported as a namespace. Import it to get math types, hashing, streams, event emitters, and more:

import { utils } from "@protohax/userscript";

const pos = new utils.Vector3(100, 64, 100);

It includes the host's entire utility library, so it's large. These are the parts most useful to scripts.

Vectors & geometry​

ExportDescription
Vector3(x, y, z)Immutable 3D vector. x, y and z are readonly.
Vector2(x, y)Immutable 2D vector. Rotations are (pitch, yaw): pitch in x, yaw in y.
AABB(min, max)Axis-aligned bounding box from two Vector3s.

IVector3 is the plain { x, y, z } interface many APIs accept. Wherever an IVector3 is expected, you can pass an object literal.

Math helpers​

Common ones: clamp, lerp, wrapDegrees, clampPitch, getRotationFromVector, getLookDirection, blockPositionKey / blockPositionFromKey, makeEntityAABB, computeStrafe. To convert angles, multiply by the constants DEG_TO_RAD / RAD_TO_DEG.

blockPositionKey(x, y, z) packs a block position into a bigint. For a map of positions around one origin, such as a search area, relativeBlockKey(dx, dy, dz) packs the offset from that origin into a plain number instead, which is a faster map key. It returns -1 when an offset is more than 65536 blocks away.

Scheduling​

Scheduler<T> and SchedulerPriority live here. They're what localPlayer.rotationScheduler uses.

utils.SchedulerPriority.High; // "High"

Events​

EventEmitter<Events> is the same emitter the host uses, with on, once, off and emit. Both session.events and entity.entityEvents are instances of it.

on(event, fn, context?, priority?) takes an optional priority. Lower runs first, and the default is 10. utils.EventPriority has named levels, from HIGHEST (-1000) to LOWEST (1000). once listeners run after all the others.

There's no "*" catch-all event: you always listen to one event by name. To see every packet, register a PacketQueueChecker instead.

Pathfinding​

astar(start, goal, planner, world, options?) runs an A* search over block positions. It returns { found, path, iterations, cost }, where path is the list of block positions from start to goal. The planner decides which moves are allowed and what they cost. AirPlanner is the built-in one: it flies through air and searches session.levelState.

const player = ctx.session.entityState.localPlayer;
const start = player.position.floor();
const goal = start.add({ x: 20, y: 0, z: 0 });

const result = utils.astar(start, goal, new utils.AirPlanner(), ctx.session.levelState, {
maxIterations: 5000,
});
if (result.found) {
const waypoints = utils.simplifyPath(result.path, 1);
}

A search can be expensive, so run it from the tick event, not movement_tick. See Deferring heavy work.

To write your own planner, implement AStarPlanner<TWorld>: neighbors, heuristic and isGoal. Two optional hooks let you cache world lookups for the length of one search. beginSearch(start, world) runs before the search. endSearch() runs after it, even if the search throws. MinHeap<K>, the priority queue behind the search, is generic over its key type (bigint by default).

Per-session state​

sessionLocal(init) creates a typed store keyed by session. The accessor it returns gives back the same object for the same session wherever you call it. The object is created with init on first access and dropped along with the session. See Per-session state.

const combat = utils.sessionLocal(() => ({ ticks: 0 }));
combat(ctx.session).ticks++;

Misc​

ExportDescription
sendToast(message, icon)Show a toast on the client. icon is "info", "success", "warning" or "error".
UUIDThe UUID class (parse, equals, toString, static ZERO).
xxh32String, fnv1aStringString hashers.
nanoid, generateRandomStringRandom id generation.
ReadStream, WriteStreamBinary IO streams.
pushUnique(list, value, equals?)Append to an array unless an equal element is already in it. Returns whether the list changed.
removeFrom(list, value, equals?)Remove the first equal element from an array. Returns whether the list changed.
Explore with autocomplete

The whole utility tree is published, so the quickest way to see what's there is to type utils. in your editor. Members starting with _ are renamed at build time, so you can't use them from a script.

NBT — the nbt namespace​

The host bundles a patched copy of prismarine-nbt and re-exports it as nbt. Use this rather than installing prismarine-nbt yourself. A separately installed copy would be the unpatched build, with an incompatible protodef instance.

import { nbt } from "@protohax/userscript";

// e.g. simplify an item's raw NBT compound
const held = ctx.session.entityState.localPlayer.inventory.hand;
if (held?.nbt) {
const simplified = nbt.simplify(held.nbt.nbt);
}

Block states (BlockState.nbtStates) and item stacks (ItemStack.nbt) return these NBT structures. nbt.simplify(...) turns a tag tree into plain JavaScript values.