Skip to main content

Targets

An acceptor says which targets a collection takes, and require says what their host must be able to do. Both are checked twice: at compile time against the target type, and at runtime per target type, cached beside the resolver's decision.

Import​

import { players, entityTypes, blockTypes, dimensions, worldTarget, slots, accepting, anyOf, allOf, except } from '@bedrock-core/db';
import type { Acceptor, Requirements, StorableTarget, TargetKind } from '@bedrock-core/db';

All re-exported from @bedrock-core/server.

What can hold a document​

type StorableTarget = World | Dimension | Entity | Block | ContainerSlot;
type TargetKind = 'world' | 'dimension' | 'entity' | 'block' | 'slot' | 'itemStack' | 'unknown';
TargetWhere the document livesOwn properties
Worldthe world✅
Player, Entitythe entity; dies with it✅
Block whose type declares minecraft:block_entity with dynamic_properties: truethe block entity, about 950 bytes per pack; dies with the block✅
ContainerSlot holding a non-stackable itemthe slot's item✅
Dimensionthe world, keyed by the dimension id❌
any other blockthe world, keyed by dimension and location❌
ItemStackrefused — a detached copy; the write never reaches the world—
ContainerSlot holding a stackable itemrefused — a stackable item cannot hold properties—

See Blocks for what a block needs, and the resolver for how the decision is made.

Acceptors​

An acceptor is a rule over target types: it is asked once per type with the type id — block or entity type, the item type of a slot, minecraft:overworld for a dimension, world for the world — and cached. Types, not instances, which is what keeps the check free on the hot path.

AcceptorTakesTarget type
players()playersPlayer
entityTypes(...ids)entities of the listed types; every entity when none is listedEntity
blockTypes(...ids)blocks of the listed types; every block when none is listedBlock
dimensions(...ids)the listed dimensions; every dimension when none is listedDimension
worldTarget()the worldWorld
slots(...itemIds)container slots holding the listed items; any item when none is listedContainerSlot
accepting(test, kinds?)any rule over type ids, for the target type the caller namesT

Without an accept, a collection takes any StorableTarget.

const balances = db.collection('balances', { schema: balanceSchema, accept: players() });

balances.for(player); // ok
balances.for(block); // compile error: Block is not Player

Combining​

CombinatorResult
anyOf(a, b)any of the rules; the target type is the union — anyOf(players(), entityTypes('papi:merchant')) takes a Player | Entity
allOf(a, b)all of the rules; the target type is the intersection
except(base, excluded)the base rule minus another — except(entityTypes(), players()) takes every entity but a player

except keeps the base type, since TypeScript cannot subtract a subclass: what is excluded is refused when for() runs, with a reason.

require​

interface Requirements {
readonly own?: true; // the target must hold its own properties
readonly enumerable?: true; // the host must be able to list its keys
readonly readableWhenUnloaded?: true; // the document must be readable while the target is not loaded
}

Only true is a demand; an omitted key is indifferent. A target whose host lacks a required capability is refused by for() with a reason — a block without dynamic properties under require: { own: true } is refused instead of stored on the world.

The check also runs in the IDE: a requirement the acceptor's target type can never satisfy is a compile error whose missing-property name is the reason. own on a dimensions() collection and readableWhenUnloaded on an entityTypes() collection are both type errors.

db.collection('elevators', {
schema: elevatorSchema,
accept: blockTypes('papi:elevator'),
require: { own: true },
});

Blocks​

A block holds its own documents when its type turns dynamic properties on:

"minecraft:block_entity": { "dynamic_properties": true }

The resolver probes for the minecraft:dynamic_properties component this gives the block, not for the block entity: a block entity without it is stored on the world like any other block.

Two things a block collection needs from the pack:

  • blockCleanup — a custom component to register and list on every accepted block type. Its onBreak fires for every removal, /setblock and script setPermutation included, and is what keeps the index honest:

    import { blockCleanup } from '@bedrock-core/db/minecraft';

    system.beforeEvents.startup.subscribe(({ blockComponentRegistry }) => {
    blockComponentRegistry.registerCustomComponent('papi:db_cleanup', blockCleanup(db));
    });
  • A loot function, if a mined block should carry its document on the dropped item. By default a mined block-entity item carries none.

An unregistered custom component named in block JSON removes the block from the world, so register it before the pack's blocks load.

Slots​

A ContainerSlot is a runtime-decided host: the same slot holds a document while it holds a non-stackable item and refuses one while it holds a stackable item. where(slot) answers for the item there now. Slot documents are never indexed, so all() does not yield them.