use crate::protocol::*; use crate::server::entity_map::*; use crate::server::network::*; use crate::shared::ability::*; use crate::shared::activation::*; use crate::shared::area_of_effect::*; use crate::shared::buffs::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::player::*; use crate::shared::projectile::*; use crate::shared::stats::*; use crate::shared::*; use bevy::prelude::*; use bevy::utils::*; use lightyear::prelude::*; use lightyear::server::events::MessageEvent; use rand::Rng; pub mod entity_map; mod network; pub fn main(transport: TransportConfig) { App::new() .add_plugins(MinimalPlugins) .add_plugins(ServerPlugin { transport }) .run(); } struct ServerPlugin { pub transport: TransportConfig, } const HEALTH_REGEN_RATE: f32 = 5.; #[derive(Resource)] struct HealthRegenTimer(pub Timer); impl Default for HealthRegenTimer { fn default() -> Self { HealthRegenTimer(Timer::from_seconds(HEALTH_REGEN_RATE, TimerMode::Repeating)) } } impl Plugin for ServerPlugin { fn build(&self, app: &mut App) { app.insert_resource(EntityMap::default()) .insert_resource(HealthRegenTimer::default()) .add_plugins(NetworkPlugin { transport: self.transport.clone(), }) .add_systems(Startup, setup) .add_systems(Update, (connects, disconnects)) .add_systems(Update, receive_message) .add_systems(FixedUpdate, timers_tick) .add_systems(FixedUpdate, effective_stats.after(buffs_despawn)) .add_systems(FixedUpdate, health_regen.after(timers_tick)) .add_systems( FixedUpdate, ( (imperative_attack_approach, imperative_attack_attack) .chain() .after(activation), activation.after(cooldown_decrement), imperative_walk_to, ) .after(player_input), ) .add_systems( FixedUpdate, ( projectile_despawn, projectile_move.after(imperative_walk_to), ) .chain(), ) .add_systems( FixedUpdate, ( area_of_effect_despawn, area_of_effect_tick.after(imperative_walk_to), area_of_effect_activate, ) .chain(), ) .add_systems(FixedUpdate, cooldown_decrement) .add_systems(FixedUpdate, (buffs_despawn, buffs_tick).chain()) .add_systems(FixedUpdate, player_input); } } fn setup(mut commands: Commands, mut entity_map: ResMut) { let client_id = 1; let entity = commands.spawn(PlayerBundle::new(1, Vec2::ZERO, Color::GRAY)); entity_map.0.insert(client_id, entity.id()); } fn connects( mut commands: Commands, mut connects: EventReader, mut entity_map: ResMut, ) { let mut rng = rand::thread_rng(); for connection in connects.read() { let client_id = connection.context(); let position = Vec2::new( 50. * rng.gen_range(-2..=2) as f32, 50. * rng.gen_range(-2..=2) as f32, ); let color = Color::hsl(360. * rng.gen_range(0..=15) as f32 / 16., 0.95, 0.7); let entity = commands.spawn(PlayerBundle::new(*client_id, position, color)); entity_map.0.insert(*client_id, entity.id()); } } fn receive_message( entity_map: Res, mut commands: Commands, mut reader: EventReader>, ) { for event in reader.read() { let client_id = event.context(); let SelectChampion(champion) = event.message(); let Some(entity) = entity_map.0.get(client_id) else { continue; }; commands.entity(*entity).insert(*champion); } } fn disconnects( mut commands: Commands, mut disconnects: EventReader, mut entity_map: ResMut, ) { for connection in disconnects.read() { let client_id = connection.context(); if let Some(entity_id) = entity_map.0.remove(client_id) { commands.entity(entity_id).despawn(); } } } fn player_input( entity_map: Res, mut input_reader: EventReader>, mut imperatives: Query<&mut Imperative>, mut activations: Query<&mut Activation>, ) { for input in input_reader.read() { let client_id = input.context(); if let Some(input) = input.input() { if let Some(entity_id) = entity_map.0.get(client_id) { match input { Inputs::Imperative(new_imperative) => { if let Ok(mut imperative) = imperatives.get_mut(*entity_id) { *imperative = *new_imperative; } } Inputs::Activation(new_activation) => { if let Ok(mut activation) = activations.get_mut(*entity_id) { *activation = *new_activation; } } Inputs::None => {} } } } } } fn imperative_walk_to( mut players: Query<(Entity, &mut Imperative, &EffectiveStats)>, mut positions: Query<&mut PlayerPosition>, time: Res