Skip to content

Latest commit

 

History

History
363 lines (267 loc) · 8.15 KB

File metadata and controls

363 lines (267 loc) · 8.15 KB

Getting Started with MindPy

Welcome to MindPy — an asyncio-native Python framework for building intelligent Minecraft bots.

Prerequisites

  • Python 3.12+
  • A Minecraft Java Edition server (offline or online mode)
  • For online mode: a Microsoft account with a Minecraft licence

Installation

From PyPI

pip install mindpy

Development install (latest)

git clone https://github.com/CybersharpX/MindPy.git
cd MindPy
pip install -e ".[dev,llm]"
pre-commit install

Your First Bot (5 minutes)

1. Offline mode (cracked / online-mode=false server)

# examples/hello_bot.py
import asyncio
from mindpy import Bot, EventTypes, Event

async def main():
    bot = Bot(
        host="localhost",
        port=25565,
        username="HelloBot",
    )

    @bot.on(EventTypes.BOT_SPAWNED)
    async def on_spawn(event: Event) -> None:
        print(f"Spawned at {bot.state.position}")
        await bot.chat("Hello, world! 👋")

    @bot.on(EventTypes.CHAT_MESSAGE)
    async def on_chat(event: Event) -> None:
        raw = event.data.get("raw", "")
        print(f"[CHAT] {raw}")

    async with bot:          # connects, runs, disconnects automatically
        await bot.run()

asyncio.run(main())

Run it:

python examples/hello_bot.py

2. Online mode (Microsoft account)

# examples/online_bot.py
import asyncio
from mindpy import Bot
from mindpy.protocol.auth import MicrosoftAuth

async def main():
    # Step 1: authenticate (one-time or cache the profile)
    async with MicrosoftAuth() as auth:
        profile = await auth.device_flow_auth()
        # Prints: "Visit https://microsoft.com/link and enter code XXXX-XXXX"
        # Sign in with the account that owns your Minecraft licence.

    # Step 2: run the bot
    async with Bot(
        "play.example.com",
        auth_profile=profile,
        online_mode=True,
        protocol_version=769,   # 1.21+
    ) as bot:
        await bot.run()

asyncio.run(main())

Core Concepts

Bot lifecycle

Bot()          ← construct
  │
  ▼
connect()      ← TCP open + handshake + login
  │
  ▼
PLAY state     ← packet read loop running in background
  │
  ▼
run()          ← blocks; calls reconnect() on drop
  │
  ▼
disconnect()   ← publishes event, stops bus, closes TCP

Using async with bot: automatically calls connect() on enter and disconnect() on exit.


Event bus

All communication between components flows through the event bus. You can subscribe with:

# Fluent decorator on the bot instance
@bot.on("chat.message")
async def on_chat(event: Event) -> None:
    print(event.data["raw"])

# Wildcard — matches all bot.* events
@bot.on("bot.*")
async def on_any(event: Event) -> None:
    print(event.event_type)

# Direct subscription with cancellation token
token = bot.event_bus.subscribe("player.joined", handler_fn)
# ...
token.cancel()   # or use as context manager: `with token:`

# Wait for a single event (with timeout)
spawn = await bot.event_bus.wait_for("bot.spawned", timeout=30.0)

Event priority controls dispatch order within the same event type:

from mindpy.events.event import EventPriority

@bot.on("chat.message", priority=EventPriority.HIGH)
async def high_priority_handler(event: Event) -> None:
    ...

Priority order (lowest value = first): CRITICAL(0) → HIGH(1) → NORMAL(2) → LOW(3).


Bot state

The current in-game state is always available on bot.state:

bot.state.connected       # bool
bot.state.health          # float  0.0–20.0
bot.state.hunger          # int    0–20
bot.state.saturation      # float
bot.state.x, .y, .z       # float  world coordinates
bot.state.yaw, .pitch     # float  look direction (degrees)
bot.state.entity_id       # int    server-assigned entity ID
bot.state.game_mode       # int    0=survival 1=creative 2=adventure 3=spectator
bot.state.is_hardcore     # bool
bot.state.dimension       # str    e.g. "minecraft:overworld"
bot.state.position        # (x, y, z) convenience tuple

