archipelago_rs 2.1.1

A Rust client for the archipelago.gg multiworld randomizer
Documentation

archipelago_rs

A Rust client library that implements the Archipelago network protocol. This is primarily intended for use in integrating games into the Archipelago multiworld randomizer, although it could also be used for making Archipelago bots or other tools.

Design

This library uses a non-blocking style. The caller must manually poll for new events using Connection.update or Client.update. In exchange, the client never blocks on the network connection and so it can safely be used within a game's main loop without having to be run on a separate thread.

In most cases, messages from the server are surfaced to the caller as Event structs returned from Connection.update. However, when the client makes requests to the server that have specific responses associated with them (as in Client.scout_locations() or Client.get()), this will return a oneshot::Receiver that the caller can use to access the response once it's available.

This library takes responsibility for tracking the state of the server as it updates and exposing it through getters such as Client.hint_points() and Client.checked_locations(). It will emit an Event::Updated whenever any of its fields updates because of a change on the server.

Interned Strings

This uses the ustr string interning library. These strings are "interned", meaning that their data is stored in global storage and they're represented as opaque scalar values. This means that their memory is never freed, but their lifetimes don't need to be managed and comparison and hashing operations are very inexpensive.

This uses Ustrs for strings that don't change either during the lifetime of the client or across multiple client connections to the same Archipelago room. This includes the names of games, players (but not their aliases), items, and locations. This makes it possible to share these as copyable objects without having to worry about lifetimes. It's unlikely that the memory will leak in practice since it's expected that the client will last for the whole game session (or, if it disconnects, another client will reconnect to the same room).

TLS/SSL Support

Rust's support for TLS, especially through WebSockets, is very low-level. Because the official Archipelago server requires TLS, this package intends to make using it as simple and batteries-included as possible.

The Rust ecosystem has two broad options for TLS support: the rustls crate, which implements the entire TLS stack in Rust; and the native-tls crate, which relies on the operating system's built-in TLS implementation. These each have their benefits: rustls isn't subject to OS-specific quirks and bugs, while native-tls can keep the algorithm and especially the certificate store up-to-date with OS updates.

To ensure maximum compatibility, by default this crate supports both. When connecting over TLS, it will first attempt to connect using rustls, because this is known to work with archipelago.gg on all platforms (even Wine/Proton, on which native-tls is currently buggy). If this fails, it will fall back to trying native-tls instead. If that fails as well, it'll try an unencrpyted TCP connection.

You can control this behavior using crate features. If you enable only one of the rustls or native-tls features, this will only connect using that system. If you enable neither, it will only connect using an unencrypted WebSocket connection.

Usage

There are two primary entrypoints for the library. The Connection struct is recommended for users who connect to Archipelago as part of a running game, which may disconnect and reconnect over time. It encapsulates the lifetime of a connection, including the time when it's still in the process of connecting and the time after it's disconnected. This makes it easy for the caller to display the current status and change its behavior based on whether a connection exists or not.

If a caller wants to manage the lifetime more directly, they can instead call Client::new() directly. This returns a future that resolves to a Client, which they can then call Client.update() on to update its status. This may be more desirable for tools that only ever intend to connect one time outside of the context of a game loop.

See text_client for a simple example of how to use this client in the context of a main loop.