Skip to main content

Crate bevy_net_backend

Crate bevy_net_backend 

Source
Expand description

Call your game’s own HTTPS JSON API from Bevy.

A system fires a request through HttpClient and gets a RequestId; a few frames later exactly one answer arrives as a Bevy message: JsonResponse<T> for typed JSON (feature json) or HttpResponse for raw bytes. The answer is the decoded value or a BackendError: network, TLS, timeout, an HTTP status with the server’s body, a decode error, cancelled, or shutdown on AppExit. Nothing is ever dropped silently.

Features: http (the real transport: ureq on a few worker threads, rustls with ring, and multipart/form-data uploads with Multipart), json (typed requests), gzip, ws (named WebSocket connections: WsClient and friends), ssh (named SSH connections that run commands, ADMIN / DEV builds only: SshClient), sftp (file operations on them), ssh-rsa (RSA keys for SSH). Default: http, json. No tokio unless you enable ssh. Without http the crate still builds, and the FakeHttpTransport drives everything in tests. The README is the full manual. A quick start:

use bevy::prelude::*;
use bevy_net_backend::prelude::*;
use serde::Deserialize;

#[derive(Deserialize, Clone, Debug)]
struct Profile {
    name: String,
    level: u32,
}

fn main() {
    App::new()
        .add_plugins((MinimalPlugins, BackendPlugin::new(HttpConfig::new("https://api.example.com"))))
        .add_json_response::<Profile>()
        .add_systems(Startup, |backend: Res<HttpClient>| {
            backend.get_json::<Profile>("/me");
        })
        .add_systems(Update, |mut answers: MessageReader<JsonResponse<Profile>>| {
            for answer in answers.read() {
                match &answer.result {
                    Ok(profile) => info!("{} is level {}", profile.name, profile.level),
                    Err(error) => warn!("could not load the profile: {error}"),
                }
            }
        })
        .run();
}

Re-exports§

pub use http;

Modules§

prelude
Everything a game usually needs: use bevy_net_backend::prelude::*;.

Structs§

