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::hashbrown::HashMap; use bevy::utils::*; use lightyear::prelude::*; use lightyear::server::events::MessageEvent; use rand::rngs::ThreadRng; 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_select_champion, receive_message_select_faction, ), ) .add_systems( Update, (receive_message_imperative, receive_message_activation), ) .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(receive_message_activation) .after(receive_message_imperative), ) .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, (tower_ai, (minion_ai_attack, minion_ai_walk).chain()) .before(imperative_attack_approach), ) .add_systems(FixedUpdate, despawn) .add_systems(FixedUpdate, player_respawn) .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions)) .add_systems( FixedUpdate, movement_collision .after(imperative_attack_approach) .after(imperative_walk_to), ); } } const TOWER_DISTANCE: f32 = 400.; fn setup(mut commands: Commands, mut entity_map: ResMut) { let [blue_tower_1, blue_tower_2, blue_tower_3, blue_nexus, red_tower_1, red_tower_2, red_tower_3, red_nexus] = generate_client_ids::<8>(&entity_map).into(); for (n, client_id) in [(0, blue_tower_1), (1, blue_tower_2), (2, blue_tower_3)].iter() { let entity = commands.spawn(TowerBundle::new( *client_id, Vec2::new(0., TOWER_DISTANCE / 2. + *n as f32 * TOWER_DISTANCE), Faction::Yellow, )); entity_map.0.insert(*client_id, entity.id()); } { let client_id = blue_nexus; let entity = commands.spawn(NexusBundle::new( client_id, Vec2::new(0., TOWER_DISTANCE / 2. + 3. * TOWER_DISTANCE), Faction::Yellow, )); entity_map.0.insert(client_id, entity.id()); } for (n, client_id) in [(0, red_tower_1), (1, red_tower_2), (2, red_tower_3)].iter() { let entity = commands.spawn(TowerBundle::new( *client_id, Vec2::new(0., -TOWER_DISTANCE / 2. - *n as f32 * TOWER_DISTANCE), Faction::Purple, )); entity_map.0.insert(*client_id, entity.id()); } { let client_id = red_nexus; let entity = commands.spawn(NexusBundle::new( client_id, Vec2::new(0., -TOWER_DISTANCE / 2. - 3. * TOWER_DISTANCE), Faction::Purple, )); 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_select_champion( 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 receive_message_select_faction( entity_map: Res, mut commands: Commands, mut reader: EventReader>, ) { for event in reader.read() { let client_id = event.context(); let SelectFaction(faction) = event.message(); let Some(entity) = entity_map.0.get(client_id) else { continue; }; commands.entity(*entity).insert(*faction); } } fn receive_message_imperative( entity_map: Res, mut commands: Commands, mut reader: EventReader>, ) { for event in reader.read() { let client_id = event.context(); let imperative = event.message(); let Some(entity) = entity_map.0.get(client_id) else { continue; }; commands.entity(*entity).insert(*imperative); } } fn receive_message_activation( entity_map: Res, mut commands: Commands, mut reader: EventReader>, ) { for event in reader.read() { let client_id = event.context(); let activation = event.message(); let Some(entity) = entity_map.0.get(client_id) else { continue; }; commands.entity(*entity).insert(*activation); } } 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 imperative_walk_to( mut players: Query<(Entity, &mut Imperative, &EffectiveStats)>, mut positions: Query<&mut PlayerPosition>, time: Res