~starkingdoms/starkingdoms

79b5eba89ef3fcf1e1ce8c9d1d8b47c01d3f854e — ghostlyzsh 1 year, 11 months ago b42428e
broken save before changes
1 files changed, 244 insertions(+), 342 deletions(-)

M server/src/main.rs
M server/src/main.rs => server/src/main.rs +244 -342
@@ 24,16 24,11 @@ use bevy_rapier2d::prelude::*;
use bevy_twite::{twite::frame::MessageType, ServerEvent, TwiteServerConfig, TwiteServerPlugin};
use component::Input;
use component::*;
use hmac::{Hmac, Mac};
use jwt::VerifyWithKey;
use packet::*;
use rand::Rng;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use starkingdoms_common::SaveModule;
use starkingdoms_common::{pack_savefile, unpack_savefile, SaveData};
use std::f32::consts::PI;
use std::time::SystemTime;

pub mod component;
pub mod macros;


@@ 61,10 56,10 @@ const FREE_MODULE_CAP: usize = 30;

fn main() {
    // read the key in
    let key = std::fs::read("/etc/starkingdoms/app_key").unwrap();
    let key = std::fs::read_to_string("/etc/starkingdoms/app_key").unwrap();

    App::new()
        .insert_resource(AppKeys { app_key: key })
        .insert_resource(AppKeys { app_key: key.into_bytes() })
        .insert_resource(TwiteServerConfig {
            addr: Ipv4Addr::new(0, 0, 0, 0),
            port: 3000,


@@ 84,7 79,7 @@ fn main() {
        .add_systems(Startup, setup_integration_parameters)
        .add_systems(Startup, spawn_planets)
        .add_systems(FixedUpdate, module_spawn)
        .add_systems(Update, (on_message, on_login).chain())
        .add_systems(Update, on_message)
        .add_systems(Update, on_close)
        .add_systems(FixedUpdate, on_position_change)
        .add_systems(


@@ 217,12 212,41 @@ fn module_spawn(
    }
}

fn on_login(
    world: &mut World,
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<PlanetType>, Without<Player>, Without<Attach>),
    >,
    mut attached_query: Query<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Attach,
            &Velocity,
            Option<&CanAttach>,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>),
    >,
    mut player_query: Query<
        (Entity, &mut Player, &Transform, &Velocity, &mut Attach),
        Without<PlanetType>,
    >,
    mut packet_recv: Local<ManualEventReader<ServerEvent>>,
    mut packet_event_send: Local<Events<ServerEvent>>,
    mut packet_event_send: ResMut<Events<ServerEvent>>,
    app_keys: Res<AppKeys>,
) {
    let app_keys = world.resource::<AppKeys>().clone();
    let mut event_queue = Vec::new();
    for ev in packet_recv.read(&packet_event_send) {
        if let ServerEvent::Recv(addr, MessageType::Text, data) = ev {


@@ 231,6 255,7 @@ fn on_login(

            match packet {
                Packet::ClientLogin { username, save, jwt } => {

                    let angle: f32 = {
                        let mut rng = rand::thread_rng();
                        rng.gen::<f32>() * std::f32::consts::PI * 2.


@@ 241,26 266,26 @@ fn on_login(
                        0.0,
                    );
                    transform.rotate_z(angle);
                    let mut entity_id = world.spawn(PlayerBundle {
                        part: PartBundle {
                            part_type: PartType::Hearty,
                            transform: TransformBundle::from(transform),
                            flags: PartFlags { attached: false },
                        },
                        player: Player {
                            addr: *addr,
                            username: username.to_string(),
                            input: component::Input::default(),
                            selected: None,
                            save_eligibility: false,
                        },
                        attach: Attach {
                            associated_player: None,
                            parent: None,
                            children: [None, None, None, None],
                        },
                    });
                    entity_id
                    let entity_id = commands
                        .spawn(PlayerBundle {
                            part: PartBundle {
                                part_type: PartType::Hearty,
                                transform: TransformBundle::from(transform),
                                flags: PartFlags { attached: false },
                            },
                            player: Player {
                                addr: *addr,
                                username: username.to_string(),
                                input: component::Input::default(),
                                selected: None,
                                save_eligibility: false,
                            },
                            attach: Attach {
                                associated_player: None,
                                parent: None,
                                children: [None, None, None, None],
                            },
                        })
                        .insert(Collider::cuboid(
                            PART_HALF_SIZE / SCALE,
                            PART_HALF_SIZE / SCALE,


@@ 280,10 305,29 @@ fn on_login(
                        .insert(RigidBody::Dynamic);
                    let id = entity_id.id().index();

                    let mut planet_query = world.query::<(Entity, &PlanetType, &Transform)>();
                    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!
                            
                            load_savefile(&mut commands, transform, entity_id, entity_id, &mut attach, savefile.children, &mut attached_query, &mut part_query);
                        } 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(),
                            };
                            let buf = serde_json::to_vec(&packet).unwrap();
                            event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf));
                        }
                    } else {
                        // nothing to do
                    }

                    // tell this player the planets
                    let mut planets = Vec::new();
                    for (entity, planet_type, transform) in planet_query.iter(&world) {
                    for (entity, planet_type, transform) in planet_query.iter() {
                        let translation = transform.translation;
                        planets.push((
                            entity.index(),


@@ 305,15 349,8 @@ fn on_login(
                    event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf));

                    // tell the player already existing users
                    let mut player_query = world.query_filtered::<(
                        Entity,
                        &mut Player,
                        &Transform,
                        &Velocity,
                        &mut Attach,
                    ), Without<PlanetType>>();
                    let mut players = Vec::new();
                    for (entity, player, _, _, _) in player_query.iter(&world) {
                    for (entity, player, _, _, _) in &player_query {
                        players.push((entity.index(), player.username.clone()));
                    }
                    let packet = Packet::PlayerList { players };


@@ 336,18 373,8 @@ fn on_login(
                    event_queue.push(ServerEvent::Broadcast(MessageType::Text, buf));

                    // tell the player where parts are
                    let mut part_query =
                        world.query_filtered::<(
                            Entity,
                            &PartType,
                            &mut Transform,
                            &mut Velocity,
                            Option<&LooseAttach>,
                            &mut PartFlags,
                        ), (Without<PlanetType>, Without<Player>, Without<Attach>)>(
                        );
                    let mut parts = Vec::new();
                    for (entity, part_type, transform, _, _, flags) in part_query.iter(&world) {
                    for (entity, part_type, transform, _, _, flags) in &part_query {
                        parts.push((
                            entity.index(),
                            Part {


@@ 359,20 386,7 @@ fn on_login(
                            },
                        ));
                    }
                    let mut attached_query = world.query_filtered::<(
                        Entity,
                        &PartType,
                        &mut Transform,
                        &mut Attach,
                        &Velocity,
                        Option<&CanAttach>,
                        Option<&LooseAttach>,
                        &mut PartFlags,
                    ), (Without<PlanetType>, Without<Player>)>(
                    );
                    for (entity, part_type, transform, _, _, _, _, flags) in
                        attached_query.iter(&world)
                    {
                    for (entity, part_type, transform, _, _, _, _, flags) in &attached_query {
                        parts.push((
                            entity.index(),
                            Part {


@@ 407,282 421,7 @@ fn on_login(
                    };
                    let buf = serde_json::to_vec(&packet).unwrap();
                    event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf));
                    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 mut attach = entity_id.get_mut::<Attach>().unwrap();
                            load_savefile(
                                world,
                                transform,
                                entity_id.id(),
                                entity_id.id(),
                                &mut attach,
                                savefile.children,
                                &mut attached_query,
                                &mut part_query,
                            );
                        } 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(),
                            };
                            let buf = serde_json::to_vec(&packet).unwrap();
                            event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf));
                        }
                    } else {
                        // nothing to do
                    }
                }
                _ => {}
            }
        }
    }
    for event in event_queue {
        packet_event_send.send(event);
    }
}

fn load_savefile(
    world: &mut World,
    transform: Transform,
    player_id: Entity,
    parent: Entity,
    parent_attach: &mut Attach,
    children: Vec<Option<SaveModule>>,
    attached_query: &mut QueryState<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Attach,
            &Velocity,
            Option<&CanAttach>,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>),
    >,
    part_query: &mut QueryState<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Velocity,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>, Without<Attach>),
    >,
) -> bool {
    let mut ret = false;
    for (i, child) in children.iter().enumerate() {
        if let Some(child) = child {
            //let attachable = can_attach != None;

            let p_pos = transform.translation;
            let angle = transform.rotation.to_euler(EulerRot::ZYX).0;
            let mut offset = Vec2::ZERO;
            let mut angle_offset = 0.;
            if i == 2 {
                offset = Vec2::new(53., -53.);
                angle_offset = 0.;
            } else if i == 0 {
                offset = Vec2::new(-53., 53.);
                angle_offset = PI;
            } else if i == 1 {
                offset = Vec2::new(53., 53.);
                angle_offset = PI / 2.;
            } else if i == 3 {
                offset = Vec2::new(-53., -53.);
                angle_offset = -PI / 2.;
            }
            let mut module = world.spawn(PartBundle {
                transform: TransformBundle::from(
                    Transform::from_xyz(
                        p_pos.x + offset.x / SCALE * angle.cos(),
                        p_pos.y + offset.y / SCALE * angle.sin(),
                        0.,
                    )
                    .with_rotation(Quat::from_euler(
                        EulerRot::ZYX,
                        angle + angle_offset,
                        0.,
                        0.,
                    )),
                ),
                part_type: child.part_type.into(),
                flags: PartFlags { attached: true },
            });
            module
                .insert(RigidBody::Dynamic)
                .with_children(|children| {
                    children
                        .spawn(Collider::cuboid(18.75 / SCALE, 23.4375 / SCALE))
                        .insert(TransformBundle::from(Transform::from_xyz(
                            0.,
                            1.5625 / SCALE,
                            0.,
                        )));
                })
                .insert(AdditionalMassProperties::MassProperties(MassProperties {
                    local_center_of_mass: vec2(0.0, 0.0),
                    mass: 0.0001,
                    principal_inertia: 0.005,
                }))
                .insert(ExternalForce::default())
                .insert(ExternalImpulse::default())
                .insert(Velocity::default())
                .insert(ReadMassProperties::default());
            let joint = FixedJointBuilder::new()
                .local_anchor1(vec2(-53. / SCALE, 0. / SCALE))
                .local_basis2(-PI / 2.);
            let mut children = [None, None, None, None];
            if PartType::from(child.part_type) == PartType::LandingThruster {
                module.insert(Attach {
                    associated_player: Some(player_id),
                    parent: Some(module.id()),
                    children: [None, None, None, None],
                });
                let joint = PrismaticJointBuilder::new(Vec2::new(0., 1.))
                    .local_anchor1(Vec2::new(0., 0.))
                    .local_anchor2(Vec2::new(0., 0.))
                    .motor_position(0., 150., 10.)
                    .limits([0., 50. / SCALE])
                    .build();
                let mut suspension = world.spawn(PartBundle {
                    transform: TransformBundle::from(
                        Transform::from_xyz(
                            p_pos.x + offset.x / SCALE * angle.cos(),
                            p_pos.y + offset.y / SCALE * angle.sin(),
                            0.,
                        )
                        .with_rotation(Quat::from_euler(
                            EulerRot::ZYX,
                            angle + angle_offset,
                            0.,
                            0.,
                        )),
                    ),
                    part_type: PartType::LandingThrusterSuspension,
                    flags: PartFlags { attached: false },
                });
                suspension
                    .insert(RigidBody::Dynamic)
                    .with_children(|children| {
                        children
                            .spawn(Collider::cuboid(PART_HALF_SIZE / SCALE, 1. / SCALE))
                            .insert(TransformBundle::from(Transform::from_xyz(
                                0.,
                                -24. / SCALE,
                                0.,
                            )));
                    })
                    .insert(ImpulseJoint::new(module.id(), joint))
                    .insert(ExternalForce::default())
                    .insert(ExternalImpulse::default())
                    .insert(Velocity::default())
                    .insert(ReadMassProperties::default())
                    .insert(AdditionalMassProperties::MassProperties(MassProperties {
                        local_center_of_mass: vec2(0.0, 0.0),
                        mass: 0.00000000000001,
                        principal_inertia: 0.0000000000001,
                    }))
                    .insert(Attach {
                        associated_player: Some(player_id),
                        parent: Some(module.id()),
                        children: [None, None, None, None],
                    });
                children[2] = Some(suspension.id());
            }
            module.insert(ImpulseJoint::new(parent, joint));
            module.insert(Attach {
                associated_player: Some(player_id),
                parent: Some(parent),
                children,
            });
            parent_attach.children[i] = Some(module.id());
            //module.5.attached = true;

            ret = ret
                | if PartType::from(child.part_type) != PartType::LandingThruster {
                    load_savefile(
                        world,
                        transform,
                        player_id,
                        module.id(),
                        &mut module.get_mut::<Attach>().unwrap(),
                        child.children.clone(),
                        attached_query,
                        part_query,
                    )
                } else {
                    false
                };
        }
    }
    return ret;
}
// permissions:
// 0 - regular user (unauthenticated is 0)
// 10 - private alpha
// 20 - supervisor
// 30 - dev

const REQUIRED_PERMISSION_LEVEL: i32 = 10;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserToken {
    pub id: i64,
    pub username: String,
    pub permission_level: i32,
    pub expires: SystemTime,
}

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<PlanetType>, Without<Player>, Without<Attach>),
    >,
    mut attached_query: Query<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Attach,
            &Velocity,
            Option<&CanAttach>,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>),
    >,
    mut player_query: Query<
        (Entity, &mut Player, &Transform, &Velocity, &mut Attach),
        Without<PlanetType>,
    >,
    mut packet_recv: Local<ManualEventReader<ServerEvent>>,
    mut packet_event_send: ResMut<Events<ServerEvent>>,
    app_keys: Res<AppKeys>,
) {
    let mut event_queue = Vec::new();
    for ev in packet_recv.read(&packet_event_send) {
        if let ServerEvent::Recv(addr, MessageType::Text, data) = ev {
            let data = String::from_utf8_lossy(&data);
            let packet: Packet = err_or_cont!(serde_json::from_str(&data));

            match packet {
                Packet::SendMessage { target, content } => {
                    // find our player
                    let mut player = None;


@@ 1174,6 913,169 @@ fn on_message(
    }
}

fn load_savefile(
    commands: &mut Commands,
    transform: Transform,
    player_id: Entity,
    parent: Entity,
    parent_attach: &mut Attach,
    children: Vec<Option<SaveModule>>,
    attached_query: &mut Query<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Attach,
            &Velocity,
            Option<&CanAttach>,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>),
    >,
    part_query: &mut Query<
        (
            Entity,
            &PartType,
            &mut Transform,
            &mut Velocity,
            Option<&LooseAttach>,
            &mut PartFlags,
        ),
        (Without<PlanetType>, Without<Player>, Without<Attach>),
    >,
) -> bool {
    let mut ret = false;
    for (i, child) in children.iter().enumerate() {
        if let Some(child) = child {
            //let attachable = can_attach != None;
            
            let p_pos = transform.translation;
            let angle = transform.rotation.to_euler(EulerRot::ZYX).0;
            let mut offset = Vec2::ZERO;
            let mut angle_offset = 0.;
            if i == 2 {
                offset = Vec2::new(53., -53.);
                angle_offset = 0.;
            } else if i == 0 {
                offset = Vec2::new(-53., 53.);
                angle_offset = PI;
            } else if i == 1 {
                offset = Vec2::new(53., 53.);
                angle_offset = PI / 2.;
            } else if i == 3 {
                offset = Vec2::new(-53., -53.);
                angle_offset = -PI / 2.;
            }
            let module = commands.spawn(PartBundle {
                transform: TransformBundle::from(Transform::from_xyz(
                               p_pos.x + offset.x / SCALE * angle.cos(),
                               p_pos.y + offset.y / SCALE * angle.sin(),
                               0.,
                           ).with_rotation(Quat::from_euler(EulerRot::ZYX, angle + angle_offset, 0., 0.))),
                part_type: child.part_type.into(),
                flags: PartFlags { attached: true },
            })
            .insert(RigidBody::Dynamic)
            .with_children(|children| {
                children
                    .spawn(Collider::cuboid(18.75 / SCALE, 23.4375 / SCALE))
                    .insert(TransformBundle::from(Transform::from_xyz(
                        0.,
                        1.5625 / SCALE,
                        0.,
                    )));
            })
            .insert(AdditionalMassProperties::MassProperties(MassProperties {
                local_center_of_mass: vec2(0.0, 0.0),
                mass: 0.0001,
                principal_inertia: 0.005,
            }))
            .insert(ExternalForce::default())
            .insert(ExternalImpulse::default())
            .insert(Velocity::default())
            .insert(ReadMassProperties::default());
            let joint = FixedJointBuilder::new()
                .local_anchor1(vec2(-53. / SCALE, 0. / SCALE))
                .local_basis2(-PI / 2.);
            let mut children = [None, None, None, None];
            if child.part_type.into() == PartType::LandingThruster {
                module.insert(Attach {
                        associated_player: Some(player_id),
                        parent: Some(module.id()),
                        children: [None, None, None, None],
                    });
                let joint = PrismaticJointBuilder::new(Vec2::new(0., 1.))
                    .local_anchor1(Vec2::new(0., 0.))
                    .local_anchor2(Vec2::new(0., 0.))
                    .motor_position(0., 150., 10.)
                    .limits([0., 50. / SCALE])
                    .build();
                let mut suspension = commands.spawn(PartBundle {
                    transform: TransformBundle::from(Transform::from_xyz(
                                   p_pos.x + offset.x / SCALE * angle.cos(),
                                   p_pos.y + offset.y / SCALE * angle.sin(),
                                   0.,
                               ).with_rotation(Quat::from_euler(EulerRot::ZYX, angle + angle_offset, 0., 0.))),
                    part_type: PartType::LandingThrusterSuspension,
                    flags: PartFlags { attached: false },
                });
                suspension
                    .insert(RigidBody::Dynamic)
                    .with_children(|children| {
                        children
                            .spawn(Collider::cuboid(PART_HALF_SIZE / SCALE, 1. / SCALE))
                            .insert(TransformBundle::from(Transform::from_xyz(
                                0.,
                                -24. / SCALE,
                                0.,
                            )));
                    })
                    .insert(ImpulseJoint::new(module.id(), joint))
                    .insert(ExternalForce::default())
                    .insert(ExternalImpulse::default())
                    .insert(Velocity::default())
                    .insert(ReadMassProperties::default())
                    .insert(AdditionalMassProperties::MassProperties(MassProperties {
                        local_center_of_mass: vec2(0.0, 0.0),
                        mass: 0.00000000000001,
                        principal_inertia: 0.0000000000001,
                    }))
                    .insert(Attach {
                        associated_player: Some(player_id),
                        parent: Some(module.id()),
                        children: [None, None, None, None],
                    });
                children[2] = Some(suspension.id());
            }
            module.insert(ImpulseJoint::new(parent, joint));
            module.insert(Attach {
                associated_player: Some(player_id),
                parent: Some(parent),
                children,
            });
            attached_query.get_mut(parent).children[3] = Some(module);
            //module.5.attached = true;

            ret = ret
                | if *child.part_type.into() != PartType::LandingThruster {
                    load_savefile(
                        commands,
                        transform,
                        player_id,
                        module.id(),
                        child.children,
                        attached_query,
                        part_query,
                    )
                } else {
                    false
                };
        }
    }
    return ret;
}

fn construct_save_data(
    attach: Attach,
    attached_query: &Query<