Skip to content

Example: Advanced Client

BlackoutRoulette edited this page Aug 15, 2026 · 1 revision

Example: Advanced Client

This example shows the recommended way to build a client: subclass Client, override callbacks as methods, use a context manager for clean resource handling, and run your own work alongside the client.

View source on GitHub

What this example does

  1. Creates a custom client class (AdvancedClient) that handles connection, authentication, and errors
  2. Logs in automatically when the server sends its initial RoomInfo packet
  3. Runs a placeholder task concurrently with the client
  4. Uses async with to ensure the client is always properly shut down

Code

import asyncio
from asyncio import Server

from archipelagopy import packets
from archipelagopy.callback_interface import OnConnectExceptionUnion
from archipelagopy.client import Client
from archipelagopy.enums.item_handling_flag import ItemHandlingFlag
from archipelagopy.packets import Connect

class AdvancedClient(Client):

    async def on_ready(self):
        print("Client is connected and ready")

    async def on_room_info(self, packet: packets.RoomInfo):
        packet = Connect(
            version=packet.version,
            tags=["Tracker"],
            name="Mario", # Slot name
            items_handling=ItemHandlingFlag.OWN_WORLD | ItemHandlingFlag.OTHER_WORLDS,
            slot_data=True
        )

        await self.send(packet)

    def on_connect_error(self, error: OnConnectExceptionUnion):
        print("Failed to connect:", error)

    async def on_connection_refused(self, packet: packets.ConnectionRefused):
        print("Connection refused:", packet.errors)
        await self.stop()

    async def on_connected(self, packet: packets.Connected):
        print(f"Logged in as: {packet.slot_info[packet.slot].name}")

    async def on_packet(self, packet: Server):
        print(f">> ({packet})\n")

async def my_work_task():
    """
    Example task, replace with your own asynchronous work.
    """

    while True:
        await asyncio.sleep(1)

async def runner():
    """
    Test function to run the client.
    """

    # Uncomment enable debug logging
    # import logging
    # logging.basicConfig(level=logging.DEBUG)

    async with AdvancedClient(port=12345) as client:
        my_task = asyncio.create_task(my_work_task())
        await client.wait_closed(my_task) # wait for the client to close or the task to finish

def main():
    try:
        asyncio.run(runner())
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()

What's happening step by step

  • class AdvancedClient(Client) creates a subclass of Client. Instead of assigning callback functions one by one (like the Simple Client), we override them as class methods. This is cleaner when you have multiple callbacks and keeps everything organized.

  • async with AdvancedClient(port=12345) as client: is a context manager. It calls client.start() when entering the block and client.stop() when leaving — even if an error occurs. This prevents resource leaks.

  • on_room_info is called automatically when the server sends its initial RoomInfo packet. This is a good place to authenticate because the packet contains the server's version, which you can pass directly to the Connect packet.

  • ItemHandlingFlag.OWN_WORLD | ItemHandlingFlag.OTHER_WORLDS combines two flags using the | (bitwise OR) operator. This tells the server to send you items from both your own game and other players' games.

  • on_connect_error handles network-level failures (server unreachable, timeout, etc.). Note it uses def, not async def — this is one of the two synchronous callbacks. See Callbacks for why.

  • on_connection_refused handles authentication failures (wrong password, invalid slot name). It calls await self.stop() to shut down the client.

  • asyncio.create_task(my_work_task()) starts my_work_task running concurrently in the background. This is where you'd put your own game logic.

  • await client.wait_closed(my_task) blocks until either the client disconnects or my_task finishes, whichever happens first. Without the task argument, it would only wait for the client to disconnect.

Clone this wiki locally