network code and stuff
This commit is contained in:
parent
e5a83229f1
commit
b0eadd5420
805
Cargo.lock
generated
805
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["client", "server"]
|
||||
members = ["client", "server", "shared"]
|
||||
|
@ -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;
|
||||
|
@ -3,5 +3,12 @@ name = "daggmask-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[dependencies]
|
||||
bevy = "0.8.0"
|
||||
bevy_renet = "0.0.5"
|
||||
bevy_egui = "0.15.0"
|
||||
bevy_rapier2d = "0.16.0"
|
||||
renet_visualizer = "0.0.2"
|
||||
bincode = "1.3.1"
|
||||
|
||||
daggmask-shared = { path = "../shared" }
|
||||
|
@ -1,45 +1,291 @@
|
||||
use std::net::UdpSocket;
|
||||
use std::{collections::HashMap, net::UdpSocket, time::SystemTime};
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
// replace xxxx with your desired port
|
||||
// replace S.S.S.S with your server address
|
||||
let socket = UdpSocket::bind("S.S.S.S:xxxx")?;
|
||||
let mut peers: Vec<String> = vec![];
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
loop {
|
||||
let mut buf = [0; 1024];
|
||||
let (_, src) = socket.recv_from(&mut buf)?;
|
||||
let stringified_buff = String::from_utf8(buf.to_vec()).unwrap();
|
||||
let stringified_buff = stringified_buff.trim_matches(char::from(0));
|
||||
use bevy_egui::{EguiContext, EguiPlugin};
|
||||
use bevy_rapier2d::prelude::*;
|
||||
use bevy_renet::{
|
||||
renet::{RenetServer, ServerAuthentication, ServerConfig, ServerEvent},
|
||||
RenetServerPlugin,
|
||||
};
|
||||
|
||||
println!("[NEW MESSAGE]{:?} => {:?}", src, stringified_buff);
|
||||
use daggmask_shared::{
|
||||
server_connection_config, setup_level, spawn_projectile, ClientChannel, NetworkFrame, Player,
|
||||
PlayerCommand, PlayerInput, Projectile, ServerChannel, ServerMessages, PROTOCOL_ID,
|
||||
};
|
||||
|
||||
if stringified_buff != "register" {
|
||||
continue;
|
||||
use renet_visualizer::RenetServerVisualizer;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ServerLobby {
|
||||
pub players: HashMap<u64, Entity>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct NetworkTick(u32);
|
||||
|
||||
// Clients last received ticks
|
||||
#[derive(Debug, Default)]
|
||||
struct ClientTicks(HashMap<u64, Option<u32>>);
|
||||
|
||||
const PLAYER_MOVE_SPEED: f32 = 5.0;
|
||||
|
||||
fn new_renet_server() -> RenetServer {
|
||||
let server_addr = "127.0.0.1:5000".parse().unwrap();
|
||||
let socket = UdpSocket::bind(server_addr).unwrap();
|
||||
let connection_config = server_connection_config();
|
||||
let server_config =
|
||||
ServerConfig::new(64, PROTOCOL_ID, server_addr, ServerAuthentication::Unsecure);
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
RenetServer::new(current_time, server_config, connection_config, socket).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(DefaultPlugins);
|
||||
|
||||
app.add_plugin(RenetServerPlugin);
|
||||
app.add_plugin(RapierPhysicsPlugin::<NoUserData>::default());
|
||||
app.add_plugin(RapierDebugRenderPlugin::default());
|
||||
app.add_plugin(FrameTimeDiagnosticsPlugin::default());
|
||||
app.add_plugin(LogDiagnosticsPlugin::default());
|
||||
app.add_plugin(EguiPlugin);
|
||||
|
||||
app.insert_resource(ServerLobby::default());
|
||||
app.insert_resource(NetworkTick(0));
|
||||
app.insert_resource(ClientTicks::default());
|
||||
app.insert_resource(new_renet_server());
|
||||
app.insert_resource(RenetServerVisualizer::<200>::default());
|
||||
|
||||
app.add_system(server_update_system);
|
||||
app.add_system(server_network_sync);
|
||||
app.add_system(move_players_system);
|
||||
app.add_system(update_projectiles_system);
|
||||
app.add_system(update_visulizer_system);
|
||||
app.add_system(despawn_projectile_system);
|
||||
app.add_system_to_stage(CoreStage::PostUpdate, projectile_on_removal_system);
|
||||
|
||||
app.add_startup_system(setup_level);
|
||||
app.add_startup_system(setup_simple_camera);
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn server_update_system(
|
||||
mut server_events: EventReader<ServerEvent>,
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut lobby: ResMut<ServerLobby>,
|
||||
mut server: ResMut<RenetServer>,
|
||||
mut visualizer: ResMut<RenetServerVisualizer<200>>,
|
||||
mut client_ticks: ResMut<ClientTicks>,
|
||||
players: Query<(Entity, &Player, &Transform)>,
|
||||
) {
|
||||
for event in server_events.iter() {
|
||||
match event {
|
||||
ServerEvent::ClientConnected(id, _) => {
|
||||
println!("Player {} connected.", id);
|
||||
visualizer.add_client(*id);
|
||||
|
||||
// Initialize other players for this new client
|
||||
for (entity, player, transform) in players.iter() {
|
||||
let translation: [f32; 3] = transform.translation.into();
|
||||
let message = bincode::serialize(&ServerMessages::PlayerCreate {
|
||||
id: player.id,
|
||||
entity,
|
||||
translation,
|
||||
})
|
||||
.unwrap();
|
||||
server.send_message(*id, ServerChannel::ServerMessages.id(), message);
|
||||
}
|
||||
|
||||
// Spawn new player
|
||||
let transform = Transform::from_xyz(0., 0.51, 0.);
|
||||
let player_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,
|
||||
..Default::default()
|
||||
})
|
||||
.insert(RigidBody::Dynamic)
|
||||
.insert(LockedAxes::ROTATION_LOCKED | LockedAxes::TRANSLATION_LOCKED_Y)
|
||||
.insert(Collider::capsule_y(0.5, 0.5))
|
||||
.insert(PlayerInput::default())
|
||||
.insert(Velocity::default())
|
||||
.insert(Player {
|
||||
id: *id,
|
||||
location: Vec2::new(10., 10.),
|
||||
})
|
||||
.id();
|
||||
|
||||
lobby.players.insert(*id, player_entity);
|
||||
|
||||
let translation: [f32; 3] = transform.translation.into();
|
||||
let message = bincode::serialize(&ServerMessages::PlayerCreate {
|
||||
id: *id,
|
||||
entity: player_entity,
|
||||
translation,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
server.broadcast_message(ServerChannel::ServerMessages.id(), message);
|
||||
}
|
||||
ServerEvent::ClientDisconnected(id) => {
|
||||
println!("Player {} disconnected.", id);
|
||||
visualizer.remove_client(*id);
|
||||
client_ticks.0.remove(id);
|
||||
if let Some(player_entity) = lobby.players.remove(id) {
|
||||
commands.entity(player_entity).despawn();
|
||||
}
|
||||
|
||||
let message =
|
||||
bincode::serialize(&ServerMessages::PlayerRemove { id: *id }).unwrap();
|
||||
server.broadcast_message(ServerChannel::ServerMessages.id(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for client_id in server.clients_id().into_iter() {
|
||||
while let Some(message) = server.receive_message(client_id, ClientChannel::Command.id()) {
|
||||
let command: PlayerCommand = bincode::deserialize(&message).unwrap();
|
||||
match command {
|
||||
PlayerCommand::BasicAttack { mut cast_at } => {
|
||||
println!(
|
||||
"Received basic attack from client {}: {:?}",
|
||||
client_id, cast_at
|
||||
);
|
||||
|
||||
if let Some(player_entity) = lobby.players.get(&client_id) {
|
||||
if let Ok((_, _, player_transform)) = players.get(*player_entity) {
|
||||
cast_at[1] = player_transform.translation[1];
|
||||
|
||||
let projectile_entity =
|
||||
spawn_projectile(&mut commands, cast_at, cast_at);
|
||||
|
||||
let message = ServerMessages::SpawnProjectile {
|
||||
entity: projectile_entity,
|
||||
location: cast_at,
|
||||
direction: cast_at,
|
||||
};
|
||||
|
||||
let message = bincode::serialize(&message).unwrap();
|
||||
server.broadcast_message(ServerChannel::ServerMessages.id(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !peers.contains(&format!("{}", src)) {
|
||||
peers.push(format!("{}", src));
|
||||
}
|
||||
while let Some(message) = server.receive_message(client_id, ClientChannel::Input.id()) {
|
||||
let input: PlayerInput = bincode::deserialize(&message).unwrap();
|
||||
client_ticks.0.insert(client_id, input.most_recent_tick);
|
||||
|
||||
for p in &peers {
|
||||
let filtered_peers = filter_peers(&peers, p);
|
||||
|
||||
if !filtered_peers.is_empty() {
|
||||
socket.send_to(filtered_peers.join(",").as_bytes(), p)?;
|
||||
if let Some(player_entity) = lobby.players.get(&client_id) {
|
||||
commands.entity(*player_entity).insert(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_peers(peers: &Vec<String>, filter: &String) -> Vec<String> {
|
||||
let mut new_peers: Vec<String> = vec![];
|
||||
fn update_projectiles_system(
|
||||
mut commands: Commands,
|
||||
mut projectiles: Query<(Entity, &mut Projectile)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, mut projectile) in projectiles.iter_mut() {
|
||||
projectile.duration.tick(time.delta());
|
||||
|
||||
for p in peers {
|
||||
if p != filter {
|
||||
new_peers.push(String::from(p));
|
||||
if projectile.duration.finished() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
new_peers
|
||||
}
|
||||
|
||||
fn update_visulizer_system(
|
||||
mut egui_context: ResMut<EguiContext>,
|
||||
mut visualizer: ResMut<RenetServerVisualizer<200>>,
|
||||
server: Res<RenetServer>,
|
||||
) {
|
||||
visualizer.update(&server);
|
||||
visualizer.show_window(egui_context.ctx_mut());
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn server_network_sync(
|
||||
mut tick: ResMut<NetworkTick>,
|
||||
mut server: ResMut<RenetServer>,
|
||||
networked_entities: Query<(Entity, &Transform), Or<(With<Player>, With<Projectile>)>>,
|
||||
) {
|
||||
let mut frame = NetworkFrame::default();
|
||||
|
||||
for (entity, transform) in networked_entities.iter() {
|
||||
frame.entities.entities.push(entity);
|
||||
frame
|
||||
.entities
|
||||
.translations
|
||||
.push(transform.translation.into());
|
||||
}
|
||||
|
||||
frame.tick = tick.0;
|
||||
tick.0 += 1;
|
||||
|
||||
let sync_message = bincode::serialize(&frame).unwrap();
|
||||
|
||||
server.broadcast_message(ServerChannel::NetworkFrame.id(), sync_message);
|
||||
}
|
||||
|
||||
fn move_players_system(mut query: Query<(&mut Velocity, &PlayerInput)>) {
|
||||
for (mut velocity, input) in query.iter_mut() {
|
||||
let x = (input.right as i8 - input.left as i8) as f32;
|
||||
let y = (input.down as i8 - input.up as i8) as f32;
|
||||
let direction = Vec2::new(x, y).normalize_or_zero();
|
||||
velocity.linvel.x = direction.x * PLAYER_MOVE_SPEED;
|
||||
velocity.linvel.y = direction.y * PLAYER_MOVE_SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_simple_camera(mut commands: Commands) {
|
||||
// camera
|
||||
commands.spawn_bundle(Camera3dBundle {
|
||||
transform: Transform::from_xyz(-5.5, 5.0, 5.5).looking_at(Vec3::ZERO, Vec3::Y),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
fn despawn_projectile_system(
|
||||
mut commands: Commands,
|
||||
mut collision_events: EventReader<CollisionEvent>,
|
||||
projectile_query: Query<Option<&Projectile>>,
|
||||
) {
|
||||
for collision_event in collision_events.iter() {
|
||||
if let CollisionEvent::Started(entity1, entity2, _) = collision_event {
|
||||
if let Ok(Some(_)) = projectile_query.get(*entity1) {
|
||||
commands.entity(*entity1).despawn();
|
||||
}
|
||||
|
||||
if let Ok(Some(_)) = projectile_query.get(*entity2) {
|
||||
commands.entity(*entity2).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn projectile_on_removal_system(
|
||||
mut server: ResMut<RenetServer>,
|
||||
removed_projectiles: RemovedComponents<Projectile>,
|
||||
) {
|
||||
for entity in removed_projectiles.iter() {
|
||||
let message = ServerMessages::DespawnProjectile { entity };
|
||||
let message = bincode::serialize(&message).unwrap();
|
||||
|
||||
server.broadcast_message(ServerChannel::ServerMessages.id(), message);
|
||||
}
|
||||
}
|
||||
|
11
shared/Cargo.toml
Normal file
11
shared/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "daggmask-shared"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bevy = "0.8.0"
|
||||
bevy_renet = "0.0.5"
|
||||
bevy_egui = "0.15.0"
|
||||
bevy_rapier2d = "0.16.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
170
shared/src/lib.rs
Normal file
170
shared/src/lib.rs
Normal file
@ -0,0 +1,170 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_renet::renet::{
|
||||
ChannelConfig, ReliableChannelConfig, RenetConnectionConfig, UnreliableChannelConfig,
|
||||
NETCODE_KEY_BYTES,
|
||||
};
|
||||
|
||||
use bevy_rapier2d::geometry::Collider;
|
||||
use bevy_rapier2d::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const PRIVATE_KEY: &[u8; NETCODE_KEY_BYTES] = b"en grisars katt hund hemlis key."; // 32-bytes
|
||||
pub const PROTOCOL_ID: u64 = 7;
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Player {
|
||||
pub id: u64,
|
||||
pub location: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Component)]
|
||||
pub struct PlayerInput {
|
||||
pub most_recent_tick: Option<u32>,
|
||||
pub up: bool,
|
||||
pub down: bool,
|
||||
pub left: bool,
|
||||
pub right: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Component)]
|
||||
pub enum PlayerCommand {
|
||||
BasicAttack { cast_at: Vec2 },
|
||||
}
|
||||
|
||||
pub enum ClientChannel {
|
||||
Input,
|
||||
Command,
|
||||
}
|
||||
|
||||
pub enum ServerChannel {
|
||||
ServerMessages,
|
||||
NetworkFrame,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Component)]
|
||||
pub enum ServerMessages {
|
||||
PlayerCreate {
|
||||
entity: Entity,
|
||||
id: u64,
|
||||
translation: [f32; 3],
|
||||
},
|
||||
PlayerRemove {
|
||||
id: u64,
|
||||
},
|
||||
SpawnProjectile {
|
||||
entity: Entity,
|
||||
location: Vec2,
|
||||
direction: Vec2,
|
||||
},
|
||||
DespawnProjectile {
|
||||
entity: Entity,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct NetworkedEntities {
|
||||
pub entities: Vec<Entity>,
|
||||
pub translations: Vec<[f32; 3]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct NetworkFrame {
|
||||
pub tick: u32,
|
||||
pub entities: NetworkedEntities,
|
||||
}
|
||||
|
||||
impl ClientChannel {
|
||||
pub fn id(&self) -> u8 {
|
||||
match self {
|
||||
Self::Input => 0,
|
||||
Self::Command => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channels_config() -> Vec<ChannelConfig> {
|
||||
vec![
|
||||
ReliableChannelConfig {
|
||||
channel_id: Self::Input.id(),
|
||||
message_resend_time: Duration::ZERO,
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
ReliableChannelConfig {
|
||||
channel_id: Self::Command.id(),
|
||||
message_resend_time: Duration::ZERO,
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerChannel {
|
||||
pub fn id(&self) -> u8 {
|
||||
match self {
|
||||
Self::NetworkFrame => 0,
|
||||
Self::ServerMessages => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channels_config() -> Vec<ChannelConfig> {
|
||||
vec![
|
||||
UnreliableChannelConfig {
|
||||
channel_id: Self::NetworkFrame.id(),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
ReliableChannelConfig {
|
||||
channel_id: Self::ServerMessages.id(),
|
||||
message_resend_time: Duration::from_millis(200),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_connection_config() -> RenetConnectionConfig {
|
||||
RenetConnectionConfig {
|
||||
send_channels_config: ClientChannel::channels_config(),
|
||||
receive_channels_config: ServerChannel::channels_config(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_connection_config() -> RenetConnectionConfig {
|
||||
RenetConnectionConfig {
|
||||
send_channels_config: ServerChannel::channels_config(),
|
||||
receive_channels_config: ClientChannel::channels_config(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// set up the level
|
||||
pub fn setup_level(mut _commands: Commands) {
|
||||
info!("bygger level...");
|
||||
}
|
||||
|
||||
pub fn spawn_projectile(commands: &mut Commands, location: Vec2, direction: Vec2) -> Entity {
|
||||
commands
|
||||
.spawn()
|
||||
.insert(Collider::ball(0.1))
|
||||
.insert(Velocity::linear(direction * 10.))
|
||||
.insert(ActiveEvents::COLLISION_EVENTS)
|
||||
.insert(Projectile {
|
||||
duration: Timer::from_seconds(1.5, false),
|
||||
location,
|
||||
direction,
|
||||
})
|
||||
.id()
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Projectile {
|
||||
pub duration: Timer,
|
||||
pub location: Vec2,
|
||||
pub direction: Vec2,
|
||||
}
|
Loading…
Reference in New Issue
Block a user