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::<NoUserData>::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<ManualEventReader<ServerEvent>>,
mut packet_send: ResMut<Events<ServerEvent>>,
) {
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::<f32>() * 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),
radius: match *planet_type {
PlanetType::Earth => 100.0
}
}));
}
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<ManualEventReader<ServerEvent>>,
mut packet_send: ResMut<Events<ServerEvent>>,
) {
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<Transform>>,
planet_query: Query<(Entity, &PlanetType, &Transform), Changed<Transform>>,
mut packet_send: EventWriter<ServerEvent>,
) {
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),
radius: match *planet_type {
PlanetType::Earth => 100.0,
}
}));
}
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));
}
}