Connection and bot lifecycle

Choose a setup method

connect takes a SoulFire gRPC-Web URL, such as http://localhost:38765. Bot provisioning takes a Minecraft address, such as localhost:25565. These addresses point to different services. Named provisioning reuses instances and accounts. Conflicting configuration fails explicitly. Instances and accounts persist when a connection scope closes.

Keep the workflow inside its scope

SDK operations are lazy Effect[A, E, R] values:

  • A is the successful result.

  • E is the expected error channel.

  • R lists required services, such as Scope.

Inside an Effect workflow, yield from executes these operations. Decorated implementations use EffectGen in source annotations. The reference shows their caller-facing Effect return types. Run the complete workflow at the application boundary with the async runtime.

import os
from datetime import UTC, datetime, timedelta

from effect_py import EffectGen, Scope, gen, sync

from soulfire import SoulFire, SoulFireOperationError


@gen
def collect_with_client() -> EffectGen[None, SoulFireOperationError, Scope]:
    # Use the SoulFire gRPC-Web address here, not the Minecraft address.
    client = yield from SoulFire.connect(
        "http://localhost:38765", token=os.environ.get("SOULFIRE_TOKEN")
    )
    instance = yield from client.get_or_create_instance(
        "reference-example", server="localhost:25565"
    )
    bot = yield from instance.get_or_create_bot("Builder", username="Builder", ready_timeout=30.0)
    result = yield from bot.collect("#minecraft:logs", count=16)
    yield from sync(lambda: print(result))

The complete example contains the imports and decorated workflows. Run a workflow at the application boundary:

import asyncio
from effect_py import run_async, scoped

asyncio.run(run_async(scoped(collect_with_client).or_die()))

The Minecraft server must accept offline accounts for this example. Set SOULFIRE_TOKEN if the SoulFire server requires authentication. or_die() converts expected errors into defects at this script boundary. For application recovery, handle the Effect error channel or inspect run_async_exit instead.

Understand readiness and cleanup

soulfire.SoulFireBot.connect() starts a stopped bot and waits for its initial player snapshot. It attaches the session that supplies soulfire.SoulFireBot.state. ready_timeout defaults to 30 seconds and must be positive and finite.

The scope closes observation and stops a bot only if the SDK started it. A previously running bot stays running. Managed installation also stops its local process when the outer scope closes. All bot work must finish inside that scope. A handle returned from a closed scope does not keep resources alive.

soulfire.SoulFireBot.start() changes desired state without readiness or cleanup ownership. soulfire.SoulFireBot.wait_for_online() waits for live state without attaching bot.state. soulfire.SoulFireBot.observe() returns a separate session unless it can reuse an attached session. Read that session’s state when you open observation separately.

Use the correct timeout

Limit

Unit

Controls

ready_timeout

Seconds

Bot startup and initial player snapshot

startup_timeout

Seconds

Managed SoulFire process startup

timeout_ms

Milliseconds

RPC transport timeout

Task deadline

Timezone-aware datetime

Server task execution

A shorter RPC timeout can fail before the readiness deadline. A task deadline is independent of the client request timeout.

Handle failures and control leases

Expected SDK failures use the Effect error channel. Use catch_tag to recover from a specific error. Do not retry every action automatically: a repeated mutation can repeat completed side effects. For task submission retries, use a stable idempotency key for the same job. A different intended job needs a new key.

soulfire.SoulFireBot.acquire_control() acquires exclusive action control with scoped release. The scope releases the lease, but it does not renew it. Call soulfire.SoulFireBotControlLease.renew() before expiry for longer work.

Continue with Task execution and ownership, the Python tutorial, or complete SDK recipes.