Skip to content

Services & C# API ​

Tier 1 gets you far with JSON alone. When you need custom logic, a mod can add C#: a service that runs your rules and behaviors that execute your actions. This page covers the public modding surface. You build against these hooks; you do not need the engine internals.

NOTE

Most mods never need this page. Reach for C# only when a behavior you need does not exist and cannot be composed from built-in ones.

Services ​

A service is a class that initializes with your mod and reacts to the world. The shape:

csharp
public class BountyService : IEntityService
{
    public string EntityType => "BountyHunter";
    public string ServiceName => "Default";

    public void Initialize()
    {
        // Register behaviors, subscribe to events, set up state.
    }
}
MemberPurpose
EntityTypeThe entity type this service is responsible for.
ServiceNameA name for the service (usually Default).
Initialize()Called once at startup. Wire everything here.

Reading and writing state ​

Use the mod API to read entities and set properties. This is the supported way to touch world state from a mod:

csharp
var entity = ModAPI.GetEntity(entityId);
double greed = Convert.ToDouble(entity.Properties.GetValueOrDefault("Greed") ?? 0.0);

ModAPI.SetProperty(entityId, "MarkedForBounty", true);
CallDoes
ModAPI.GetEntity(id)Get a read snapshot of an entity.
ModAPI.SetProperty(id, key, value)Set a property. This is also how you signal another entity in a handshake.

IMPORTANT

Any property you write must be declared in the entity's propertySchemas. Undeclared keys are flagged by validation.

Behaviors ​

A behavior is the code an action runs. It executes each tick while the action is active and returns a status:

csharp
public ExecutionStatus Execute(Entity entity, ...)
{
    // Do work. Return one of:
    //   InProgress - keep going next tick
    //   Success    - done; declared postconditions are applied automatically
    //   Failed     - abort; planner replans
    return ExecutionStatus.Success;
}

Register your behavior under the behaviorName your action JSON references, so the planner can find it. See GOAP authoring.

ReturnMeaning
InProgressNot finished; run again next tick.
SuccessFinished; the action's postconditions are applied for you.
FailedCould not complete; the pawn replans.

IMPORTANT

Do not re-apply an action's declared postconditions by hand inside the behavior. They are applied automatically on Success. Writing them again double-applies the effect. Direct SetProperty writes are only for effects the action cannot declare, such as signaling a different entity.

Reacting to the world ​

Services can subscribe to world events to run logic at the right time:

EventFires whenTypical use
Game tickEvery tickTimed logic, decay, cyclical needs.
Action completedA pawn finishes an actionReact to what just happened.
Goal completedA pawn finishes a goalAward, record, or chain consequences.
Entity spawnedA new entity enters the worldRegister it with your service.

A common pattern is a cyclical need: on a timer, flip an entity from satisfied to seeking, which makes its GOAP goal activate. Always clear any cross-entity signal flags when a new cycle starts, so a stale flag does not complete the next run instantly.

Threading ​

Your goals, actions, and behaviors run on the AI thread, not the render thread. They read and write state through the state API (ModAPI.GetEntity, ModAPI.SetProperty) against the shared immutable store, so for normal content work you are not managing locks yourself: read, decide, write.

The one rule: anything that touches the UI must be marshaled to the main thread. Do not update UI directly from a behavior or an AI-thread callback. Use the provided main-thread event path for display updates. See the architecture note.

Don't hardcode tuning ​

Keep balance values in your mod's config files and read them at init, rather than baking constants into C#. This lets players and other modders retune without recompiling, and matches how the base game is built. See Economy Config.

What stays internal ​

This API is the stable surface for mods. The engine internals behind it (how state is stored, how planning is scheduled, the full service pipeline) are intentionally not part of the modding contract and are not documented here. You can build deep, original systems on the surface above without them.

Community documentation for BBox: Kingpin.