ApiKeyHeader
A key in a header of your choice, e.g. X-Api-Key: <key>.
ApiKeyQuery
A key in a query parameter, e.g. ?api_key=<key>. Prefer a header where the API allows it: URLs end up in server access logs, and ureq logs the full path and query at trace level.
BackendCredentials
The game’s current credentials, applied to every request (except those made without_credentials). Empty by default; the game sets it after its own login call and clears it on logout.
BackendPlugin
The plugin. Add it once; it adds no other plugin and works under MinimalPlugins (and even without Bevy’s TimePlugin).
BearerToken
Authorization: Bearer <token>: Laravel Sanctum / Passport, most Node and Go APIs, JWTs.
FakeHttpTransport
An in-memory HttpTransport for tests: it records every request and answers from scripted routes or by hand. Clones share one state, so keep a clone to script and inspect it after inserting it:
FakeSshTransportssh
An in-memory SshTransport for tests: records every connect, command, SFTP operation, cancel and close, and answers from a script. Clones share one state. Events are delivered on the next poll (the next frame’s First). Never touches the network or any file.
FakeWsTransportws
An in-memory WsTransport for tests: records every link, frame and close, and answers from a script. Clones share one state. Events are delivered on the next poll (the next frame’s First).
HttpClient
Makes requests. A resource with shared access only (Res<HttpClient>), so any number of systems in any schedule can use it without ordering against each other; it also works from &World.
HttpConfig
The backend’s settings: a resource the plugin inserts, which the game may change at any time (for example set_base_url after reading its own settings file).
HttpResponse
The answer to a raw request (HttpClient::send, request, get): the response (status 200–299) or why not.
HttpTransportRes
The installed transport. The plugin inserts the HTTP transport (feature http) when the app has none; insert your own (for example a FakeHttpTransport) to replace it.
InFlight
Every request waiting for its answer, HTTP, WebSocket and SSH alike (optional read-only tracking, e.g. for a “saving…” spinner). An HTTP request appears here in PostUpdate (BackendSystems::Send) of the frame it was made in, a WebSocket request or SSH command in the same place (also while it waits for its connection). A request leaves it when it is answered; the answer message follows in First (of that frame, or of the next one for answers decided in PostUpdate, such as a cancel).
JsonBodyFieldjson
A field in the JSON body, e.g. {"token": "<token>", ...}, for APIs that want it there.
JsonEnvelopejson and ws
The default protocol (feature json): JSON objects in text frames.
JsonResponsejson
The answer to a typed JSON request (get_json::<T>, post_json::<T>, send_json::<T>): the decoded T or why not. Register each T once with BackendAppExt::add_json_response.
Multiparthttp
A multipart/form-data form: text fields and files, in order. Build it, then send it with HttpClient::post_multipart (or post_multipart_json, send_multipart, OutgoingRequest::with_multipart).
OutgoingRequest
A request the game builds: method, path (appended to the base URL), query parameters, headers, an optional body and an optional timeout.
PreparedRequest
A request ready for an HttpTransport: the full URL, every header (defaults and credentials applied), the body, the timeout and the response body limit.
RawResponse
What a server sent back: status, headers and the raw body.
Rejection
The error payload of a BackendError::Rejected answer (with the default JSON envelope: the JSON of error in {"id":…,"ok":false,"error":…}).
RequestId
Identifies one request made through bevy_net_backend. Every answer carries the id of the request it answers, so a game can match them.
RequestInfo
What a pending request is, for display (a “saving…” list, a debug overlay). Never holds a query string, header value or body.
RusshTransportssh
The real SshTransport (feature ssh): russh 0.63.3 with ring for the AEAD ciphers, on one private thread with a tokio current-thread runtime, started on the first connect. The plugin inserts one unless an SshTransportRes exists.
Secret
A secret string (token, key, password) that never shows up in Debug, Display or this crate’s logs: both print <redacted>. Read it with expose where it is really needed.
SftpEntrysftp and ssh
One entry of a directory listing. #[non_exhaustive]: build one with new.
SftpFinishedsftp and ssh
The one answer to an SFTP operation. Written in First.
SftpProgresssftp and ssh
Progress of an SFTP transfer: bytes moved so far (at most about 10 per second per transfer). Written in First.
SshAuthssh
How to log in. Several can be given (SshTarget::with_auth); they are tried in order. Keys are the recommendation (a key file or the agent); a password and keyboard-interactive (e.g. password + a 2FA code) are opt-in for servers that need them, with values typed by the admin at runtime. There is deliberately no way to pass key bytes: keys are loaded at runtime from the admin’s machine, never baked into a binary. Debug shows the key file’s name, never its path, contents, passphrase or password.
SshClientssh
Opens, closes and uses named SSH connections (feature ssh). Like HttpClient a resource with shared access only (Res<SshClient>), so any number of systems can use it without ordering. Everything is applied in PostUpdate (BackendSystems::Send).
SshCommandssh
A command to run (SshClient::run). The command line goes to the server’s shell as it is: quote arguments yourself. It may hold secrets (a token in an argument): its Debug shows only its length, and the crate never logs it. Prefer passing secrets through with_stdin; a command line is visible to other users of the server in its process list.
SshConnIdssh
Identifies one connection attempt: each connect of a named connection is a new one. Opaque; Display shows it for logs.
SshConnectionInfossh
What one connection is doing, in SshConnections.
SshConnectionsssh
The state of every named SSH connection (read-only for the game). Every name passed to connect gets an entry, a refused one too (Disconnected with the error). Only connections that are not Disconnected count against SshSettings::with_max_connections; beyond 256 remembered names the oldest Disconnected ones are forgotten.
SshExitssh
How a command ended. A non-zero status is still Ok: the command ran (check success). #[non_exhaustive]: build one with the constructors.
SshFinishedssh
The one answer to SshClient::run: how the command ended, or why it did not run to its end. Written in First.
SshNamessh
The name of an SSH connection ("main", "build-box", …). Cheap to clone.
SshOutputssh
A chunk of a command’s output, as it arrived (a chunk may end in the middle of a line or of a UTF-8 character: join chunks before splitting lines). 0..n per command, all written before or in the same frame as its SshFinished. Debug shows the length only (output may hold secrets); the crate never logs output.
SshPromptssh
One prompt of a keyboard-interactive round. The texts come from the server (untrusted; control characters removed). #[non_exhaustive].
SshPromptAnswersssh
A ready-made SshPromptResponder: answers each prompt whose text contains a given word (case-insensitive), e.g. password and code. A round with a prompt nothing matches gives up. Debug shows the words, never the answers.
SshPromptRequestssh
One keyboard-interactive round: the server’s name and instructions and its prompts (all server-supplied, untrusted). #[non_exhaustive].
SshReconnectssh
Automatic reconnect of an SSH connection (SshTarget::with_reconnect; OFF unless set): exponential backoff with full jitter, as for WebSocket. A reconnect never re-runs a command: commands that were running when the connection was lost are answered Disconnected (with their honest started); commands that had not been sent yet wait for the new connection. Not retried: host key, authentication, protocol (Ssh) and invalid-settings errors. The delay before attempt n is a random value in 0..=min(cap, base · 2^(n-1)); the counter resets after the connection stayed up for stable_after.
SshSettingsssh
App-wide SSH settings, given to the plugin with BackendPlugin::with_ssh. Private fields + builder.
SshStateChangedssh
A connection’s state changed. Written in First.
SshTargetssh
One SSH server and how to reach it, given to SshClient::connect. Private fields + builder.
SshTransportResssh
The installed SSH transport. The plugin inserts a RusshTransport when there is none; insert a FakeSshTransport for tests. Connections of a transport that is removed or replaced count as lost.
TungsteniteTransportws
The real WebSocket transport (feature ws): tungstenite 0.30 (no permessage-deflate) on one std thread per link, named net-backend-ws-link#N. TLS is rustls with ring, passed explicitly, and Mozilla’s roots, exactly like the HTTP transport. No async runtime.
UreqTransporthttp
The real HTTP transport: ureq 3.4 (HTTP/1.1, blocking) with rustls (ring crypto) and the Mozilla root certificates, on HttpConfig::workers threads. The plugin creates it from the config when the app has no HttpTransportRes; create one yourself to install it later.
WsClientws
Opens, closes and uses named WebSocket connections. Like HttpClient a resource with shared access only (Res<WsClient>), so any number of systems can use it without ordering. Everything is applied in PostUpdate (BackendSystems::Send).
WsConnectionInfows
What one connection is doing, in WsConnections.
WsConnectionsws
The state of every named connection (read-only for the game).
WsHandshakews
Everything a transport needs to open one link: the URL, the handshake headers (credentials applied) and the connection’s limits. Built by the plugin; Debug never shows header values or the query.
WsLinkIdws
Identifies one connection attempt (a “link”): each (re)connect of a named connection is a new link. Opaque; Display shows it for logs.
WsMessagews
A data frame the server sent (every one, whether or not the protocol also turned it into an answer or a push). Written in First of the frame it arrived.
WsNamews
The name of a WebSocket connection ("main", "chat", …). Cheap to clone.
WsOutgoingws
A request with a raw payload, for WsClient::request_raw; the protocol wraps it (with JsonEnvelope, kind is "type" and the payload must be JSON).
WsPushjson and ws
A typed server push (features ws + json).
WsRawResponsews
The answer to WsClient::request_raw: the response payload or why not. Exactly one per request. Debug shows the payload length only.
WsReconnectws
Reconnect policy: exponential backoff with full jitter, a cap and optional maximum attempts.
WsResponsejson and ws
The answer to a typed request: the decoded response or why not. Exactly one per request.
WsSettingsws
The settings of one connection, given to WsClient::connect. Private fields + builder.
WsStateChangedws
A connection’s state changed (or an attempt failed). Written in First.
WsTransportResws
The installed WebSocket transport. The plugin inserts a TungsteniteTransport when there is none; insert a FakeWsTransport for tests. Links of a transport that is removed or replaced count as lost (and reconnect on the new one).

