network code and stuff
This commit is contained in:
@@ -5,6 +5,11 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bevy = "0.8"
|
||||
bevy_renet = "0.0.5"
|
||||
bevy_egui = "0.15.0"
|
||||
renet_visualizer = "0.0.2"
|
||||
bincode = "1.3.1"
|
||||
daggmask-shared = { path = "../shared" }
|
||||
|
||||
# Enable a small amount of optimization in debug mode
|
||||
[profile.dev]
|
||||
|
@@ -1,59 +1,246 @@
|
||||
use bevy::prelude::*;
|
||||
use std::{collections::HashMap, net::UdpSocket, time::SystemTime};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_startup_system(setup)
|
||||
.add_startup_system(spawn_player)
|
||||
.add_system(move_player)
|
||||
.run();
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use bevy_egui::{EguiContext, EguiPlugin};
|
||||
use bevy_renet::{
|
||||
renet::{ClientAuthentication, RenetClient, RenetError},
|
||||
run_if_client_connected, RenetClientPlugin,
|
||||
};
|
||||
|
||||
use daggmask_shared::{
|
||||
client_connection_config, setup_level, ClientChannel, NetworkFrame, PlayerCommand, PlayerInput,
|
||||
ServerChannel, ServerMessages, PROTOCOL_ID,
|
||||
};
|
||||
|
||||
use renet_visualizer::{RenetClientVisualizer, RenetVisualizerStyle};
|
||||
|
||||
#[derive(Component)]
|
||||
struct ControlledPlayer;
|
||||
|
||||
#[derive(Default)]
|
||||
struct NetworkMapping(HashMap<Entity, Entity>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PlayerInfo {
|
||||
client_entity: Entity,
|
||||
server_entity: Entity,
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
info!("hehe");
|
||||
let mut camera_bundle = Camera2dBundle::default();
|
||||
camera_bundle.projection.scale = 1. / 50.;
|
||||
commands.spawn_bundle(camera_bundle);
|
||||
#[derive(Debug, Default)]
|
||||
struct ClientLobby {
|
||||
players: HashMap<u64, PlayerInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MostRecentTick(Option<u32>);
|
||||
|
||||
fn new_renet_client() -> RenetClient {
|
||||
let server_addr = "127.0.0.1:5000".parse().unwrap();
|
||||
let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let connection_config = client_connection_config();
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
let client_id = current_time.as_millis() as u64;
|
||||
let authentication = ClientAuthentication::Unsecure {
|
||||
client_id,
|
||||
protocol_id: PROTOCOL_ID,
|
||||
server_addr,
|
||||
user_data: None,
|
||||
};
|
||||
|
||||
RenetClient::new(current_time, socket, 1, connection_config, authentication).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(DefaultPlugins);
|
||||
app.add_plugin(RenetClientPlugin);
|
||||
app.add_plugin(TransformPlugin);
|
||||
app.add_plugin(FrameTimeDiagnosticsPlugin::default());
|
||||
app.add_plugin(LogDiagnosticsPlugin::default());
|
||||
app.add_plugin(EguiPlugin);
|
||||
|
||||
app.add_event::<PlayerCommand>();
|
||||
|
||||
app.insert_resource(ClientLobby::default());
|
||||
app.insert_resource(PlayerInput::default());
|
||||
app.insert_resource(MostRecentTick(None));
|
||||
app.insert_resource(new_renet_client());
|
||||
app.insert_resource(RenetClientVisualizer::<200>::new(
|
||||
RenetVisualizerStyle::default(),
|
||||
));
|
||||
app.insert_resource(NetworkMapping::default());
|
||||
|
||||
app.add_system(player_input);
|
||||
app.add_system(client_send_input.with_run_criteria(run_if_client_connected));
|
||||
app.add_system(client_send_player_commands.with_run_criteria(run_if_client_connected));
|
||||
app.add_system(client_sync_players.with_run_criteria(run_if_client_connected));
|
||||
app.add_system(update_visulizer_system);
|
||||
|
||||
app.add_startup_system(setup_level);
|
||||
app.add_system(panic_on_error_system);
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
// If any error is found we just panic
|
||||
fn panic_on_error_system(mut renet_error: EventReader<RenetError>) {
|
||||
for e in renet_error.iter() {
|
||||
panic!("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_visulizer_system(
|
||||
mut egui_context: ResMut<EguiContext>,
|
||||
mut visualizer: ResMut<RenetClientVisualizer<200>>,
|
||||
client: Res<RenetClient>,
|
||||
mut show_visualizer: Local<bool>,
|
||||
keyboard_input: Res<Input<KeyCode>>,
|
||||
) {
|
||||
visualizer.add_network_info(client.network_info());
|
||||
if keyboard_input.just_pressed(KeyCode::F1) {
|
||||
*show_visualizer = !*show_visualizer;
|
||||
}
|
||||
if *show_visualizer {
|
||||
visualizer.show_window(egui_context.ctx_mut());
|
||||
}
|
||||
}
|
||||
|
||||
fn player_input(
|
||||
keyboard_input: Res<Input<KeyCode>>,
|
||||
mut player_input: ResMut<PlayerInput>,
|
||||
mouse_button_input: Res<Input<MouseButton>>,
|
||||
target_query: Query<&Transform, With<Target>>,
|
||||
mut player_commands: EventWriter<PlayerCommand>,
|
||||
most_recent_tick: Res<MostRecentTick>,
|
||||
) {
|
||||
player_input.left = keyboard_input.pressed(KeyCode::A) || keyboard_input.pressed(KeyCode::Left);
|
||||
player_input.right =
|
||||
keyboard_input.pressed(KeyCode::D) || keyboard_input.pressed(KeyCode::Right);
|
||||
player_input.up = keyboard_input.pressed(KeyCode::W) || keyboard_input.pressed(KeyCode::Up);
|
||||
player_input.down = keyboard_input.pressed(KeyCode::S) || keyboard_input.pressed(KeyCode::Down);
|
||||
player_input.most_recent_tick = most_recent_tick.0;
|
||||
|
||||
if mouse_button_input.just_pressed(MouseButton::Left) {
|
||||
player_commands.send(PlayerCommand::BasicAttack {
|
||||
cast_at: Vec2::default(), // TODO: spawn projectiles correctly
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn client_send_input(player_input: Res<PlayerInput>, mut client: ResMut<RenetClient>) {
|
||||
let input_message = bincode::serialize(&*player_input).unwrap();
|
||||
|
||||
client.send_message(ClientChannel::Input.id(), input_message);
|
||||
}
|
||||
|
||||
fn client_send_player_commands(
|
||||
mut player_commands: EventReader<PlayerCommand>,
|
||||
mut client: ResMut<RenetClient>,
|
||||
) {
|
||||
for command in player_commands.iter() {
|
||||
let command_message = bincode::serialize(command).unwrap();
|
||||
client.send_message(ClientChannel::Command.id(), command_message);
|
||||
}
|
||||
}
|
||||
|
||||
fn client_sync_players(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut client: ResMut<RenetClient>,
|
||||
mut lobby: ResMut<ClientLobby>,
|
||||
mut network_mapping: ResMut<NetworkMapping>,
|
||||
mut most_recent_tick: ResMut<MostRecentTick>,
|
||||
) {
|
||||
let client_id = client.client_id();
|
||||
while let Some(message) = client.receive_message(ServerChannel::ServerMessages.id()) {
|
||||
let server_message = bincode::deserialize(&message).unwrap();
|
||||
match server_message {
|
||||
ServerMessages::PlayerCreate {
|
||||
id,
|
||||
translation,
|
||||
entity,
|
||||
} => {
|
||||
println!("Player {} connected.", id);
|
||||
let mut client_entity = commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Capsule::default())),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
transform: Transform::from_xyz(translation[0], translation[1], translation[2]),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if client_id == id {
|
||||
client_entity.insert(ControlledPlayer);
|
||||
}
|
||||
|
||||
let player_info = PlayerInfo {
|
||||
server_entity: entity,
|
||||
client_entity: client_entity.id(),
|
||||
};
|
||||
lobby.players.insert(id, player_info);
|
||||
network_mapping.0.insert(entity, client_entity.id());
|
||||
}
|
||||
ServerMessages::PlayerRemove { id } => {
|
||||
println!("Player {} disconnected.", id);
|
||||
if let Some(PlayerInfo {
|
||||
server_entity,
|
||||
client_entity,
|
||||
}) = lobby.players.remove(&id)
|
||||
{
|
||||
commands.entity(client_entity).despawn();
|
||||
network_mapping.0.remove(&server_entity);
|
||||
}
|
||||
}
|
||||
ServerMessages::SpawnProjectile {
|
||||
entity,
|
||||
location,
|
||||
direction,
|
||||
} => {
|
||||
let projectile_entity = commands.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::rgb(0.25, 0.25, 0.75),
|
||||
custom_size: Some(Vec2::new(50.0, 100.0)),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
|
||||
network_mapping.0.insert(entity, projectile_entity.id());
|
||||
}
|
||||
ServerMessages::DespawnProjectile { entity } => {
|
||||
if let Some(entity) = network_mapping.0.remove(&entity) {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(message) = client.receive_message(ServerChannel::NetworkFrame.id()) {
|
||||
let frame: NetworkFrame = bincode::deserialize(&message).unwrap();
|
||||
match most_recent_tick.0 {
|
||||
None => most_recent_tick.0 = Some(frame.tick),
|
||||
Some(tick) if tick < frame.tick => most_recent_tick.0 = Some(frame.tick),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
for i in 0..frame.entities.entities.len() {
|
||||
if let Some(entity) = network_mapping.0.get(&frame.entities.entities[i]) {
|
||||
let translation = frame.entities.translations[i].into();
|
||||
let transform = Transform {
|
||||
translation,
|
||||
..Default::default()
|
||||
};
|
||||
commands.entity(*entity).insert(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Player;
|
||||
|
||||
fn spawn_player(mut commands: Commands) {
|
||||
commands
|
||||
.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite {
|
||||
color: Color::rgb(0., 0.47, 1.),
|
||||
custom_size: Some(Vec2::new(1., 1.)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Player);
|
||||
}
|
||||
|
||||
fn move_player(keys: Res<Input<KeyCode>>, mut player_query: Query<&mut Transform, With<Player>>) {
|
||||
let mut direction = Vec2::ZERO;
|
||||
if keys.any_pressed([KeyCode::Up, KeyCode::W]) {
|
||||
direction.y += 1.;
|
||||
}
|
||||
if keys.any_pressed([KeyCode::Down, KeyCode::S]) {
|
||||
direction.y -= 1.;
|
||||
}
|
||||
if keys.any_pressed([KeyCode::Right, KeyCode::D]) {
|
||||
direction.x += 1.;
|
||||
}
|
||||
if keys.any_pressed([KeyCode::Left, KeyCode::A]) {
|
||||
direction.x -= 1.;
|
||||
}
|
||||
if direction == Vec2::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
let move_speed = 0.13;
|
||||
let move_delta = (direction * move_speed).extend(0.);
|
||||
|
||||
for mut transform in player_query.iter_mut() {
|
||||
transform.translation += move_delta;
|
||||
}
|
||||
}
|
||||
struct Target;
|
||||
|
Reference in New Issue
Block a user