Connection and bot lifecycle¶
Choose a setup method¶
soulfire.SoulFire.create_bot()installs a local SoulFire server and returns a ready bot.soulfire.SoulFire.install()installs once so several bots can share the managed server.soulfire.SoulFire.connect()connects to an existing server and checks compatibility.soulfire.SoulFire.instance()andsoulfire.SoulFireInstance.bot()create handles for existing UUIDs.
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:
Ais the successful result.Eis the expected error channel.Rlists required services, such asScope.
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 |
|---|---|---|
|
Seconds |
Bot startup and initial player snapshot |
|
Seconds |
Managed SoulFire process startup |
|
Milliseconds |
RPC transport timeout |
Task |
Timezone-aware |
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.