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.