Developers

Script plugins

For servers and advanced setups: plugins that run inside BedrockAdvancements, with test functions and your own trigger types.

Adding a plugin

  1. Put your plugin's folder in BedrockAdvancements_BP/scripts/plugins/.
  2. In scripts/plugins/index.js, import it and add it to PLUGINS:
import gems from "./gems/index.js";

export const PLUGINS = [
  gems,
];

A plugin's index.js exports { id, register }:

import { BA } from "../../api/index.js";

function register() {
  BA.plugin("gems", () => {
    BA.tab({ id: "main", title: "Gem Collector", icon: "textures/items/emerald", background: "textures/blocks/emerald_block" });
    BA.advancement({
      id: "root",
      tab: "main",
      title: "Gem Collector",
      description: "Pick up an emerald",
      icon: "textures/items/emerald",
      criteria: { emerald: { trigger: "inventory_changed", item: "emerald" } },
    });
  });
}

export default { id: "gems", register };

If a plugin throws while registering, the error is logged and the other plugins still load. scripts/plugins/example/ is a complete example.

What scripts add

  • Test functions on many triggers, like test(dead, player, source) on player_killed_entity, or itemTest(stack) on inventory_changed.
  • Reward functions: rewards: { run: (player) => { … } }.
  • The tick trigger: { trigger: "tick", test: (player) => player.isSwimming, interval: 20 }.
  • The enabled function on a tab: hide it (and pause its progress) whenever it returns false.
  • Custom trigger types, below.

BA.advancements(defaults, list) defines several at once, each inheriting defaults (handy for { tab: "main" }).

Custom trigger types

BA.registerTrigger("gems:jumps", {
  // Called once, the first time any loaded advancement uses this trigger.
  start() {
    BA.events.everyPlayer("gems:jumps", 2, (player) => {
      if (!player.isJumping) return;
      for (const ref of BA.triggers.pending(player, BA.triggers.refs("gems:jumps"))) {
        BA.triggers.hit(player, ref); // counts towards `count` if the criterion has one
      }
    });
  },
});
  • BA.triggers.refs(type): every criterion using the trigger; ref.def is its definition.
  • BA.triggers.pending(player, refs): those the player still needs (skips completed ones and disabled tabs).
  • BA.events.onAfter(name, fn) / onBefore(name, fn): shared world event subscriptions.
  • BA.events.everyPlayer(name, ticks, fn): runs fn(player) for each online player every ticks, spread across ticks.
  • BA.data(player).getState(key) / setState(key, value) / addState(key, n): saved per-player state for your trigger.

Triggers start lazily and check pending() first, so unused triggers cost nothing.