// 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 . #![allow(clippy::type_complexity)] // bevy :( #![allow(clippy::too_many_arguments)] // bevy :( #![allow(clippy::only_used_in_recursion)] // todo: remove this use crate::ws::{StkTungsteniteServerConfig, StkTungsteniteServerPlugin}; use bevy::{ app::{PluginGroupBuilder, ScheduleRunnerPlugin}, prelude::*, time::TimePlugin, }; use bevy_rapier2d::prelude::*; use module::component::{ Attach, CanAttach, LooseAttach, ModuleTimer, PartBundle, PartFlags, PartType, }; use packet::*; use planet::PlanetType; use serde::{Deserialize, Serialize}; use std::fs; use crate::config::{PartsConfig, PhysicsSolver, PlanetsConfig, StkConfig}; use std::sync::OnceLock; use std::time::Duration; #[macro_use] pub mod config; pub mod macros; pub mod mathutil; pub mod module; pub mod packet; pub mod planet; 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; // good ol' classic almost useless but still necessary code static _SERVER_CONFIG: OnceLock = OnceLock::new(); #[inline] pub fn server_config() -> StkConfig { _SERVER_CONFIG.get().unwrap().clone() } static _PARTS_CONFIG: OnceLock = OnceLock::new(); #[inline] pub fn parts_config() -> PartsConfig { _PARTS_CONFIG.get().unwrap().clone() } static _PLANETS_CONFIG: OnceLock = OnceLock::new(); #[inline] pub fn planets_config() -> PlanetsConfig { _PLANETS_CONFIG.get().unwrap().clone() } // group main stk plugins together #[cfg(debug_assertions)] impl PluginGroup for StkPluginGroup { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() .add(TaskPoolPlugin::default()) .add(TypeRegistrationPlugin) .add(FrameCountPlugin) .add(TimePlugin) .add(ScheduleRunnerPlugin::run_loop(Duration::from_millis( server_config().server.tick_time_ms, ))) .add(bevy::log::LogPlugin { level: bevy::log::Level::DEBUG, filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(), update_subscriber: None, }) } } #[cfg(not(debug_assertions))] impl PluginGroup for StkPluginGroup { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() .add(TaskPoolPlugin::default()) .add(TypeRegistrationPlugin) .add(FrameCountPlugin) .add(TimePlugin) .add(ScheduleRunnerPlugin::run_loop(Duration::from_millis(1))) } } // auth token #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UserToken { pub id: i64, pub username: String, pub permission_level: i32, pub expires: std::time::SystemTime, } fn main() { // read the server main config let server_config = fs::read_to_string("/etc/starkingdoms/config.toml").unwrap(); let parts_config = fs::read_to_string("/etc/starkingdoms/parts.toml").unwrap(); let planets_config = fs::read_to_string("/etc/starkingdoms/planets.toml").unwrap(); // put config in variables let server_config: StkConfig = toml::from_str(&server_config).unwrap(); _SERVER_CONFIG.set(server_config.clone()).unwrap(); let parts_config: PartsConfig = toml::from_str(&parts_config).unwrap(); _PARTS_CONFIG.set(parts_config.clone()).unwrap(); let planets_config: PlanetsConfig = toml::from_str(&planets_config).unwrap(); _PLANETS_CONFIG.set(planets_config.clone()).unwrap(); // make the game, start it in .run() App::new() .insert_resource(AppKeys { app_key: server_config.security.app_key.as_bytes().to_vec(), }) .insert_resource(StkTungsteniteServerConfig { addr: server_config.server.bind.ip, port: server_config.server.bind.port, }) .insert_resource(server_config.clone()) .insert_resource(parts_config) .insert_resource(planets_config) .add_plugins(StkPluginGroup) .insert_resource(RapierConfiguration { gravity: Vect { x: 0.0, y: 0.0 }, ..Default::default() }) .init_resource::() .add_plugins(RapierPhysicsPlugin::::pixels_per_meter( server_config.world.pixels_per_meter, )) .add_plugins(StkTungsteniteServerPlugin) .add_systems(Startup, setup_integration_parameters) .add_systems(Startup, planet::spawn_planets) .add_systems(FixedUpdate, module::module_spawn) .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::player_input_update).chain(), ) .add_systems(FixedUpdate, module::save::save_eligibility) .add_systems(FixedUpdate, module::convert_modules) .insert_resource(Time::::from_seconds( server_config.server.world_fixed_timestep, )) .run(); // game is done running info!("Goodbye!"); } fn setup_integration_parameters(mut context: ResMut, server_config: Res) { context.integration_parameters = server_config.physics.parameters; match server_config.physics.solver { PhysicsSolver::SmallstepPGS => { context .integration_parameters .switch_to_small_steps_pgs_solver(); } PhysicsSolver::OldPGS => { context .integration_parameters .switch_to_standard_pgs_solver(); } } } fn gravity_update( mut part_query: Query< ( &Transform, &ReadMassProperties, &mut ExternalForce, &mut ExternalImpulse, ), With, >, planet_query: Query<(&Transform, &ReadMassProperties), With>, server_config: Res, ) { for (part_transform, part_mp, mut forces, mut impulses) in &mut part_query { impulses.impulse = Vec2::ZERO; forces.force = Vec2::ZERO; forces.torque = 0.; let part_mp = part_mp.get(); let part_mass = part_mp.mass; let part_translate = part_transform.translation; for (planet_transform, planet_mp) in &planet_query { let planet_mp = planet_mp.get(); let planet_mass = planet_mp.mass; let planet_translate = planet_transform.translation; let distance = planet_translate.distance(part_translate); let force = server_config.world.gravity * ((part_mass * planet_mass) / (distance * distance)); let direction = (planet_translate - part_translate).normalize() * force; /*let gravity_force = ExternalForce::at_point( direction.xy(), part_transform.translation.xy(), part_transform.translation.xy(), ); forces.force += gravity_force.force; forces.torque += gravity_force.torque;*/ impulses.impulse += direction.xy(); } } }