Skip to content

Commit

Permalink
Split bevy_ggrs example into lib & bin
Browse files Browse the repository at this point in the history
  • Loading branch information
garryod committed Jul 1, 2023
1 parent b7b4117 commit 5931aca
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 198 deletions.
8 changes: 8 additions & 0 deletions examples/bevy_ggrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ repository = "https://github.com/johanhelsing/matchbox"
keywords = ["gamedev", "webrtc", "peer-to-peer", "networking", "wasm"]
license = "MIT OR Apache-2.0"

[lib]
name = "bevy_ggrs_example_lib"
path = "src/lib.rs"

[[bin]]
name = "bevy_ggrs_example"
path = "src/main.rs"

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features = [
"Document",
Expand Down
203 changes: 203 additions & 0 deletions examples/bevy_ggrs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use bevy::{log::LogPlugin, prelude::*};
use bevy_ggrs::{GGRSPlugin, GGRSSchedule, Session};
use bevy_matchbox::prelude::*;
use ggrs::SessionBuilder;

mod args;
mod box_game;

use args::*;
use box_game::*;

const FPS: usize = 60;

#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, States)]
enum AppState {
#[default]
Lobby,
InGame,
}

const SKY_COLOR: Color = Color::rgb(0.69, 0.69, 0.69);

pub fn run() {
// read query string or command line arguments
let args = Args::get();
info!("{:?}", args);

let mut app = App::new();

GGRSPlugin::<GGRSConfig>::new()
// define frequency of rollback game logic update
.with_update_frequency(FPS)
// define system that returns inputs given a player handle, so GGRS can send the inputs
// around
.with_input_system(input)
// register types of components AND resources you want to be rolled back
.register_rollback_component::<Transform>()
.register_rollback_component::<Velocity>()
.register_rollback_resource::<FrameCount>()
// make it happen in the bevy app
.build(&mut app);

app.insert_resource(ClearColor(SKY_COLOR))
.add_plugins(
DefaultPlugins
.set(LogPlugin {
filter: "info,wgpu_core=warn,wgpu_hal=warn,matchbox_socket=debug".into(),
level: bevy::log::Level::DEBUG,
})
.set(WindowPlugin {
primary_window: Some(Window {
title: "Matchbox Bevy GGRS Example".to_string(),
canvas: Some("#bevy_ggrs_example".to_string()),
fit_canvas_to_parent: true, // behave on wasm
..default()
}),
..default()
}),
)
// Some of our systems need the query parameters
.insert_resource(args)
.init_resource::<FrameCount>()
.add_state::<AppState>()
.add_systems((lobby_startup, start_matchbox_socket).in_schedule(OnEnter(AppState::Lobby)))
.add_system(lobby_system.in_set(OnUpdate(AppState::Lobby)))
.add_system(lobby_cleanup.in_schedule(OnExit(AppState::Lobby)))
.add_system(setup_scene_system.in_schedule(OnEnter(AppState::InGame)))
.add_system(log_ggrs_events.in_set(OnUpdate(AppState::InGame)))
// these systems will be executed as part of the advance frame update
.add_systems((move_cube_system, increase_frame_system).in_schedule(GGRSSchedule))
.run();
}

fn start_matchbox_socket(mut commands: Commands, args: Res<Args>) {
let room_id = match &args.room {
Some(id) => id.clone(),
None => format!("bevy_ggrs?next={}", &args.players),
};

let room_url = format!("{}/{}", &args.matchbox, room_id);
info!("connecting to matchbox server: {:?}", room_url);

commands.insert_resource(MatchboxSocket::new_ggrs(room_url));
}

// Marker components for UI
#[derive(Component)]
struct LobbyText;
#[derive(Component)]
struct LobbyUI;

fn lobby_startup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera3dBundle::default());

// All this is just for spawning centered text.
commands
.spawn(NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
position_type: PositionType::Absolute,
justify_content: JustifyContent::Center,
align_items: AlignItems::FlexEnd,
..default()
},
background_color: Color::rgb(0.43, 0.41, 0.38).into(),
..default()
})
.with_children(|parent| {
parent
.spawn(TextBundle {
style: Style {
align_self: AlignSelf::Center,
justify_content: JustifyContent::Center,
..default()
},
text: Text::from_section(
"Entering lobby...",
TextStyle {
font: asset_server.load("fonts/quicksand-light.ttf"),
font_size: 96.,
color: Color::BLACK,
},
),
..default()
})
.insert(LobbyText);
})
.insert(LobbyUI);
}

fn lobby_cleanup(query: Query<Entity, With<LobbyUI>>, mut commands: Commands) {
for e in query.iter() {
commands.entity(e).despawn_recursive();
}
}

fn lobby_system(
mut app_state: ResMut<NextState<AppState>>,
args: Res<Args>,
mut socket: ResMut<MatchboxSocket<SingleChannel>>,
mut commands: Commands,
mut query: Query<&mut Text, With<LobbyText>>,
) {
// regularly call update_peers to update the list of connected peers
for (peer, new_state) in socket.update_peers() {
// you can also handle the specific dis(connections) as they occur:
match new_state {
PeerState::Connected => info!("peer {peer:?} connected"),
PeerState::Disconnected => info!("peer {peer:?} disconnected"),
}
}

let connected_peers = socket.connected_peers().count();
let remaining = args.players - (connected_peers + 1);
query.single_mut().sections[0].value = format!("Waiting for {remaining} more player(s)",);
if remaining > 0 {
return;
}

info!("All peers have joined, going in-game");

// extract final player list
let players = socket.players();

let max_prediction = 12;

// create a GGRS P2P session
let mut sess_build = SessionBuilder::<GGRSConfig>::new()
.with_num_players(args.players)
.with_max_prediction_window(max_prediction)
.with_input_delay(2)
.with_fps(FPS)
.expect("invalid fps");

for (i, player) in players.into_iter().enumerate() {
sess_build = sess_build
.add_player(player, i)
.expect("failed to add player");
}

let channel = socket.take_channel(0).unwrap();

// start the GGRS session
let sess = sess_build
.start_p2p_session(channel)
.expect("failed to start session");

commands.insert_resource(Session::P2PSession(sess));

// transition to in-game state
app_state.set(AppState::InGame);
}

fn log_ggrs_events(mut session: ResMut<Session<GGRSConfig>>) {
match session.as_mut() {
Session::P2PSession(s) => {
for event in s.events() {
info!("GGRS Event: {:?}", event);
}
}
_ => panic!("This example focuses on p2p."),
}
}
Loading

0 comments on commit 5931aca

Please sign in to comment.