Enums§

BackendError
Why a request did not succeed. Every request gets exactly one answer; this is the error half.
BackendSystems
The plugin’s system sets, named after phases: HTTP, WebSocket (feature ws) and SSH (feature ssh) systems all run in them (in that order within a set). #[non_exhaustive]: a later version may add a set.
ConfigError
What is wrong with an HttpConfig (from HttpConfig::validate).
HostKeyProblem
Why an SSH host key was refused (BackendError::HostKey). #[non_exhaustive].
RequestKind
The kind of connection a pending request belongs to. #[non_exhaustive]: later versions may add kinds.
RequestPurpose
What a request is for, so Credentials can treat kinds differently. 0.1.0 only makes Http requests.
SftpEntryKindsftp and ssh
The kind of a directory entry.
SftpOpsftp and ssh
One SFTP operation (what a transport receives; games use the SshClient methods). Remote paths are the server’s (relative paths start in the login directory). Debug shows local paths by file name only and uploads by length only.
SftpOutcomesftp and ssh
What a finished SFTP operation produced. #[non_exhaustive]; Debug shows downloaded data by length only.
SshEventssh
What a transport reports. After Closed a connection reports nothing more; a request reports at most one Finished / SftpFinished. Debug never shows output bytes.
SshStatessh
The state of one named SSH connection. #[non_exhaustive].
SshStreamssh
Which output stream a chunk came from.
WsFramews
One WebSocket data frame. Debug shows the kind and length only (a frame may hold a token).
WsIncomingws
What the protocol made of an incoming frame.
WsLinkEventws
What happened on a link. After Closed or Failed a link reports nothing more.
WsStatews
The state of one named connection. A resource (WsConnections) plus messages (WsStateChanged), not Bevy States. #[non_exhaustive].

