Introduction
Steam-vent allows using the steam client protocol to interact with steam servers.
The steam client protocol can be used to send and receive chat messages, manipulate the inventory, manage friends and more.
Discovering servers
Before we can connect to any steam servers, we first need to know what servers are available.
Servers are provided trough a ServerList, while you can manually specify
a list of servers, the usual method is to use the steam web-api to discover a
list of servers with ServerList::discover.
Once you have a server list you can save a copy of the server list for future
use. But be aware that if you're re-using an old server list, some server in the
list might no longer be functional, so be prepared with dealing with connection
issues when trying to connect using a re-used ServerList.
Anonymous sessions
Note all usages of the steam client api require authentication.
For apis that don't require authentication you can start anonymous sessions
using Connection::anonymous or Connection::anonymous_server to
start a new session using the anonymous client or server account respectively.
Authentication
Authenticated user connections are started with Connection::login.
Besides the normal username and password, you need to provide a store for the steam guard machine data and a confirmation handler.
Steam guard machine data store
After authentication, steam provides the client with some machine specific data to allow skipping (depending on various factors) the authentication step for the next login.
For steam-vent to make use of this data, the app needs to provide a way for
storing this data by providing an implementation of the GuardDataStore
trait.
steam-vent bundles two implementations of the trait:
-
NullGuardDataStore: doesn't store any guard data, effectively disabling the mechanism. -
FileGuardDataStore: store the machine data as json in the provided path.Additionally comes with a helper (
FileGuardDataStore::user_cache()) for using a file in the user's cache directory (exact location depends on the platform).
If none of these methods of storage are suitable for the specific use case, the app can provide their own implementation for the trait. For example storing the data in the application database.
Confirmation handler
When logging into steam, a user needs to provide confirmation of the login trough a second factor. Usually either trough the mobile app or by providing a TOTP token.
An app can implement this confirmation by providing one or more implementations
of the AuthConfirmationHandler trait.
steam-vent bundles the following implementations of the trait:
DeviceConfirmationHandler: waits for the user to confirm the login trough the mobile appUserProvidedAuthConfirmationHandler: asks the user for the TOTP token by sending details about the requested token to the provided output and reading the token from the provided input.ConsoleAuthConfirmationHandler: is convenience wrapper around theUserProvidedAuthConfirmationHandlerthat uses stdin and stdout.SharedSecretAuthConfirmationHandler: generates the TOTP automatically by providing the shared secret, allowing for zero-interaction authentication.
Multiple authentication providers can be combined by using
AuthConfirmationHandler::or, where the first backend that
successfully completes the confirmation will be used. A common use case for apps
will be combining the DeviceConfirmationHandler and one of the TOTP providers
to allow users to confirm the login trough either the app or TOTP.
Alternatively apps can provide their own implementation of the trait to integrate whichever method of asking the user for the TOTP token.
Listen before authentication
Steam sends some messages to the client immediately on authentication.
If you need to listen to those messages, you can create a connection without
logging in yet by using UnAuthenticatedConnection::connect.
Unauthenticated connections cannot be used to send messages, but you can use it to start listening on messages before authenticating.
Once you're started listening for all the messages you require, you can convert
an UnAuthenticatedConnection into a authenticated Connection by using
anonymous, anonymous_server or login.
Customizing the websocket
Sometimes it's necessary to customize the websocket that steam-vent uses, for example to change the TLS settings or configure a proxy.
To accomplish this, steam-vent allows using a custom transport with
UnAuthenticatedConnection::from_sender_receiver which takes a
impl Sink<BytesMut, Error = NetworkError> that will be used to send messages
to steam, and a impl Stream<Item = Result<BytesMut, NetworkError>> that is
used to receive messages.
The Sink/Stream pair can be acquired, for example, by spliting a websocket
from tokio_tungsentite and maping it's data to/from BytesMut.
Once you've created the UnAuthenticatedConnection you can start a
session by using anonymous, anonymous_server or
login.
See the custom_transport example for a full example of creating a
Connection with a customer transport.
Sending messages
There are three different types of messages you can send to steam:
- One-shot messages
- Job messages
- Rpc messages
One-shot messages
One-shot messages are messages sent by the client where no response is expected.
You can send one-show messages with Connection::send, the returned future will complete as soon as the message has been send.
Job messages
Job messages are messages that expect a response from steam
You can send job messages with Connection::job, the returned future will complete once steam returns a response or the response times out.
Note that type of the response isn't specified in the steam protobufs, you fill thus need to manually specify the response type.
Rpc messages
Rpc messages are messages that expect a response from steam
You can send job messages with Connection::service_method, the returned future will complete once steam returns a response or the response times out.
For rpc messages, the return type of the response is specified in the protobufs and you will thus not need to specify the return type.
Rpc messages are denoted in the protobufs by being included in a service
block. See for example the FriendMessages service.
In the generated code for the protobufs, rpc messages implement the
ServiceMethodRequest trait.
Receiving messages
Besides the messages sent as a reply to a message, there are two categories of messages you can listen.
- Notification messages
- Regular messages
Notification messages
You can listen for notification messages with
Connection::on_notification, this will return Stream of
messages of the specified type.
Notification messages are denoted in the protobufs by being included in a
service block. See for example the FriendMessagesClient service.
In the generated code for the protobufs, notification messages implement the
ServiceMethodRequest trait.
Regular messages
For non-notification messages, you can use one to wait for a single
messages of a specific type or on to receive a stream of messages.
Game Coordinator
Some messages need to be send to a game-specific game coordinator instead.
These messages are proxied trough the normal steam connection with a special wrapper, steam-vent can handle this proxying for you.
To send and receive messages from the game coordindator, you need to acquire an
instance of the GameCoordinator with
Connection::game_coordinator.
This takes an implementation of the GCHandshake trait, which specifies
the specifics of initiating the game coordinator connection.
Game-specific implementations can be found the in the protobuf (e.g.
GCHandshake from steam-vent-proto-csgo). Additionally there is a
generic implementation GenericGCHandshake, using the game-specific
handshake is recommended and will generally provide better results.
Once a GameCoordinator is aquired, you can send and receive messages
from it using the same interface as your would with a Connection.