SoulFire LogoSoulFire

Coordinate native bot control

Use ControlTask resources and priority arbitration for direct Minecraft actions.

Plugins share the JVM with SoulFire and the headless Minecraft client. Use native control when an event, visual script, or SDK operation cannot express the required action. These examples describe the pinned source baseline.

Internal Minecraft and plugin APIs can change between releases. Build against the backend version you deploy. Do not copy the older ControllingTask API into this source baseline.

Runtime objects

ObjectScope
SoulFireServerBackend process and shared services
InstanceManagerOne instance, settings, and lifecycle
BotConnectionOne live bot session and its headless Minecraft client
ControlStateMovement inputs consumed by the client
BotControlAPIResource ownership, control task execution, and arbitration

current() accesses the current thread's matching runtime context. Use currentOptional() if your code can run without that context. Use the bot's wrapped scheduler for lifecycle-bound work, rather than an unrelated executor.

Submit one action

This fragment runs inside a bot event handler with a live connection:

OpenInventory.java
import com.soulfiremc.server.bot.ControlResource;
import com.soulfiremc.server.bot.ControlTask;
import java.util.Set;

var task = ControlTask.once(
  "Open the player inventory",
  Set.of(ControlResource.INVENTORY),
  () -> {
    var player = connection.minecraft().player;
    if (player != null) {
      player.sendOpenInventory();
    }
  }
);
boolean started = connection.botControl().tryStart(task);

tryStart returns false when an active task owns a conflicting resource. Handle that result. Ignoring it can silently skip your action. A default task claims all resources unless you supply a smaller set.

Choose an arbitration policy

MethodBehavior
tryStart(task)Starts only when no active task conflicts
enqueue(task)Starts now or queues until its resources are available
submit(task)Starts or suspends conflicts if its priority can preempt them
replace(task)Stops conflicting active and suspended tasks, then starts this task
cancel(task)Cancels that task in active, suspended, or queued state
stopAll()Cancels all tasks in those states

Resources include movement, rotation, hands, inventory, container, chat, vehicle, camera, and protocol access. Tasks with disjoint resource sets can run together. Priorities are LOW, NORMAL, HIGH, and CRITICAL. Only a strictly higher priority can preempt a lower priority through submit.

Choose tryStart for optional work, or enqueue for work that can wait. Use replace only if replacing the other owner is part of the intended behavior.

Perform a sequence without blocking the tick

InventorySequence.java
import com.soulfiremc.server.bot.ControlPriority;
import com.soulfiremc.server.bot.ControlResource;
import com.soulfiremc.server.bot.ControlTask;
import java.util.List;
import java.util.Set;

connection.botControl().enqueue(ControlTask.sequence(
  "Open and close inventory",
  ControlPriority.NORMAL,
  Set.of(ControlResource.INVENTORY, ControlResource.CONTAINER),
  List.of(
    ControlTask.action(() -> {
      var player = connection.minecraft().player;
      if (player != null) player.sendOpenInventory();
    }),
    ControlTask.waitMillis(50),
    ControlTask.action(() -> {
      var player = connection.minecraft().player;
      if (player != null) player.closeContainer();
    })
  )
));

Each step advances through ticks. A delay waits without sleeping the tick thread. A delay does not prove that the Minecraft server acknowledged an inventory action. For acknowledgement-sensitive work, inspect the relevant event or state before advancing.

Movement and cleanup

connection.controlState().up(true) sets forward movement until the state changes. Call resetAll() when the behavior stops if your plugin owns those movement inputs. Do not reset another owner's controls from an unrelated callback.

Custom control tasks can implement onStarted, onSuspended, onResumed, and onStopped. Release or reset owned state in those lifecycle methods. Inspect ControlStopReason to distinguish completion, cancellation, replacement, and failure.

Markers and direct access

ControlTask.marker claims resources for a marker-based workflow. claimMarker retrieves the marker and finishes its active task. A marker remains an owner until claimed or stopped.

Direct access includes connection.minecraft().player, .level, .gameMode, and .getConnection(). Check lifecycle availability before using them. Player and world objects can be absent during login or after disconnection.

Read the BotControlAPI implementation and its tests before implementing a custom arbitration policy. See events for execution context and architecture for the RPC path.

How is this page?

Last updated on

On this page