The bot automatically keeps state in sync from incoming Play-state packets.


Configuration

Create config.yaml:

bot:
  username: MindPyBot
  host: localhost
  port: 25565
  max_reconnect_attempts: 5
  reconnect_delay: 5.0

logging:
  level: INFO
  file: logs/mindpy.log

Load it:

from mindpy.config import Config
from mindpy import Bot

config = Config("config.yaml")
bot = Bot(
    host=config.get("bot.host", "localhost"),
    port=config.get("bot.port", 25565),
    username=config.get("bot.username", "MindPyBot"),
    config=config,
)

Or use environment variables (take precedence over YAML):

export MINDPY_BOT_USERNAME=Agent1
export MINDPY_BOT_HOST=play.example.com

Memory system

from mindpy.memory import MemoryManager

memory = MemoryManager()

# Working memory — current context
memory.working_memory.add("current_task", "mining_diamonds")

# Long-term memory — persistent facts
memory.long_term_memory.store_fact("home", (100, 64, 200))
home = memory.long_term_memory.get_fact("home")

# Conversation memory — chat history
memory.conversation_memory.add_message("player", "Go get wood.")

# Save / load all memory layers
await memory.save_all()
await memory.load_all()

Task system

from mindpy.tasks import TaskManager, BaseTask, TaskData, TaskStatus

class MineOresTask(BaseTask):
    @property
    def name(self) -> str:
        return "mine_ores"

    @property
    def description(self) -> str:
        return "Mine iron ore near current position"

    async def execute(self, context) -> str:
        # ... mining logic ...
        return TaskStatus.COMPLETED.value

task_manager = TaskManager(bot.event_bus)
await task_manager.start(num_workers=2)

task_id = await task_manager.submit_task(MineOresTask())

Tasks are interruptible, suspendable, and serializable:

await task_manager.suspend_task(task_id)
await task_manager.resume_task(task_id)
await task_manager.cancel_task(task_id)

Goal system

from mindpy.goals import GoalManager, GoalPriority

goal_manager = GoalManager(bot.event_bus)

goal = goal_manager.create_goal(
    name="Collect Diamonds",
    description="Mine and collect 10 diamonds",
    priority=GoalPriority.HIGH,
)

# Goals decompose into tasks automatically
sub_goals = await goal_manager.decompose_goal(goal.goal_id)

AI integration

from mindpy.llm import LLMManager
from mindpy.ai import AIAgent, AgentContext

# Setup
llm = LLMManager()
llm.setup_openai(api_key="sk-...", model="gpt-4o")
# or: llm.setup_ollama(base_url="http://localhost:11434", model="llama3")

agent = AIAgent(
    llm,
    system_prompt=(
        "You are a helpful Minecraft bot. "
        "Always respond in one short sentence."
    ),
)

# Decide an action based on context
ctx = AgentContext(
    position=bot.state.position,
    health=bot.state.health,
    inventory=[],
)
response = await agent.decide(ctx, user_message="What should I do now?")

Protocol versions

MindPy supports Minecraft Java Edition 1.8 through 1.21+:

Protocol Minecraft Notes
47 1.8 VarInt keepalive, string UUID
340 1.12.2 int64 keepalive, ClientSettings
754 1.16.5 UUID as bytes, TeleportConfirm
762 1.19.4 Signed chat, SystemChatMessage
765 1.20.4 Configuration state
769 1.21+ Latest IDs, forward-compat fallback

Unknown versions ≥ 47 automatically fall back to the closest older supported version.


Running tests

pytest                                        # all tests
pytest tests/test_protocol.py -v             # protocol layer only
pytest tests/test_bot.py -v                  # bot tests only
pytest --cov=mindpy --cov-report=html        # with coverage report

Next steps