Skip to content

Extending JSPrismarine ​

There are two ways to change what a JSPrismarine server does, and most work needs only the first.

  • Packs change content. Blocks, items, entities, loot, recipes, worldgen and commands come from Bedrock behavior packs and Java datapacks. A pack is data, so it needs no code and no build, and it survives server updates.
  • Code changes behavior. For what data cannot express, embed the server in your own Node.js program through @jsprismarine/prismarine, listen to its events and call its services.

JSPrismarine has no plugin loader yet: there is no folder the server scans for code to run. A "plugin" today is a program that starts the server itself, as in Embed the server.

Add packs ​

List the packs in config.json and restart the server:

SettingWhat it does
content-strictWhether a pack that reports an error stops the server instead of loading short.
content-world-packsExtra behavior pack directories this world loads, after the vanilla ones.
content-datapacksJava datapack directories this world loads, after the vanilla one.
jsonc
{
    "content-world-packs": ["packs/my-mobs"],
    "content-datapacks": ["packs/gameplay-tweaks"]
}

Later packs override earlier ones, so your packs win over vanilla. Using packs explains which formats load, how conflicts resolve and how to read what the server made of a pack. Molang, Entities and Gameplay data cover what each kind of definition can do.

Run the server from a checkout with pnpm run dev while you write a pack: it restarts the server whenever a loaded pack's files change. See Run from source.

Embed the server ​

@jsprismarine/prismarine is the whole server as a library. Your program builds a Config and a Logger, starts a Server, and listens to its events. This is the same thing the prismarine command does, minus the console.

bash
pnpm add @jsprismarine/prismarine @jsprismarine/logger
typescript
import { Logger } from '@jsprismarine/logger';
import { Config, Server } from '@jsprismarine/prismarine';

// Reads config.json in the working directory, and writes it with every default if it is missing.
const config = new Config();
const logger = new Logger(config.getLogLevel());
const server = new Server({ config, logger });

server.on('playerSpawn', (event) => {
    logger.info(`${event.getPlayer().getName()} spawned`);
});

server.on('chat', (event) => {
    logger.info(`chat: ${event.getChat().getMessage()}`);
});

await server.bootstrap();

server.shutdown() stops it and saves the worlds.

The server reads its game data the way the prebuilt server does: from node_modules/.cache/jsprismarine/ in the working directory. Point JSP_VANILLA_BEHAVIOR_PACK, JSP_VANILLA_DATAPACK and JSP_JAVA_REPORTS at copies made by a checkout, or run a minecraft:flat world, which needs no Java data. Running JSPrismarine has the details.

Events ​

server.on(name, listener) takes one of these 18 events. Calling event.preventDefault() cancels an event where the server checks for it, such as a player's move or a trip through a portal.

EventListener receives
chatChatEvent
tickTickEvent
entityDamageEntityDamageEvent
entityDeathEntityDeathEvent
entityPortalEnterEntityPortalEnterEvent
playerChangeDimensionPlayerChangeDimensionEvent
playerConnectPlayerConnectEvent
playerDisconnectPlayerDisconnectEvent
playerSpawnPlayerSpawnEvent
playerDespawnPlayerDespawnEvent
playerMovePlayerMoveEvent
playerToggleFlightPlayerToggleFlightEvent
playerToggleSprintPlayerToggleSprintEvent
playerToggleOperatorPlayerToggleOperatorEvent
playerSetGamemodePlayerSetGamemodeEvent
raknetConnectRaknetConnectEvent
raknetDisconnectRaknetDisconnectEvent
raknetEncapsulatedPacketRaknetEncapsulatedPacketEvent

The API reference documents every class the events hand you, and every other export of the published packages.

Use the pieces on their own ​

Every package is published on its own, so a tool can take one without the server: read worlds with @jsprismarine/leveldb, parse NBT with @jsprismarine/nbt, evaluate Molang with @jsprismarine/molang, or speak the protocol with @jsprismarine/protocol over @jsprismarine/raknet or @jsprismarine/nethernet.