From 66e5b4bcfec6d5c81b80e1d2b61e6d1fed199e3f Mon Sep 17 00:00:00 2001 From: ghostlyzsh Date: Thu, 25 Jul 2024 11:40:09 -0500 Subject: [PATCH] oops now basic organizational fixes are done --- server/src/component.rs | 53 --- server/src/main.rs | 778 +-------------------------------- server/src/module/component.rs | 1 - server/src/module/mod.rs | 9 +- server/src/module/save.rs | 4 +- server/src/module/thruster.rs | 2 +- server/src/player/component.rs | 33 ++ server/src/player/mod.rs | 636 +++++++++++++++++++++++++++ server/src/player/packet.rs | 120 +++++ 9 files changed, 818 insertions(+), 818 deletions(-) delete mode 100644 server/src/component.rs create mode 100644 server/src/player/component.rs create mode 100644 server/src/player/packet.rs diff --git a/server/src/component.rs b/server/src/component.rs deleted file mode 100644 index b39b82716a70e7dd89a272acfb2e5651655302aa..0000000000000000000000000000000000000000 --- a/server/src/component.rs +++ /dev/null @@ -1,53 +0,0 @@ -// StarKingdoms.IO, a browser game about drifting through space -// Copyright (C) 2023 ghostly_zsh, TerraMaster85, core -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -use std::net::SocketAddr; - -use bevy::prelude::*; -use serde::{Deserialize, Serialize}; - -use crate::module::component::{Attach, PartBundle}; - -#[derive(Component, Clone, Copy, Serialize, Deserialize, Debug, Default)] -pub struct Input { - pub up: bool, - pub down: bool, - pub left: bool, - pub right: bool, -} - -#[derive(Component, Clone, Debug)] -pub struct Player { - pub addr: SocketAddr, - pub username: String, - pub input: Input, - pub selected: Option, - pub save_eligibility: bool, - pub energy_capacity: u32, - pub energy: u32, -} - - -#[derive(Bundle)] -pub struct PlayerBundle { - pub part: PartBundle, - pub player: Player, - pub attach: Attach, -} - -#[derive(Resource)] -pub struct AppKeys { - pub app_key: Vec, -} diff --git a/server/src/main.rs b/server/src/main.rs index 495a6f7e8b08f5f9d5ca11f17a61277340b60c37..b2b9a684539fd0c5439c3c23f55de194dd994161 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -17,51 +17,44 @@ #![allow(clippy::too_many_arguments)] // bevy :( #![allow(clippy::only_used_in_recursion)] // todo: remove this -use crate::mathutil::rot2d; -use crate::ws::{StkTungsteniteServerConfig, StkTungsteniteServerPlugin, WsEvent}; -use bevy::math::{vec2, vec3}; +use crate::ws::{StkTungsteniteServerConfig, StkTungsteniteServerPlugin}; use bevy::{ app::{PluginGroupBuilder, ScheduleRunnerPlugin}, - ecs::event::ManualEventReader, prelude::*, time::TimePlugin, }; use bevy_rapier2d::prelude::*; -use component::*; -use hmac::{Hmac, Mac}; -use jwt::VerifyWithKey; -use module::component::{Attach, CanAttach, LooseAttach, ModuleTimer, PartBundle, PartFlags, PartType}; -use module::save::{construct_save_data, load_savefile}; -use module::thruster::search_thrusters; -use module::{attach_on_module_tree, despawn_module_tree, detach_recursive}; +use module::component::{ + Attach, CanAttach, LooseAttach, ModuleTimer, PartBundle, PartFlags, PartType, +}; use packet::*; use planet::PlanetType; -use rand::Rng; use serde::{Deserialize, Serialize}; -use sha2::Sha256; -use starkingdoms_common::{pack_savefile, unpack_savefile, SaveData}; use std::fs; use crate::config::{PartsConfig, PhysicsSolver, PlanetsConfig, StkConfig}; use std::sync::OnceLock; use std::time::Duration; -pub mod component; -mod config; +#[macro_use] +pub mod config; pub mod macros; pub mod mathutil; +pub mod module; pub mod packet; -pub mod ws; pub mod planet; -pub mod module; pub mod player; +pub mod ws; struct StkPluginGroup; +#[derive(Resource)] +pub struct AppKeys { + pub app_key: Vec, +} + // factor to multiply positions by to send to the client pub static CLIENT_SCALE: f32 = 50.0; -// half size of hearty -pub static PART_HALF_SIZE: f32 = 25.0; // good ol' classic almost useless but still necessary code static _SERVER_CONFIG: OnceLock = OnceLock::new(); @@ -160,13 +153,13 @@ fn main() { .add_systems(Startup, setup_integration_parameters) .add_systems(Startup, planet::spawn_planets) .add_systems(FixedUpdate, module::module_spawn) - .add_systems(Update, on_message) - .add_systems(Update, on_close) - .add_systems(FixedUpdate, send_player_energy) - .add_systems(FixedUpdate, on_position_change) + .add_systems(Update, player::on_message) + .add_systems(Update, player::packet::on_close) + .add_systems(FixedUpdate, player::packet::send_player_energy) + .add_systems(FixedUpdate, player::packet::on_position_change) .add_systems( FixedUpdate, - (module::break_modules, gravity_update, player_input_update).chain(), + (module::break_modules, gravity_update, player::player_input_update).chain(), ) .add_systems(FixedUpdate, module::save::save_eligibility) .add_systems(FixedUpdate, module::convert_modules) @@ -196,741 +189,6 @@ fn setup_integration_parameters(mut context: ResMut, server_confi } } -fn on_message( - mut commands: Commands, - planet_query: Query<(Entity, &PlanetType, &Transform)>, - mut part_query: Query< - ( - Entity, - &PartType, - &mut Transform, - &mut Velocity, - Option<&LooseAttach>, - &mut PartFlags, - ), - (Without, Without, Without), - >, - mut attached_query: Query< - ( - Entity, - &PartType, - &mut Transform, - &mut Attach, - &Velocity, - Option<&CanAttach>, - Option<&LooseAttach>, - &mut PartFlags, - ), - (Without, Without), - >, - mut player_query: Query< - ( - Entity, - &mut Player, - &Transform, - &Velocity, - &mut Attach, - &mut PartFlags, - ), - Without, - >, - mut packet_recv: Local>, - mut packet_event_send: ResMut>, - app_keys: Res, - server_config: Res, -) { - let mut event_queue = Vec::new(); - for ev in packet_recv.read(&packet_event_send) { - if let WsEvent::Recv { from, message } = ev { - let packet: Packet = err_or_cont!(message.try_into()); - - match packet { - Packet::ClientLogin { - username, - save, - jwt, - } => { - // auth - // plz no remove - if let Some(token) = jwt { - let key: Hmac = Hmac::new_from_slice(&app_keys.app_key).unwrap(); - let claims: UserToken = match token.verify_with_key(&key) { - Ok(c) => c, - Err(e) => { - event_queue.push(WsEvent::Send { - to: *from, - message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: format!("Token is invalid or verification failed: {e}. Please log in again, or contact StarKingdoms staff if the problem persists.") }.into(), - }); - event_queue.push(WsEvent::Close { addr: *from }); - continue; - } - }; - - if claims.permission_level - < server_config.security.required_permission_level - { - event_queue.push(WsEvent::Send { - to: *from, - message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: format!("Permission level {} is too low, {} is required. If your permissions were just changed, you need to log out and log back in for the change to take effect. If you believe this is a mistake, contact StarKingdoms staff.", claims.permission_level, server_config.security.required_permission_level) }.into(), - }); - event_queue.push(WsEvent::Close { addr: *from }); - continue; - } - - event_queue.push(WsEvent::Send { - to: *from, - message: Packet::Message { message_type: MessageType::Server, actor: "StarKingdoms Team".to_string(), content: "Thank you for participating in the StarKingdoms private alpha! Your feedback is essential to improving the game, so please give us any feedback you have in the Discord! <3".to_string() }.into(), - }); - } else if server_config.security.required_permission_level != 0 { - event_queue.push(WsEvent::Send { - to: *from, - message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: "Authentication is required to join this server at the moment. Log in and try again, or try again later.".to_string() }.into(), - }); - event_queue.push(WsEvent::Close { addr: *from }); - continue; - } - let angle: f32 = { - let mut rng = rand::thread_rng(); - rng.gen::() * std::f32::consts::PI * 2. - }; - let mut transform = - Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0); - transform.rotate_z(angle); - let mut player_comp = Player { - addr: *from, - username: username.to_string(), - input: component::Input::default(), - selected: None, - save_eligibility: false, - energy_capacity: part!(PartType::Hearty).energy_capacity, - energy: part!(PartType::Hearty).energy_capacity, - }; - let mut entity_id = commands.spawn(PartBundle { - part_type: PartType::Hearty, - transform: TransformBundle::from(transform), - flags: PartFlags { attached: false }, - }); - entity_id - .insert(Collider::cuboid(0.5, 0.5)) - .insert(AdditionalMassProperties::MassProperties(MassProperties { - local_center_of_mass: vec2(0.0, 0.0), - mass: part!(PartType::Hearty).mass, - principal_inertia: 7.5, - })) - .insert(ExternalImpulse { - impulse: Vec2::ZERO, - torque_impulse: 0.0, - }) - .insert(ExternalForce::default()) - .insert(ReadMassProperties::default()) - .insert(Velocity::default()) - .insert(RigidBody::Dynamic); - let id = entity_id.id().index(); - - let entity = entity_id.id(); - let mut attach = Attach { - associated_player: None, - parent: None, - children: [None, None, None, None], - }; - if let Some(save) = save { - // attempt to decode the savefile - if let Ok(savefile) = unpack_savefile(&app_keys.app_key, save) { - // HEY! GHOSTLY! THIS SAVE FILE IS VALID! PLEASE LOAD IT! - // THANKS! - - let children = load_savefile( - &mut commands, - transform, - entity, - entity, - savefile.children, - &mut attached_query, - &mut part_query, - &mut player_query, - &mut player_comp, - ); - player_comp.energy = player_comp.energy_capacity; - attach.children = children; - } else { - let packet = Packet::Message { - message_type: packet::MessageType::Error, - actor: "SERVER".to_string(), - content: "Savefile signature corrupted or inner data invalid. Save was not loaded. Contact StarKingdoms staff for assistance.".to_string(), - }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - } - } else { - // nothing to do - } - let mut entity_id = commands.entity(entity); - entity_id.insert(player_comp); - entity_id.insert(attach); - - // tell this player the planets - let mut planets = Vec::new(); - for (entity, planet_type, transform) in planet_query.iter() { - let translation = transform.translation; - planets.push(( - entity.index(), - Planet { - planet_type: *planet_type, - transform: proto_transform!(Transform::from_translation( - translation * CLIENT_SCALE - )), - radius: match *planet_type { - PlanetType::Earth => { - planet!(PlanetType::Earth).size * CLIENT_SCALE - } - PlanetType::Moon => { - planet!(PlanetType::Moon).size * CLIENT_SCALE - } - PlanetType::Mars => { - planet!(PlanetType::Mars).size * CLIENT_SCALE - } - }, - }, - )); - } - let packet = Packet::PlanetPositions { planets }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - - // 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 }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - - // tell other players that a player has spawned in - let packet = Packet::SpawnPlayer { - id, - username: username.to_string(), - }; - event_queue.push(WsEvent::Broadcast { - message: packet.into(), - }); - let packet = Packet::Message { - message_type: packet::MessageType::Server, - actor: "SERVER".to_string(), - content: format!("{} has joined the server!", username), - }; - event_queue.push(WsEvent::Broadcast { - message: packet.into(), - }); - - // tell the player where parts are - let mut parts = Vec::new(); - for (entity, part_type, transform, _, _, flags) in &part_query { - parts.push(( - entity.index(), - Part { - part_type: *part_type, - transform: proto_transform!(Transform::from_translation( - transform.translation * CLIENT_SCALE - )), - flags: proto_part_flags!(flags), - }, - )); - } - for (entity, part_type, transform, _, _, _, _, flags) in &attached_query { - parts.push(( - entity.index(), - Part { - part_type: *part_type, - transform: proto_transform!(Transform::from_translation( - transform.translation * CLIENT_SCALE - )), - flags: proto_part_flags!(flags), - }, - )); - } - parts.push(( - id, - Part { - part_type: PartType::Hearty, - transform: proto_transform!(Transform::from_translation( - transform.translation - ) - .with_rotation(transform.rotation)), - flags: ProtoPartFlags { attached: false }, - }, - )); - let packet = Packet::PartPositions { parts }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - - // and send the welcome message :) - let packet = Packet::Message { - message_type: packet::MessageType::Server, - actor: "SERVER".to_string(), - content: format!( - "starkingdoms-server v{} says hello", - env!("CARGO_PKG_VERSION") - ), - }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - let packet = Packet::Message { - message_type: packet::MessageType::Server, - actor: "SERVER".to_string(), - content: "Welcome to StarKingdoms.IO! Have fun!".to_string(), - }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - let packet = Packet::Message { - message_type: packet::MessageType::Server, - actor: "SERVER".to_string(), - content: "Found a bug? Have a feature request? Please bring this and all other feedback to the game's official Discord server! Join here: https://discord.gg/3u7Yw8DWtQ".to_string(), - }; - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - } - Packet::SendMessage { target, content } => { - // find our player - let mut player = None; - for (_, q_player, _, _, _, _) in &player_query { - if q_player.addr == *from { - player = Some(q_player); - } - } - let player = player.unwrap(); - if let Some(target_username) = target { - let mut target_player = None; - for (_, q_player, _, _, _, _) in &player_query { - if q_player.username == target_username { - target_player = Some(q_player); - } - } - let target_player = target_player.unwrap(); - let packet = Packet::Message { - message_type: packet::MessageType::Direct, - actor: player.username.clone(), - content, - }; - event_queue.push(WsEvent::Send { - to: target_player.addr, - message: packet.clone().into(), - }); - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - } else { - // send to general chat - let packet = Packet::Message { - message_type: packet::MessageType::Chat, - actor: player.username.clone(), - content, - }; - - event_queue.push(WsEvent::Broadcast { - message: packet.into(), - }); - } - } - Packet::PlayerInput { - up, - down, - left, - right, - } => { - for (_, mut q_player, _, _, _, _) in &mut player_query { - if q_player.addr == *from { - q_player.input.up = up; - q_player.input.down = down; - q_player.input.left = left; - q_player.input.right = right; - } - } - } - Packet::PlayerMouseInput { - x, - y, - released, - button: _, - } => { - let x = x / CLIENT_SCALE; - let y = y / CLIENT_SCALE; - for (entity, mut q_player, _transform, _velocity, _attach, _) in - &mut player_query - { - if q_player.addr == *from { - if released { - let select = if let Some(s) = q_player.selected { - s - } else { - break; - }; - q_player.selected = None; - if attached_query.contains(select) { - let module = attached_query.get(select).unwrap(); - let attach = module.3.clone(); - let lost_energy_capacity = detach_recursive( - &mut commands, - module.0, - &mut attached_query, - &mut player_query, - ); - let mut module = attached_query.get_mut(select).unwrap(); - module.2.translation = vec3(x, y, 0.); - if *module.1 == PartType::LandingThruster { - let sub_entity = attach.children[2].unwrap(); - let mut suspension = - attached_query.get_mut(sub_entity).unwrap(); - suspension.2.translation = vec3(x, y, 0.); - } - let mut player = player_query.get_mut(entity).unwrap().1; - player.energy_capacity -= lost_energy_capacity; - player.energy = - std::cmp::min(player.energy, player.energy_capacity); - break; - } - if attach_on_module_tree( - x, - y, - &mut commands, - entity, - select, - entity, - &mut attached_query, - &mut part_query, - &mut player_query, - ) { - let mut part = part_query.get_mut(select).unwrap(); - part.5.attached = true; // all of this code is cursed. what the hell is it actually doing - break; - } - // move module to cursor since no attach - let mut part = part_query.get_mut(select).unwrap(); - part.2.translation = vec3(x, y, 0.); - if *part.1 == PartType::LandingThruster { - if let Some(loose_attach) = part.4 { - let sub_entity = loose_attach.children[2].unwrap(); - let mut part = part_query.get_mut(sub_entity).unwrap(); - part.2.translation = vec3(x, y, 0.); - } - } - break; - } - for (m_entity, part_type, transform, m_attach, _velocity, _, _, _) in - &attached_query - { - if *part_type == PartType::LandingThrusterSuspension { - continue; - } - let pos = transform.translation; - let rel_x = pos.x - x; - let rel_y = pos.y - y; - let angle = -transform.rotation.z; - let x = rel_x * angle.cos() - rel_y * angle.sin(); - let y = rel_x * angle.sin() + rel_y * angle.cos(); - let mut bound = [-0.5, 0.5, -0.5, 0.5]; // left, right, top, bottom - if let PartType::Cargo = part_type { - bound = [-0.375, 0.375, -0.5, 0.4375]; - } - - if bound[0] < x - && x < bound[1] - && bound[2] < y - && y < bound[3] - && m_attach.associated_player.unwrap() == entity - { - q_player.selected = Some(m_entity); - break; - } - } - for (entity, part_type, transform, _, _, _) in &part_query { - if *part_type == PartType::LandingThrusterSuspension { - continue; - } - let pos = transform.translation; - let rel_x = pos.x - x; - let rel_y = pos.y - y; - let angle = -transform.rotation.z; - let x = rel_x * angle.cos() - rel_y * angle.sin(); - let y = rel_x * angle.sin() + rel_y * angle.cos(); - let mut bound = [-0.5, 0.5, -0.5, 0.5]; // left, right, top, bottom - if let PartType::Cargo = part_type { - bound = [-0.375, 0.375, -0.5, 0.4375]; - } - - if bound[0] < x && x < bound[1] && bound[2] < y && y < bound[3] { - q_player.selected = Some(entity); - break; - } - } - } - } - } - Packet::RequestSave { old_save } => { - for (_, q_player, _, _, attach, _) in &mut player_query { - if q_player.addr == *from { - // HEY! GHOSTLY! PLEASE FILL THIS STRUCT WITH DATA! - // THANKS! - - let unused_modules = if let Some(ref old_save) = old_save { - if let Ok(old_savedata) = - unpack_savefile(&app_keys.app_key, old_save.to_string()) - { - old_savedata.unused_modules - } else { - Vec::new() - } - } else { - Vec::new() - }; - let save = SaveData { - children: construct_save_data(attach.clone(), &attached_query), - unused_modules, - }; - let save_string = pack_savefile(&app_keys.app_key, save); - let packet = Packet::SaveData { - payload: save_string, - }; - - event_queue.push(WsEvent::Send { - to: *from, - message: packet.into(), - }); - } - } - } - _ => continue, - } - } - } - for event in event_queue { - packet_event_send.send(event); - } -} - -fn on_close( - player_query: Query<(Entity, &Player, &Attach)>, - attached_query: Query<&Attach, With>, - part_query: Query<&PartType>, - 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 WsEvent::Close { addr } = packet { - for (entity, player, attach) in &player_query { - if player.addr == *addr { - despawn_module_tree( - &mut commands, - attach, - &attached_query, - &part_query, - &mut packets, - ); - commands.entity(entity).despawn_recursive(); - - let packet = Packet::PlayerLeave { id: entity.index() }; - - for (in_entity, player, _) in &player_query { - if entity != in_entity { - packets.push(WsEvent::Send { - to: player.addr, - message: packet.clone().into(), - }); - } - } - } - } - } - } - for packet in packets { - packet_send.send(packet); - } -} - - -fn send_player_energy(player_query: Query<&Player>, mut packet_send: EventWriter) { - for player in &player_query { - let packet = Packet::EnergyUpdate { - amount: player.energy, - max: player.energy_capacity, - }; - - packet_send.send(WsEvent::Send { - to: player.addr, - message: packet.into(), - }); - } -} - -fn on_position_change( - mut commands: Commands, - part_query: Query<(Entity, &PartType, &Transform, &PartFlags), Changed>, - planet_query: Query<(Entity, &PlanetType, &Transform), Changed>, - mut packet_send: EventWriter, -) { - let mut updated_parts = Vec::new(); - for (entity, part_type, transform, flags) in part_query.iter() { - let id = commands.entity(entity).id().index(); - updated_parts.push(( - id, - Part { - part_type: *part_type, - transform: proto_transform!(Transform::from_translation( - transform.translation * CLIENT_SCALE, - ) - .with_rotation(transform.rotation)), - flags: proto_part_flags!(flags), - }, - )); - } - - if !updated_parts.is_empty() { - let packet = Packet::PartPositions { - parts: updated_parts, - }; - - packet_send.send(WsEvent::Broadcast { - message: packet.into(), - }); - } - - 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::from_translation( - transform.translation * CLIENT_SCALE - )), - radius: match *planet_type { - PlanetType::Earth => planet!(PlanetType::Earth).size * CLIENT_SCALE, - PlanetType::Moon => planet!(PlanetType::Moon).size * CLIENT_SCALE, - PlanetType::Mars => planet!(PlanetType::Mars).size * CLIENT_SCALE, - }, - }, - )); - } - - if !planets.is_empty() { - let packet = Packet::PlanetPositions { planets }; - - packet_send.send(WsEvent::Broadcast { - message: packet.into(), - }); - } -} - -fn player_input_update( - mut player_and_body_query: Query<( - Entity, - &mut Player, - &Attach, - &mut ExternalForce, - &Transform, - )>, - mut attached_query: Query< - (&Attach, &PartType, &mut ExternalForce, &Transform), - Without, - >, -) { - for (_, mut player, attach, mut forces, transform) in &mut player_and_body_query { - //forces.torque = 0.0; - //forces.force = Vec2::ZERO; - if !(player.input.up || player.input.down || player.input.right || player.input.left) { - continue; - } - - let mut fmul_bottom_left_thruster: f32 = 0.0; - let mut fmul_bottom_right_thruster: f32 = 0.0; - let mut fmul_top_left_thruster: f32 = 0.0; - let mut fmul_top_right_thruster: f32 = 0.0; - if player.input.up { - fmul_bottom_left_thruster -= 1.0; - fmul_bottom_right_thruster -= 1.0; - } - if player.input.down { - fmul_top_left_thruster += 1.0; - fmul_top_right_thruster += 1.0; - } - if player.input.left { - fmul_top_left_thruster += 1.0; - fmul_bottom_right_thruster -= 1.0; - } - if player.input.right { - fmul_top_right_thruster += 1.0; - fmul_bottom_left_thruster -= 1.0; - } - fmul_top_left_thruster = fmul_top_left_thruster.clamp(-1.0, 1.0); - fmul_top_right_thruster = fmul_top_right_thruster.clamp(-1.0, 1.0); - fmul_bottom_left_thruster = fmul_bottom_left_thruster.clamp(-1.0, 1.0); - fmul_bottom_right_thruster = fmul_bottom_right_thruster.clamp(-1.0, 1.0); - if player.input.up { - fmul_bottom_left_thruster -= 2.0; - fmul_bottom_right_thruster -= 2.0; - } - if player.input.down { - fmul_top_left_thruster += 2.0; - fmul_top_right_thruster += 2.0; - } - - let rot = transform.rotation.to_euler(EulerRot::ZYX).0; - - let thrusters = [ - (fmul_bottom_left_thruster, -PART_HALF_SIZE, -PART_HALF_SIZE), - (fmul_bottom_right_thruster, PART_HALF_SIZE, -PART_HALF_SIZE), - (fmul_top_left_thruster, -PART_HALF_SIZE, PART_HALF_SIZE), - (fmul_top_right_thruster, PART_HALF_SIZE, PART_HALF_SIZE), - ]; - - for (force_multiplier, x_offset, y_offset) in thrusters { - if force_multiplier != 0.0 && player.energy >= part!(PartType::Hearty).thruster_energy { - player.energy -= part!(PartType::Hearty).thruster_energy; - let thruster_pos_uncast = vec2(x_offset, y_offset); - let thruster_pos_cast = - rot2d(thruster_pos_uncast, rot) + transform.translation.xy(); - let thruster_force = force_multiplier * part!(PartType::Hearty).thruster_force; - let thruster_vec = vec2(-thruster_force * rot.sin(), thruster_force * rot.cos()); - let thruster_force = ExternalForce::at_point( - thruster_vec, - thruster_pos_cast, - transform.translation.xy(), - ); - forces.force += thruster_force.force; - forces.torque += thruster_force.torque; - } - } - // change to support other thruster types later - if player.energy >= part!(PartType::LandingThruster).thruster_energy { - search_thrusters( - player.input, - attach.clone(), - *transform, - &mut player.energy, - &mut attached_query, - ); - } - } -} - fn gravity_update( mut part_query: Query< ( diff --git a/server/src/module/component.rs b/server/src/module/component.rs index ce2f62809fdf77548fd172b7d7666ff3215fa9a2..065ba3199670f70fb5b31f7c24d13dd63e7504e4 100644 --- a/server/src/module/component.rs +++ b/server/src/module/component.rs @@ -75,4 +75,3 @@ impl Default for ModuleTimer { Self::new() } } - diff --git a/server/src/module/mod.rs b/server/src/module/mod.rs index 739222177d08ad6b13d82668b31ff65bb8cad992..3fb67fc4aa46abdd55c09fea044dd05aa96906ff 100644 --- a/server/src/module/mod.rs +++ b/server/src/module/mod.rs @@ -5,11 +5,16 @@ use bevy_rapier2d::prelude::*; use component::*; use rand::Rng; -use crate::{capacity, config::StkConfig, part, planet::PlanetType, proto_part_flags, proto_transform, ws::WsEvent, Packet, Part, Player}; +use crate::{ + capacity, config::StkConfig, part, planet::PlanetType, player::component::Player, proto_part_flags, proto_transform, ws::WsEvent, Packet, Part +}; -pub mod thruster; pub mod component; pub mod save; +pub mod thruster; + +// half size of hearty +pub static PART_HALF_SIZE: f32 = 25.0; pub fn module_spawn( mut commands: Commands, diff --git a/server/src/module/save.rs b/server/src/module/save.rs index 57696a4c9053c6d084349e8c193a4940c596e1fe..5406dc0b92b618a8654e37286d5758520d671584 100644 --- a/server/src/module/save.rs +++ b/server/src/module/save.rs @@ -4,7 +4,9 @@ use bevy::{math::vec2, prelude::*}; use bevy_rapier2d::prelude::*; use starkingdoms_common::SaveModule; -use crate::{capacity, mass, planet::PlanetType, ws::WsEvent, Attach, CanAttach, LooseAttach, Packet, PartBundle, PartFlags, PartType, Player}; +use crate::{ + capacity, mass, planet::PlanetType, player::component::Player, ws::WsEvent, Attach, CanAttach, LooseAttach, Packet, PartBundle, PartFlags, PartType +}; pub fn load_savefile( commands: &mut Commands, diff --git a/server/src/module/thruster.rs b/server/src/module/thruster.rs index 8c80a5b0ef7df4ceb182f19ad2d60e147291277c..c4cbf997b3dc7b85d5078160ccc0af07fc3a6f92 100644 --- a/server/src/module/thruster.rs +++ b/server/src/module/thruster.rs @@ -1,8 +1,8 @@ use std::f32::consts::PI; +use crate::{part, player::component::{Input, Player}, Attach, PartType}; use bevy::prelude::*; use bevy_rapier2d::prelude::*; -use crate::{part, Attach, Input, PartType, Player}; pub fn search_thrusters( input: Input, diff --git a/server/src/player/component.rs b/server/src/player/component.rs new file mode 100644 index 0000000000000000000000000000000000000000..9fcf0cc7064d2e39de1277d1262d72289d0febfa --- /dev/null +++ b/server/src/player/component.rs @@ -0,0 +1,33 @@ +use std::net::SocketAddr; + +use bevy::prelude::{Bundle, Component, Entity}; +use serde::{Deserialize, Serialize}; + +use crate::module::component::{Attach, PartBundle}; + + +#[derive(Component, Clone, Copy, Serialize, Deserialize, Debug, Default)] +pub struct Input { + pub up: bool, + pub down: bool, + pub left: bool, + pub right: bool, +} + +#[derive(Component, Clone, Debug)] +pub struct Player { + pub addr: SocketAddr, + pub username: String, + pub input: Input, + pub selected: Option, + pub save_eligibility: bool, + pub energy_capacity: u32, + pub energy: u32, +} + +#[derive(Bundle)] +pub struct PlayerBundle { + pub part: PartBundle, + pub player: Player, + pub attach: Attach, +} diff --git a/server/src/player/mod.rs b/server/src/player/mod.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..86dffb2230bceac8bc4b116329b12b37a5265fc9 100644 --- a/server/src/player/mod.rs +++ b/server/src/player/mod.rs @@ -0,0 +1,636 @@ +use bevy::{ecs::event::ManualEventReader, math::{vec2, vec3}, prelude::*}; +use bevy_rapier2d::prelude::*; +use component::Player; +use hmac::{Hmac, Mac}; +use rand::Rng; +use sha2::Sha256; +use starkingdoms_common::{pack_savefile, unpack_savefile, SaveData}; +use jwt::VerifyWithKey; + +use crate::{config::StkConfig, err_or_cont, mathutil::rot2d, + module::{component::{Attach, CanAttach, LooseAttach, PartBundle, PartFlags, PartType}, save::load_savefile, PART_HALF_SIZE}, + part, planet::PlanetType, + proto_part_flags, proto_transform, ws::WsEvent, AppKeys, MessageType, + Packet, Part, Planet, ProtoPartFlags, UserToken, CLIENT_SCALE}; + +pub mod component; +pub mod packet; + +pub fn on_message( + mut commands: Commands, + planet_query: Query<(Entity, &PlanetType, &Transform)>, + mut part_query: Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Velocity, + Option<&LooseAttach>, + &mut PartFlags, + ), + (Without, Without, Without), + >, + mut attached_query: Query< + ( + Entity, + &PartType, + &mut Transform, + &mut Attach, + &Velocity, + Option<&CanAttach>, + Option<&LooseAttach>, + &mut PartFlags, + ), + (Without, Without), + >, + mut player_query: Query< + ( + Entity, + &mut Player, + &Transform, + &Velocity, + &mut Attach, + &mut PartFlags, + ), + Without, + >, + mut packet_recv: Local>, + mut packet_event_send: ResMut>, + app_keys: Res, + server_config: Res, +) { + let mut event_queue = Vec::new(); + for ev in packet_recv.read(&packet_event_send) { + if let WsEvent::Recv { from, message } = ev { + let packet: Packet = err_or_cont!(message.try_into()); + + match packet { + Packet::ClientLogin { + username, + save, + jwt, + } => { + // auth + // plz no remove + if let Some(token) = jwt { + let key: Hmac = Hmac::new_from_slice(&app_keys.app_key).unwrap(); + let claims: UserToken = match token.verify_with_key(&key) { + Ok(c) => c, + Err(e) => { + event_queue.push(WsEvent::Send { + to: *from, + message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: format!("Token is invalid or verification failed: {e}. Please log in again, or contact StarKingdoms staff if the problem persists.") }.into(), + }); + event_queue.push(WsEvent::Close { addr: *from }); + continue; + } + }; + + if claims.permission_level + < server_config.security.required_permission_level + { + event_queue.push(WsEvent::Send { + to: *from, + message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: format!("Permission level {} is too low, {} is required. If your permissions were just changed, you need to log out and log back in for the change to take effect. If you believe this is a mistake, contact StarKingdoms staff.", claims.permission_level, server_config.security.required_permission_level) }.into(), + }); + event_queue.push(WsEvent::Close { addr: *from }); + continue; + } + + event_queue.push(WsEvent::Send { + to: *from, + message: Packet::Message { message_type: MessageType::Server, actor: "StarKingdoms Team".to_string(), content: "Thank you for participating in the StarKingdoms private alpha! Your feedback is essential to improving the game, so please give us any feedback you have in the Discord! <3".to_string() }.into(), + }); + } else if server_config.security.required_permission_level != 0 { + event_queue.push(WsEvent::Send { + to: *from, + message: Packet::Message { message_type: MessageType::Error, actor: "SERVER".to_string(), content: "Authentication is required to join this server at the moment. Log in and try again, or try again later.".to_string() }.into(), + }); + event_queue.push(WsEvent::Close { addr: *from }); + continue; + } + let angle: f32 = { + let mut rng = rand::thread_rng(); + rng.gen::() * std::f32::consts::PI * 2. + }; + let mut transform = + Transform::from_xyz(angle.cos() * 30.0, angle.sin() * 30.0, 0.0); + transform.rotate_z(angle); + let mut player_comp = Player { + addr: *from, + username: username.to_string(), + input: component::Input::default(), + selected: None, + save_eligibility: false, + energy_capacity: part!(PartType::Hearty).energy_capacity, + energy: part!(PartType::Hearty).energy_capacity, + }; + let mut entity_id = commands.spawn(PartBundle { + part_type: PartType::Hearty, + transform: TransformBundle::from(transform), + flags: PartFlags { attached: false }, + }); + entity_id + .insert(Collider::cuboid(0.5, 0.5)) + .insert(AdditionalMassProperties::MassProperties(MassProperties { + local_center_of_mass: vec2(0.0, 0.0), + mass: part!(PartType::Hearty).mass, + principal_inertia: 7.5, + })) + .insert(ExternalImpulse { + impulse: Vec2::ZERO, + torque_impulse: 0.0, + }) + .insert(ExternalForce::default()) + .insert(ReadMassProperties::default()) + .insert(Velocity::default()) + .insert(RigidBody::Dynamic); + let id = entity_id.id().index(); + + let entity = entity_id.id(); + let mut attach = Attach { + associated_player: None, + parent: None, + children: [None, None, None, None], + }; + if let Some(save) = save { + // attempt to decode the savefile + if let Ok(savefile) = unpack_savefile(&app_keys.app_key, save) { + // HEY! GHOSTLY! THIS SAVE FILE IS VALID! PLEASE LOAD IT! + // THANKS! + + let children = load_savefile( + &mut commands, + transform, + entity, + entity, + savefile.children, + &mut attached_query, + &mut part_query, + &mut player_query, + &mut player_comp, + ); + player_comp.energy = player_comp.energy_capacity; + attach.children = children; + } else { + let packet = Packet::Message { + message_type: crate::packet::MessageType::Error, + actor: "SERVER".to_string(), + content: "Savefile signature corrupted or inner data invalid. Save was not loaded. Contact StarKingdoms staff for assistance.".to_string(), + }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + } + } else { + // nothing to do + } + let mut entity_id = commands.entity(entity); + entity_id.insert(player_comp); + entity_id.insert(attach); + + // tell this player the planets + let mut planets = Vec::new(); + for (entity, planet_type, transform) in planet_query.iter() { + let translation = transform.translation; + planets.push(( + entity.index(), + Planet { + planet_type: *planet_type, + transform: proto_transform!(Transform::from_translation( + translation * CLIENT_SCALE + )), + radius: match *planet_type { + PlanetType::Earth => { + planet!(PlanetType::Earth).size * CLIENT_SCALE + } + PlanetType::Moon => { + planet!(PlanetType::Moon).size * CLIENT_SCALE + } + PlanetType::Mars => { + planet!(PlanetType::Mars).size * CLIENT_SCALE + } + }, + }, + )); + } + let packet = Packet::PlanetPositions { planets }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + + // 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 }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + + // tell other players that a player has spawned in + let packet = Packet::SpawnPlayer { + id, + username: username.to_string(), + }; + event_queue.push(WsEvent::Broadcast { + message: packet.into(), + }); + let packet = Packet::Message { + message_type: crate::packet::MessageType::Server, + actor: "SERVER".to_string(), + content: format!("{} has joined the server!", username), + }; + event_queue.push(WsEvent::Broadcast { + message: packet.into(), + }); + + // tell the player where parts are + let mut parts = Vec::new(); + for (entity, part_type, transform, _, _, flags) in &part_query { + parts.push(( + entity.index(), + Part { + part_type: *part_type, + transform: proto_transform!(Transform::from_translation( + transform.translation * CLIENT_SCALE + )), + flags: proto_part_flags!(flags), + }, + )); + } + for (entity, part_type, transform, _, _, _, _, flags) in &attached_query { + parts.push(( + entity.index(), + Part { + part_type: *part_type, + transform: proto_transform!(Transform::from_translation( + transform.translation * CLIENT_SCALE + )), + flags: proto_part_flags!(flags), + }, + )); + } + parts.push(( + id, + Part { + part_type: PartType::Hearty, + transform: proto_transform!(Transform::from_translation( + transform.translation + ) + .with_rotation(transform.rotation)), + flags: ProtoPartFlags { attached: false }, + }, + )); + let packet = Packet::PartPositions { parts }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + + // and send the welcome message :) + let packet = Packet::Message { + message_type: crate::packet::MessageType::Server, + actor: "SERVER".to_string(), + content: format!( + "starkingdoms-server v{} says hello", + env!("CARGO_PKG_VERSION") + ), + }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + let packet = Packet::Message { + message_type: crate::packet::MessageType::Server, + actor: "SERVER".to_string(), + content: "Welcome to StarKingdoms.IO! Have fun!".to_string(), + }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + let packet = Packet::Message { + message_type: crate::packet::MessageType::Server, + actor: "SERVER".to_string(), + content: "Found a bug? Have a feature request? Please bring this and all other feedback to the game's official Discord server! Join here: https://discord.gg/3u7Yw8DWtQ".to_string(), + }; + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + } + Packet::SendMessage { target, content } => { + // find our player + let mut player = None; + for (_, q_player, _, _, _, _) in &player_query { + if q_player.addr == *from { + player = Some(q_player); + } + } + let player = player.unwrap(); + if let Some(target_username) = target { + let mut target_player = None; + for (_, q_player, _, _, _, _) in &player_query { + if q_player.username == target_username { + target_player = Some(q_player); + } + } + let target_player = target_player.unwrap(); + let packet = Packet::Message { + message_type: crate::packet::MessageType::Direct, + actor: player.username.clone(), + content, + }; + event_queue.push(WsEvent::Send { + to: target_player.addr, + message: packet.clone().into(), + }); + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + } else { + // send to general chat + let packet = Packet::Message { + message_type: crate::packet::MessageType::Chat, + actor: player.username.clone(), + content, + }; + + event_queue.push(WsEvent::Broadcast { + message: packet.into(), + }); + } + } + Packet::PlayerInput { + up, + down, + left, + right, + } => { + for (_, mut q_player, _, _, _, _) in &mut player_query { + if q_player.addr == *from { + q_player.input.up = up; + q_player.input.down = down; + q_player.input.left = left; + q_player.input.right = right; + } + } + } + Packet::PlayerMouseInput { + x, + y, + released, + button: _, + } => { + let x = x / CLIENT_SCALE; + let y = y / CLIENT_SCALE; + for (entity, mut q_player, _transform, _velocity, _attach, _) in + &mut player_query + { + if q_player.addr == *from { + if released { + let select = if let Some(s) = q_player.selected { + s + } else { + break; + }; + q_player.selected = None; + if attached_query.contains(select) { + let module = attached_query.get(select).unwrap(); + let attach = module.3.clone(); + let lost_energy_capacity = crate::module::detach_recursive( + &mut commands, + module.0, + &mut attached_query, + &mut player_query, + ); + let mut module = attached_query.get_mut(select).unwrap(); + module.2.translation = vec3(x, y, 0.); + if *module.1 == PartType::LandingThruster { + let sub_entity = attach.children[2].unwrap(); + let mut suspension = + attached_query.get_mut(sub_entity).unwrap(); + suspension.2.translation = vec3(x, y, 0.); + } + let mut player = player_query.get_mut(entity).unwrap().1; + player.energy_capacity -= lost_energy_capacity; + player.energy = + std::cmp::min(player.energy, player.energy_capacity); + break; + } + if crate::module::attach_on_module_tree( + x, + y, + &mut commands, + entity, + select, + entity, + &mut attached_query, + &mut part_query, + &mut player_query, + ) { + let mut part = part_query.get_mut(select).unwrap(); + part.5.attached = true; // all of this code is cursed. what the hell is it actually doing + break; + } + // move module to cursor since no attach + let mut part = part_query.get_mut(select).unwrap(); + part.2.translation = vec3(x, y, 0.); + if *part.1 == PartType::LandingThruster { + if let Some(loose_attach) = part.4 { + let sub_entity = loose_attach.children[2].unwrap(); + let mut part = part_query.get_mut(sub_entity).unwrap(); + part.2.translation = vec3(x, y, 0.); + } + } + break; + } + for (m_entity, part_type, transform, m_attach, _velocity, _, _, _) in + &attached_query + { + if *part_type == PartType::LandingThrusterSuspension { + continue; + } + let pos = transform.translation; + let rel_x = pos.x - x; + let rel_y = pos.y - y; + let angle = -transform.rotation.z; + let x = rel_x * angle.cos() - rel_y * angle.sin(); + let y = rel_x * angle.sin() + rel_y * angle.cos(); + let mut bound = [-0.5, 0.5, -0.5, 0.5]; // left, right, top, bottom + if let PartType::Cargo = part_type { + bound = [-0.375, 0.375, -0.5, 0.4375]; + } + + if bound[0] < x + && x < bound[1] + && bound[2] < y + && y < bound[3] + && m_attach.associated_player.unwrap() == entity + { + q_player.selected = Some(m_entity); + break; + } + } + for (entity, part_type, transform, _, _, _) in &part_query { + if *part_type == PartType::LandingThrusterSuspension { + continue; + } + let pos = transform.translation; + let rel_x = pos.x - x; + let rel_y = pos.y - y; + let angle = -transform.rotation.z; + let x = rel_x * angle.cos() - rel_y * angle.sin(); + let y = rel_x * angle.sin() + rel_y * angle.cos(); + let mut bound = [-0.5, 0.5, -0.5, 0.5]; // left, right, top, bottom + if let PartType::Cargo = part_type { + bound = [-0.375, 0.375, -0.5, 0.4375]; + } + + if bound[0] < x && x < bound[1] && bound[2] < y && y < bound[3] { + q_player.selected = Some(entity); + break; + } + } + } + } + } + Packet::RequestSave { old_save } => { + for (_, q_player, _, _, attach, _) in &mut player_query { + if q_player.addr == *from { + // HEY! GHOSTLY! PLEASE FILL THIS STRUCT WITH DATA! + // THANKS! + + let unused_modules = if let Some(ref old_save) = old_save { + if let Ok(old_savedata) = + unpack_savefile(&app_keys.app_key, old_save.to_string()) + { + old_savedata.unused_modules + } else { + Vec::new() + } + } else { + Vec::new() + }; + let save = SaveData { + children: crate::module::save::construct_save_data(attach.clone(), &attached_query), + unused_modules, + }; + let save_string = pack_savefile(&app_keys.app_key, save); + let packet = Packet::SaveData { + payload: save_string, + }; + + event_queue.push(WsEvent::Send { + to: *from, + message: packet.into(), + }); + } + } + } + _ => continue, + } + } + } + for event in event_queue { + packet_event_send.send(event); + } +} + +pub fn player_input_update( + mut player_and_body_query: Query<( + Entity, + &mut Player, + &Attach, + &mut ExternalForce, + &Transform, + )>, + mut attached_query: Query< + (&Attach, &PartType, &mut ExternalForce, &Transform), + Without, + >, +) { + for (_, mut player, attach, mut forces, transform) in &mut player_and_body_query { + //forces.torque = 0.0; + //forces.force = Vec2::ZERO; + if !(player.input.up || player.input.down || player.input.right || player.input.left) { + continue; + } + + let mut fmul_bottom_left_thruster: f32 = 0.0; + let mut fmul_bottom_right_thruster: f32 = 0.0; + let mut fmul_top_left_thruster: f32 = 0.0; + let mut fmul_top_right_thruster: f32 = 0.0; + if player.input.up { + fmul_bottom_left_thruster -= 1.0; + fmul_bottom_right_thruster -= 1.0; + } + if player.input.down { + fmul_top_left_thruster += 1.0; + fmul_top_right_thruster += 1.0; + } + if player.input.left { + fmul_top_left_thruster += 1.0; + fmul_bottom_right_thruster -= 1.0; + } + if player.input.right { + fmul_top_right_thruster += 1.0; + fmul_bottom_left_thruster -= 1.0; + } + fmul_top_left_thruster = fmul_top_left_thruster.clamp(-1.0, 1.0); + fmul_top_right_thruster = fmul_top_right_thruster.clamp(-1.0, 1.0); + fmul_bottom_left_thruster = fmul_bottom_left_thruster.clamp(-1.0, 1.0); + fmul_bottom_right_thruster = fmul_bottom_right_thruster.clamp(-1.0, 1.0); + if player.input.up { + fmul_bottom_left_thruster -= 2.0; + fmul_bottom_right_thruster -= 2.0; + } + if player.input.down { + fmul_top_left_thruster += 2.0; + fmul_top_right_thruster += 2.0; + } + + let rot = transform.rotation.to_euler(EulerRot::ZYX).0; + + let thrusters = [ + (fmul_bottom_left_thruster, -PART_HALF_SIZE, -PART_HALF_SIZE), + (fmul_bottom_right_thruster, PART_HALF_SIZE, -PART_HALF_SIZE), + (fmul_top_left_thruster, -PART_HALF_SIZE, PART_HALF_SIZE), + (fmul_top_right_thruster, PART_HALF_SIZE, PART_HALF_SIZE), + ]; + + for (force_multiplier, x_offset, y_offset) in thrusters { + if force_multiplier != 0.0 && player.energy >= part!(PartType::Hearty).thruster_energy { + player.energy -= part!(PartType::Hearty).thruster_energy; + let thruster_pos_uncast = vec2(x_offset, y_offset); + let thruster_pos_cast = + rot2d(thruster_pos_uncast, rot) + transform.translation.xy(); + let thruster_force = force_multiplier * part!(PartType::Hearty).thruster_force; + let thruster_vec = vec2(-thruster_force * rot.sin(), thruster_force * rot.cos()); + let thruster_force = ExternalForce::at_point( + thruster_vec, + thruster_pos_cast, + transform.translation.xy(), + ); + forces.force += thruster_force.force; + forces.torque += thruster_force.torque; + } + } + // change to support other thruster types later + if player.energy >= part!(PartType::LandingThruster).thruster_energy { + crate::module::thruster::search_thrusters( + player.input, + attach.clone(), + *transform, + &mut player.energy, + &mut attached_query, + ); + } + } +} diff --git a/server/src/player/packet.rs b/server/src/player/packet.rs new file mode 100644 index 0000000000000000000000000000000000000000..99c11aa7c9967124f224f777a919b3f8b3209d4f --- /dev/null +++ b/server/src/player/packet.rs @@ -0,0 +1,120 @@ +use bevy::{ecs::event::ManualEventReader, prelude::*}; + +use crate::{module::component::{Attach, PartFlags, PartType}, planet::PlanetType, proto_part_flags, proto_transform, ws::WsEvent, Packet, Part, Planet, CLIENT_SCALE}; + +use super::component::Player; + +pub fn send_player_energy(player_query: Query<&Player>, mut packet_send: EventWriter) { + for player in &player_query { + let packet = Packet::EnergyUpdate { + amount: player.energy, + max: player.energy_capacity, + }; + + packet_send.send(WsEvent::Send { + to: player.addr, + message: packet.into(), + }); + } +} + +pub fn on_position_change( + mut commands: Commands, + part_query: Query<(Entity, &PartType, &Transform, &PartFlags), Changed>, + planet_query: Query<(Entity, &PlanetType, &Transform), Changed>, + mut packet_send: EventWriter, +) { + let mut updated_parts = Vec::new(); + for (entity, part_type, transform, flags) in part_query.iter() { + let id = commands.entity(entity).id().index(); + updated_parts.push(( + id, + Part { + part_type: *part_type, + transform: proto_transform!(Transform::from_translation( + transform.translation * CLIENT_SCALE, + ) + .with_rotation(transform.rotation)), + flags: proto_part_flags!(flags), + }, + )); + } + + if !updated_parts.is_empty() { + let packet = Packet::PartPositions { + parts: updated_parts, + }; + + packet_send.send(WsEvent::Broadcast { + message: packet.into(), + }); + } + + 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::from_translation( + transform.translation * CLIENT_SCALE + )), + radius: match *planet_type { + PlanetType::Earth => planet!(PlanetType::Earth).size * CLIENT_SCALE, + PlanetType::Moon => planet!(PlanetType::Moon).size * CLIENT_SCALE, + PlanetType::Mars => planet!(PlanetType::Mars).size * CLIENT_SCALE, + }, + }, + )); + } + + if !planets.is_empty() { + let packet = Packet::PlanetPositions { planets }; + + packet_send.send(WsEvent::Broadcast { + message: packet.into(), + }); + } +} + +pub fn on_close( + player_query: Query<(Entity, &Player, &Attach)>, + attached_query: Query<&Attach, With>, + part_query: Query<&PartType>, + 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 WsEvent::Close { addr } = packet { + for (entity, player, attach) in &player_query { + if player.addr == *addr { + crate::module::despawn_module_tree( + &mut commands, + attach, + &attached_query, + &part_query, + &mut packets, + ); + commands.entity(entity).despawn_recursive(); + + let packet = Packet::PlayerLeave { id: entity.index() }; + + for (in_entity, player, _) in &player_query { + if entity != in_entity { + packets.push(WsEvent::Send { + to: player.addr, + message: packet.clone().into(), + }); + } + } + } + } + } + } + for packet in packets { + packet_send.send(packet); + } +}