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::faction::*; use crate::shared::health::*; use crate::shared::health_event::*; use crate::shared::immovable::*; use crate::shared::imperative::*; use crate::shared::minion::*; use crate::shared::nexus::*; use crate::shared::player::*; use crate::shared::projectile::*; use crate::shared::shape::*; use crate::shared::stats::*; use crate::shared::tower::*; 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) .add_systems( FixedUpdate, (tower_ai, (minion_ai_attack, minion_ai_walk).chain()) .before(imperative_attack_approach), ) .add_systems(FixedUpdate, minion_despawn) .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions)); } } fn setup(mut commands: Commands, mut entity_map: ResMut) { { let client_id = 1; let entity = commands.spawn(TowerBundle::new( client_id, Vec2::new(0., 100.), Faction::Blue, )); entity_map.0.insert(client_id, entity.id()); } { let client_id = 2; let entity = commands.spawn(TowerBundle::new( client_id, Vec2::new(0., -100.), Faction::Red, )); entity_map.0.insert(client_id, entity.id()); } for client_id in 3..=8 { let entity = commands.spawn(MinionBundle::new( client_id, Vec2::new((client_id - 2) as f32 * 25., 0.), Faction::Blue, )); entity_map.0.insert(client_id, entity.id()); } for client_id in 9..=14 { let entity = commands.spawn(MinionBundle::new( client_id, Vec2::new((client_id - 8) as f32 * -25., 0.), Faction::Red, )); entity_map.0.insert(client_id, entity.id()); } { let client_id = 15; let entity = commands.spawn(NexusBundle::new( client_id, Vec2::new(100., 100.0), Faction::Blue, )); entity_map.0.insert(client_id, entity.id()); } { let client_id = 16; let entity = commands.spawn(NexusBundle::new( client_id, Vec2::new(-100., -100.0), Faction::Red, )); 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