1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! !
//!
//! This crate provides a [Bevy](https://bevyengine.org/) plugin for integrating with
//! the Steamworks SDK.
//!
//! ## Installation
//! Add the following to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! bevy-steamworks = "0.1.0"
//! ```
//!
//! Ensure that your computer has all the needed
//! [requirements](https://rust-lang.github.io/rust-bindgen/requirements.html) to use
//! [bindgen](https://github.com/rust-lang/rust-bindgen).
//!
//! Download and install the [steamworks sdk](https://partner.steamgames.com/doc/sdk)
//! and set the environment variable `STEAM_SDK_LOCATION` to point to it.
//!
//! At runtime, a "steam_appid.txt" file with the registered Steam App ID of the game
//! is required in the same directory as the executable.
//!
//! ## Usage
//!
//! To add the plugin to your game, simply add the `SteamworksPlugin` to your
//! `AppBuilder`.
//!
//! ```rust
//! use bevy::prelude::*;
//! use bevy_steamworks::SteamworksPlugin;
//!
//! fn main() {
//!   App::build()
//!       .add_plugins(DefaultPlugins)
//!       .add_plugin(SteamworksPlugin)
//!       .run()
//! }
//! ```
//!
//! The plugin adds `steamworks::Client` as a Bevy ECS resource, which can be
//! accessed like any other resource in Bevy. The client implements `Send` and `Sync`
//! and can be used to make requests via the SDK from any of Bevy's threads. However,
//! any asynchronous callbacks from Steam will only run on the main thread.
//!
//! The plugin will automatically call `SingleClient::run_callbacks` on the Bevy
//! main thread every frame, so there is no need to run it manually.
//!
//! **NOTE**: If the plugin fails to initialize (i.e. `Client::init()` fails and
//! returns an error, an error wil lbe logged (via `bevy_log`), but it will not
//! panic. In this case, it may be necessary to use `Option<Res<Client>>` instead.
//!
//! ```rust
//! use bevy_steamworks::{Client, FriendFlags};
//!
//! fn steam_system(steam_client: Res<Client>) {
//!   for friend in client.friends().get_friends(FriendFlags::IMMEDIATE) {
//!     println!("Friend: {:?} - {}({:?})", friend.id(), friend.name(), friend.state());
//!   }
//! }
//!
//! fn main() {
//!   App::build()
//!       .add_plugins(DefaultPlugins)
//!       .add_plugin(SteamworksPlugin)
//!       .add_startup_system(steam_system.system())
//!       .run()
//! }
//! ```

use bevy_app::{AppBuilder, Plugin};
use bevy_ecs::system::{IntoSystem, NonSend};
use bevy_log::error;
pub use steamworks::*;

fn run_steam_callbacks(client: NonSend<SingleClient>) {
    client.run_callbacks();
}

pub struct SteamworksPlugin;

impl Plugin for SteamworksPlugin {
    fn build(&self, app: &mut AppBuilder) {
        match Client::init() {
            Err(err) => error!("Failed to initialize Steamworks client: {}", err),
            Ok((client, single)) => {
                app.insert_resource(client)
                    .insert_non_send_resource(single)
                    .add_system(run_steam_callbacks.system());
            }
        }
    }
}