use std::net::Ipv4Addr; use bevy::utils::tracing; use bevy::{ecs::event::ManualEventReader, prelude::*}; use bevy_twite::{twite::frame::MessageType, ServerEvent, TwiteServerConfig, TwiteServerPlugin}; use bevy_rapier2d::prelude::*; use component::*; use packet::*; use rand::Rng; pub mod component; pub mod packet; pub mod macros; const SCALE: f32 = 10.0; fn main() { let subscriber = tracing_subscriber::FmtSubscriber::new(); tracing::subscriber::set_global_default(subscriber).unwrap(); info!( "StarKingdoms server v{} starting up", env!("CARGO_PKG_VERSION") ); App::new() .insert_resource(TwiteServerConfig { addr: Ipv4Addr::new(0, 0, 0, 0), port: 3000 }) .add_plugins(MinimalPlugins) .insert_resource(RapierConfiguration { gravity: Vect { x: 0., y: 0. }, ..Default::default() }) .add_plugins(RapierPhysicsPlugin::::pixels_per_meter(SCALE)) .add_plugins(TwiteServerPlugin) .add_systems(Startup, spawn_planets) //.add_systems(Update, on_connection) .add_systems(Update, on_message) .add_systems(Update, on_close) .add_systems(FixedUpdate, on_position_change) .run(); info!("Goodbye!"); } fn spawn_planets(mut commands: Commands) { info!("Spawning planets"); let earth_pos = Transform::from_xyz(0.0, 0.0, 0.0); let earth_radius = 100.0; commands .spawn(PlanetBundle { planet_type: PlanetType::Earth, transform: TransformBundle::from(earth_pos), }) .insert(Collider::ball(earth_radius)) .insert(RigidBody::Fixed); } fn on_message( mut commands: Commands, planet_query: Query<(Entity, &PlanetType, &Transform)>, player_query: Query<(Entity, &Player)>, part_query: Query<(Entity, &PartType, &Transform)>, mut packet_recv: Local>, mut packet_send: ResMut>, ) { let mut packets = Vec::new(); for ev in packet_recv.read(&packet_send) { if let ServerEvent::Recv(addr, MessageType::Text, data) = ev { let data = String::from_utf8_lossy(&data); let json: serde_json::Value = err_or_cont!(serde_json::from_str(&data)); let packet_type = json["t"].clone(); let data = json["c"].clone(); let packet_type = some_or_cont!(packet_type.as_str()); match packet_type { // handshake "ClientLogin" => { // spawn player let username = data["username"].clone(); let username = some_or_cont!(username.as_str()); let angle: f32 = { let mut rng = rand::thread_rng(); rng.gen::() * std::f32::consts::PI * 2. }; let mut transform = Transform::from_xyz(angle.cos() * 210., angle.sin() * 210., 0.0); transform.rotate_z(angle); let id = commands .spawn(PlayerBundle { part: PartBundle { part_type: PartType::Hearty, transform: TransformBundle::from(transform), }, player: Player { addr: *addr, username: username.to_string() }, }) .insert(Collider::cuboid(10.0, 10.0)) .insert(RigidBody::Dynamic) .id().index(); // tell this player the planets let mut planets = Vec::new(); for (entity, planet_type, transform) in planet_query.iter() { planets.push((entity.index(), Planet { planet_type: *planet_type, transform: proto_transform!(transform) })); } let packet = Packet::PlanetPositions { planets }; let buf = serde_json::to_vec(&packet).unwrap(); packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); // tell the player already existing users let mut players = Vec::new(); for (entity, player) in &player_query { players.push((entity.index(), player.username.clone())); } let packet = Packet::PlayerList { players, }; let buf = serde_json::to_vec(&packet).unwrap(); packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); // tell other players that a player has spawned in let packet = Packet::SpawnPlayer { id, username: username.to_string(), }; let buf = serde_json::to_vec(&packet).unwrap(); packets.push(ServerEvent::Broadcast(MessageType::Text, buf)); // tell the player where parts are let mut parts = Vec::new(); for (entity, part_type, transform) in &part_query { parts.push((entity.index(), Part { part_type: *part_type, transform: proto_transform!(transform), })); } parts.push((id, Part { part_type: PartType::Hearty, transform: proto_transform!(transform), })); let packet = Packet::PartPositions { parts }; let buf = serde_json::to_vec(&packet).unwrap(); packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); } _ => continue }; } } for packet in packets { packet_send.send(packet); } } fn on_close( player_query: Query<(Entity, &Player)>, mut commands: Commands, mut packet_recv: Local>, mut packet_send: ResMut>, ) { let mut packets = Vec::new(); for packet in packet_recv.read(&packet_send) { if let ServerEvent::Close(addr) = packet { for (entity, player) in &player_query { if player.addr == *addr { commands.entity(entity).despawn(); let packet = Packet::PlayerLeave { id: entity.index() }; let buf = serde_json::to_vec(&packet).unwrap(); for (in_entity, player) in &player_query { if entity != in_entity { packets.push(ServerEvent::Send(player.addr, MessageType::Text, buf.clone())); } } } } } } for packet in packets { packet_send.send(packet); } } fn on_position_change( mut commands: Commands, part_query: Query<(Entity, &PartType, &Transform), Changed>, planet_query: Query<(Entity, &PlanetType, &Transform), Changed>, mut packet_send: EventWriter, ) { let mut updated_parts = Vec::new(); for (entity, part_type, transform) in part_query.iter() { let id = commands.entity(entity).id().index(); updated_parts.push((id, Part { part_type: *part_type, transform: proto_transform!(transform), })); } if !updated_parts.is_empty() { let packet = Packet::PartPositions { parts: updated_parts }; let buf = serde_json::to_vec(&packet).unwrap(); packet_send.send(ServerEvent::Broadcast(MessageType::Text, buf)); } let mut planets = Vec::new(); for (entity, planet_type, transform) in planet_query.iter() { let id = commands.entity(entity).id().index(); planets.push((id, Planet { planet_type: *planet_type, transform: proto_transform!(transform) })); } if !planets.is_empty() { let packet = Packet::PlanetPositions { planets }; let buf = serde_json::to_vec(&packet).unwrap(); packet_send.send(ServerEvent::Broadcast(MessageType::Text, buf)); } }