Concepts

Hooks

On this page

A hook point is a place where the server takes one of the game's own steps, such as running a command, sending a packet or breaking a block. Each step is written as a function from an input to an output:

input ──prepend*──▶ input′ ──override? or the game's own step──▶ output ──append*──▶ output′

A plugin subscribes to a hook point in one or more modes:

ModeRunsIs shownMay answer Reply::Replace(..) withThen
prependbefore the stepthe inputa new inputthe step runs on the new input
overrideinstead of the stepthe inputthe outputthe game's own step does not run
appendafter the stepthe input and the outputa new outputthe caller gets the new output

Answering Reply::Pass changes nothing. A plugin that does nothing at a hook point must answer Pass, which is the trait's default.

Several plugins at one point

Plugins run in priority order (First, Early, Normal, Late, Observer). Within a priority, they run in load order.

  • Prepends chain. Each prepend sees the input the previous one left.
  • The first override wins. Overrides are asked in order. The first one to answer Replace supplies the output, and the rest are not asked. If none answers, the game's own step runs.
  • Appends chain. Each append sees the output the previous one left.

A plugin subscribed in several modes at one point is called once per mode, in the order prepend, override, append.

Subscribing

Manifest::new("x", "1")
    .hooking("command.time", Mode::Override)      // one point
    .hooking("packet.play.*", Mode::Append)       // a family: a prefix ending in ".*"
    .hooking("*", Mode::Prepend)                  // every point

A prefix pattern must end at a dot. packet.pl* matches nothing.

The call

pub struct HookCall<'a> {
    pub point: &'a str,           // e.g. "command.time"
    pub mode: Mode,
    pub input: &'a Data,          // what the step runs (or would run) on
    pub output: Option<&'a Data>, // Some only for an append
}

The input and output are Data. Each point's shape is listed in the hook point reference.

Cancelling

There is no separate "cancel". To cancel a step, override it with an output that means "did not happen". For a command that is false; for a clientbound packet it is [] (nothing written); for a serverbound packet it is {"handled": false, "reply": []}.

A wrong answer is refused, not obeyed

If a reply does not fit the point's type (a missing field, a string where a number belongs, a negative count where only positive ones exist), the server logs it once and keeps the value it would have replaced. See Guarantees.

From docs/api/concepts/hooks.md in the RustMC repository at bbd81209ad.