Constants§

DEADLINE_GRACE
How long after its own timeout a request is answered with a timeout by the plugin, in case the transport never reports it (a stuck worker, a custom transport that forgets it).
DEFAULT_MAX_BODY_BYTES
The default limit of a response body: 10 MiB. A bigger body is answered with BackendError::BodyTooLarge.
DEFAULT_MULTIPART_MAX_BYTEShttp
Default limit of one encoded form (the whole request body): 32 MiB.
DEFAULT_MULTIPART_MAX_PARTShttp
Default limit of parts in one form.
DEFAULT_SFTP_MAX_BYTESssh
Default limit of one SFTP transfer (upload or download): 256 MiB.
DEFAULT_SFTP_TIMEOUTssh
Default time an SFTP operation (a whole transfer) may take.
DEFAULT_SSH_COMMAND_TIMEOUTssh
Default time a command may run.
DEFAULT_SSH_CONNECT_TIMEOUTssh
Default limit for connect + key exchange + host key check + authentication together.
DEFAULT_SSH_MAX_OUTPUT_BYTESssh
Default limit of a command’s output (stdout + stderr): 8 MiB.
DEFAULT_TIMEOUT
The default request timeout: 15 s for the whole call (connect, send, receive).
DEFAULT_WORKERS
The default number of worker threads of the HTTP transport.
DEFAULT_WS_MAX_MESSAGE_BYTESws
Default largest message (and frame) accepted: 1 MiB.
DEFAULT_WS_READ_TIMEOUTws
Default read timeout of a connection thread.
MAX_SSH_COMMAND_BYTESssh
The longest command line accepted: 64 KiB.
MAX_TIMEOUT
The longest timeout accepted: one hour.
MAX_WORKERS
The most worker threads the HTTP transport starts.

Traits§

BackendAppExt
App extension of this crate (implemented for App only; sealed, so methods behind features never break an outside implementation).
Credentials
Adds the game’s authentication to a request, right before it is sent (after the config’s default headers and the request’s own headers, so it wins over both).
HttpTransport
Moves prepared requests to a server and results back. The plugin owns the bookkeeping (pending requests, deadlines, cancel, exit); a transport only has to deliver.
SshPromptResponderssh
Answers keyboard-interactive prompts (SshAuth::keyboard_interactive). Called on the SSH thread: never block. Return one answer per prompt, or None to give up (the method then fails). Methods added later always come with a default implementation.
SshTransportssh
Opens SSH connections and runs requests on them. The plugin owns the bookkeeping (pending requests, deadlines, cancel, exit) and every answer; a transport only reports. All methods run on the main thread and must never block or panic.
WsProtocolws
How requests and pushes are laid out in frames. Every frame also arrives raw as a WsMessage, whatever the protocol does with it.
WsPushMessagejson and ws
A typed server push (features ws + json): pushes of kind KIND arrive as WsPush<Self>. Register with add_ws_push.
WsRequestjson and ws
A typed WebSocket request (features ws + json), sent with WsClient::request and answered with WsResponse<Self::Response>. Register with add_ws_request.
WsTransportws
Opens links and moves frames. The plugin owns reconnects, requests and every answer; a transport only delivers. send, close and poll run on the main thread and must never block. Methods added later always come with a default implementation.

Type Aliases§

HttpTransportResult
What a transport reports for one request: the server’s answer (any status; the plugin turns a non-2xx status into BackendError::Status) or the error that stopped it.