From a8f056d1523e00e69662e2868f3976103ead82ff Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Thu, 22 Dec 2022 14:23:49 +0200 Subject: [PATCH] Move register/login to plugins, implement mostly --- sdbclient/src/main.rs | 17 +- .../src/plugins/menu/accountlogin/mod.rs | 123 ++++++++++++++ sdbclient/src/plugins/menu/accountlogin/ui.rs | 74 +++++++++ sdbclient/src/plugins/menu/accountloginui.rs | 43 ----- .../src/plugins/menu/accountregister/mod.rs | 153 ++++++++++++++++++ .../src/plugins/menu/accountregister/ui.rs | 101 ++++++++++++ .../src/plugins/menu/accountregisterui.rs | 53 ------ .../src/plugins/menu/accountscreenloggedin.rs | 39 ++++- sdbclient/src/plugins/menu/mod.rs | 37 +++-- 9 files changed, 507 insertions(+), 133 deletions(-) create mode 100644 sdbclient/src/plugins/menu/accountlogin/mod.rs create mode 100644 sdbclient/src/plugins/menu/accountlogin/ui.rs delete mode 100644 sdbclient/src/plugins/menu/accountloginui.rs create mode 100644 sdbclient/src/plugins/menu/accountregister/mod.rs create mode 100644 sdbclient/src/plugins/menu/accountregister/ui.rs delete mode 100644 sdbclient/src/plugins/menu/accountregisterui.rs diff --git a/sdbclient/src/main.rs b/sdbclient/src/main.rs index 4a4c456..5d8971d 100644 --- a/sdbclient/src/main.rs +++ b/sdbclient/src/main.rs @@ -28,14 +28,6 @@ pub enum GameState { Game, } -// Input structs -/// Login inputs -#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)] -pub struct InputsUserLogin(String, String); -/// Register inputs -#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)] -pub struct InputsUserRegister(String, String, String, String); - fn main() { let mut app = App::new(); @@ -97,14 +89,7 @@ fn main() { }) .insert_resource(cfg::CfgHidden { api_server: "http://localhost:8080/api".to_string(), - }) - .insert_resource(InputsUserLogin("".to_string(), "".to_string())) - .insert_resource(InputsUserRegister( - "".to_string(), - "".to_string(), - "".to_string(), - "".to_string(), - )); + }); app.add_startup_system(setup).add_state(GameState::Splash); diff --git a/sdbclient/src/plugins/menu/accountlogin/mod.rs b/sdbclient/src/plugins/menu/accountlogin/mod.rs new file mode 100644 index 0000000..0e2f147 --- /dev/null +++ b/sdbclient/src/plugins/menu/accountlogin/mod.rs @@ -0,0 +1,123 @@ +/* + * This file is part of sdbclient + * Copyright (C) 2022 Jonni Liljamo + * + * Licensed under GPL-3.0-only. + * See LICENSE for licensing information. + */ + +use bevy::{ + prelude::*, + tasks::{AsyncComputeTaskPool, Task}, +}; +use bevy_console::PrintConsoleLine; + +use futures_lite::future; + +use crate::{ + api::{self, user::ResponseToken}, + cfg::{self, CfgDirs, CfgHidden, CfgUser}, + util, +}; + +use super::MenuState; + +pub mod ui; + +pub struct AccountLoginPlugin; + +impl Plugin for AccountLoginPlugin { + fn build(&self, app: &mut App) { + app.add_state(LoginState::None) + // UI system + .insert_resource(ui::InputsUserLogin::new()) + .add_system_set( + SystemSet::on_update(LoginState::Input).with_system(ui::account_login_ui), + ) + // Login system, as in calling the API + .add_system_set( + SystemSet::on_enter(LoginState::LoggingIn).with_system(start_login_call), + ) + .add_system_set( + SystemSet::on_update(LoginState::LoggingIn).with_system(handle_login_call), + ); + } +} + +/// Login State +#[derive(Clone, Eq, PartialEq, Debug, Hash)] +pub enum LoginState { + None, + Input, + LoggingIn, +} + +#[derive(Component)] +struct LoginCall(Task); + +fn start_login_call( + mut commands: Commands, + cfg_hidden: Res, + inputs: Res, +) { + let api_address = cfg_hidden.api_server.clone(); + let i = inputs.clone(); + + let thread_pool = AsyncComputeTaskPool::get(); + let task = thread_pool.spawn(async move { + let token_response = api::user::token(api_address, i.email, i.password); + + token_response + }); + commands.spawn(LoginCall(task)); +} + +fn handle_login_call( + mut commands: Commands, + mut login_call_tasks: Query<(Entity, &mut LoginCall)>, + mut inputs: ResMut, + mut login_state: ResMut>, + mut menu_state: ResMut>, + mut cfg_user: ResMut, + cfg_dirs: Res, + mut console: EventWriter, +) { + let (entity, mut task) = login_call_tasks.single_mut(); + if let Some(login_response) = future::block_on(future::poll_once(&mut task.0)) { + match login_response { + ResponseToken::Valid(res) => { + console.send(PrintConsoleLine::new(format!( + "Logged in with: {}", + inputs.email + ))); + + // TODO: We need to fetch the user details at some point. + *cfg_user = CfgUser { + logged_in: true, + user_token: res.token, + id: res.id, + username: "NOT SET".to_string(), + email: "NOT SET".to_string(), + }; + + util::sl::save( + cfg_dirs.0.config_dir().to_str().unwrap(), + cfg::FILE_CFG_USER, + &cfg_user.into_inner(), + console, + ); + + login_state.set(LoginState::None).unwrap(); + menu_state.set(MenuState::AccountLoggedIn).unwrap(); + } + ResponseToken::Error { error } => { + inputs.error = error; + login_state.set(LoginState::Input).unwrap(); + } + } + + // Remove the task, since it's done now + commands.entity(entity).remove::(); + commands.entity(entity).despawn_recursive(); + } +} diff --git a/sdbclient/src/plugins/menu/accountlogin/ui.rs b/sdbclient/src/plugins/menu/accountlogin/ui.rs new file mode 100644 index 0000000..a41d613 --- /dev/null +++ b/sdbclient/src/plugins/menu/accountlogin/ui.rs @@ -0,0 +1,74 @@ +/* + * This file is part of sdbclient + * Copyright (C) 2022 Jonni Liljamo + * + * Licensed under GPL-3.0-only. + * See LICENSE for licensing information. + */ + +use bevy::prelude::*; +use bevy_egui::{egui, EguiContext}; + +use crate::util::eguipwd; + +use crate::plugins::menu::MenuState; + +use super::LoginState; + +/// Login inputs +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)] +pub struct InputsUserLogin { + pub email: String, + pub password: String, + pub error: String, +} + +impl InputsUserLogin { + pub fn new() -> Self { + Self { + email: "".to_string(), + password: "".to_string(), + error: "".to_string(), + } + } +} + +pub fn account_login_ui( + mut egui_context: ResMut, + mut menu_state: ResMut>, + mut login_state: ResMut>, + mut inputs: ResMut, +) { + egui::Window::new("Login") + .collapsible(false) + .show(egui_context.ctx_mut(), |ui| { + ui.horizontal(|ui| { + ui.label("Email: "); + ui.text_edit_singleline(&mut inputs.email); + }); + + ui.horizontal(|ui| { + ui.label("Password: "); + ui.add(eguipwd::password(&mut inputs.password)); + }); + + // Show an error if there is one + if !inputs.error.is_empty() { + ui.horizontal(|ui| { + ui.label(egui::RichText::new(inputs.error.clone()).color(egui::Color32::RED)); + }); + } + + ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { + if ui.button("Cancel").clicked() { + login_state.set(LoginState::None).unwrap(); + menu_state.set(MenuState::AccountLoggedOut).unwrap(); + } + if ui.button("Login").clicked() { + login_state.set(LoginState::LoggingIn).unwrap(); + // Reset error field + inputs.error = "".to_string(); + } + }) + }); +} diff --git a/sdbclient/src/plugins/menu/accountloginui.rs b/sdbclient/src/plugins/menu/accountloginui.rs deleted file mode 100644 index a6349c7..0000000 --- a/sdbclient/src/plugins/menu/accountloginui.rs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of sdbclient - * Copyright (C) 2022 Jonni Liljamo - * - * Licensed under GPL-3.0-only. - * See LICENSE for licensing information. - */ - -use bevy::prelude::*; -use bevy_egui::{egui, EguiContext}; - -use crate::{util::eguipwd, InputsUserLogin}; - -use super::MenuState; - -pub fn account_login_ui( - mut egui_context: ResMut, - mut menu_state: ResMut>, - mut inputs: ResMut, -) { - egui::Window::new("Login") - .collapsible(false) - .show(egui_context.ctx_mut(), |ui| { - ui.horizontal(|ui| { - ui.label("Email: "); - ui.text_edit_singleline(&mut inputs.0); - }); - - ui.horizontal(|ui| { - ui.label("Password: "); - ui.add(eguipwd::password(&mut inputs.1)); - }); - - ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { - if ui.button("Cancel").clicked() { - menu_state.set(MenuState::Main).unwrap(); - } - if ui.button("Login").clicked() { - info!("todo"); - } - }) - }); -} diff --git a/sdbclient/src/plugins/menu/accountregister/mod.rs b/sdbclient/src/plugins/menu/accountregister/mod.rs new file mode 100644 index 0000000..dd3e5ff --- /dev/null +++ b/sdbclient/src/plugins/menu/accountregister/mod.rs @@ -0,0 +1,153 @@ +/* + * This file is part of sdbclient + * Copyright (C) 2022 Jonni Liljamo + * + * Licensed under GPL-3.0-only. + * See LICENSE for licensing information. + */ + +use bevy::{ + prelude::*, + tasks::{AsyncComputeTaskPool, Task}, +}; +use bevy_console::PrintConsoleLine; + +use futures_lite::future; + +use crate::{ + api::{ + self, + user::{ResponseRegister, ResponseToken}, + }, + cfg::{self, CfgDirs, CfgHidden, CfgUser}, + util, +}; + +use super::MenuState; + +pub mod ui; + +pub struct AccountRegisterPlugin; + +impl Plugin for AccountRegisterPlugin { + fn build(&self, app: &mut App) { + app.add_state(RegisterState::None) + // UI system + .insert_resource(ui::InputsUserRegister::new()) + .add_system_set( + SystemSet::on_update(RegisterState::Input).with_system(ui::account_register_ui), + ) + // Register system, as in calling the API + .add_system_set( + SystemSet::on_enter(RegisterState::Registering).with_system(start_register_call), + ) + .add_system_set( + SystemSet::on_update(RegisterState::Registering).with_system(handle_register_call), + ); + } +} + +/// Register State +#[derive(Clone, Eq, PartialEq, Debug, Hash)] +pub enum RegisterState { + None, + Input, + Registering, +} + +struct RegisterCallResponse { + register: ResponseRegister, + token: ResponseToken, +} + +#[derive(Component)] +struct RegisterCall(Task); + +fn start_register_call( + mut commands: Commands, + cfg_hidden: Res, + inputs: Res, +) { + let api_address = cfg_hidden.api_server.clone(); + let i = inputs.clone(); + + let thread_pool = AsyncComputeTaskPool::get(); + let task = thread_pool.spawn(async move { + let register_response = api::user::register( + api_address.clone(), + i.username.clone(), + i.email.clone(), + i.password.clone(), + ); + + // TODO: This is bad, if the above fails, this will too. Or maybe that doesn't matter? + let token_response = + api::user::token(api_address.clone(), i.email.clone(), i.password.clone()); + + RegisterCallResponse { + register: register_response, + token: token_response, + } + }); + commands.spawn(RegisterCall(task)); +} + +fn handle_register_call( + mut commands: Commands, + mut register_call_tasks: Query<(Entity, &mut RegisterCall)>, + mut inputs: ResMut, + mut register_state: ResMut>, + mut menu_state: ResMut>, + mut cfg_user: ResMut, + cfg_dirs: Res, + mut console: EventWriter, +) { + let (entity, mut task) = register_call_tasks.single_mut(); + if let Some(register_call_response) = future::block_on(future::poll_once(&mut task.0)) { + match register_call_response.register { + ResponseRegister::Valid(register_res) => { + console.send(PrintConsoleLine::new(format!( + "Registered user: {}", + register_res.username, + ))); + + match register_call_response.token { + ResponseToken::Valid(token_res) => { + *cfg_user = CfgUser { + logged_in: true, + user_token: token_res.token, + id: register_res.id, + username: register_res.username, + email: register_res.email, + }; + + util::sl::save( + cfg_dirs.0.config_dir().to_str().unwrap(), + cfg::FILE_CFG_USER, + &cfg_user.into_inner(), + console, + ); + } + ResponseToken::Error { error } => { + // TODO: Handle? Is it possible to even get here without the server shitting itself between the register and token calls? + // And if the server does indeed shit itself between those calls, the user can just login normally, so 🤷‍♀️ + console.send(PrintConsoleLine::new(format!( + "Something went wrong with getting the user token after registering, got error: '{}'", error + ))); + } + } + + register_state.set(RegisterState::None).unwrap(); + menu_state.set(MenuState::AccountLoggedIn).unwrap(); + } + ResponseRegister::Error { error } => { + inputs.error = error; + register_state.set(RegisterState::Input).unwrap(); + } + } + + // Remove the task, since it's done now + commands.entity(entity).remove::(); + commands.entity(entity).despawn_recursive(); + } +} diff --git a/sdbclient/src/plugins/menu/accountregister/ui.rs b/sdbclient/src/plugins/menu/accountregister/ui.rs new file mode 100644 index 0000000..2c4f130 --- /dev/null +++ b/sdbclient/src/plugins/menu/accountregister/ui.rs @@ -0,0 +1,101 @@ +/* + * This file is part of sdbclient + * Copyright (C) 2022 Jonni Liljamo + * + * Licensed under GPL-3.0-only. + * See LICENSE for licensing information. + */ + +use bevy::prelude::*; +use bevy_egui::{egui, EguiContext}; + +use crate::util::eguipwd; + +use crate::plugins::menu::MenuState; + +use super::RegisterState; + +/// Register inputs +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)] +pub struct InputsUserRegister { + pub username: String, + pub email: String, + pub password: String, + pub password_confirm: String, + pub passwords_match: bool, + pub error: String, +} + +impl InputsUserRegister { + pub fn new() -> Self { + Self { + username: "".to_string(), + email: "".to_string(), + password: "".to_string(), + password_confirm: "".to_string(), + passwords_match: true, + error: "".to_string(), + } + } +} + +pub fn account_register_ui( + mut egui_context: ResMut, + mut menu_state: ResMut>, + mut register_state: ResMut>, + mut inputs: ResMut, +) { + egui::Window::new("Register") + .collapsible(false) + .show(egui_context.ctx_mut(), |ui| { + ui.horizontal(|ui| { + ui.label("Username: "); + ui.text_edit_singleline(&mut inputs.username); + }); + + ui.horizontal(|ui| { + ui.label("Email: "); + ui.text_edit_singleline(&mut inputs.email); + }); + + ui.horizontal(|ui| { + ui.label("Password: "); + ui.add(eguipwd::password(&mut inputs.password)); + }); + + ui.horizontal(|ui| { + ui.label("Confirm password: "); + ui.add(eguipwd::password(&mut inputs.password_confirm)); + }); + + inputs.passwords_match = inputs.password == inputs.password_confirm; + + // Show an error if there is one + if !inputs.passwords_match { + ui.horizontal(|ui| { + ui.label( + egui::RichText::new("passwords don't match").color(egui::Color32::RED), + ); + }); + } else if !inputs.error.is_empty() { + ui.horizontal(|ui| { + ui.label(egui::RichText::new(inputs.error.clone()).color(egui::Color32::RED)); + }); + } + + ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { + if ui.button("Cancel").clicked() { + register_state.set(RegisterState::None).unwrap(); + menu_state.set(MenuState::AccountLoggedOut).unwrap(); + } + + ui.add_enabled_ui(inputs.passwords_match, |ui| { + if ui.button("Register").clicked() { + register_state.set(RegisterState::Registering).unwrap(); + // Reset error field + inputs.error = "".to_string(); + } + }); + }) + }); +} diff --git a/sdbclient/src/plugins/menu/accountregisterui.rs b/sdbclient/src/plugins/menu/accountregisterui.rs deleted file mode 100644 index 7f542c5..0000000 --- a/sdbclient/src/plugins/menu/accountregisterui.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of sdbclient - * Copyright (C) 2022 Jonni Liljamo - * - * Licensed under GPL-3.0-only. - * See LICENSE for licensing information. - */ - -use bevy::prelude::*; -use bevy_egui::{egui, EguiContext}; - -use crate::{util::eguipwd, InputsUserRegister}; - -use super::MenuState; - -pub fn account_register_ui( - mut egui_context: ResMut, - mut menu_state: ResMut>, - mut inputs: ResMut, -) { - egui::Window::new("Register") - .collapsible(false) - .show(egui_context.ctx_mut(), |ui| { - ui.horizontal(|ui| { - ui.label("Username: "); - ui.text_edit_singleline(&mut inputs.0); - }); - - ui.horizontal(|ui| { - ui.label("Email: "); - ui.text_edit_singleline(&mut inputs.1); - }); - - ui.horizontal(|ui| { - ui.label("Password: "); - ui.add(eguipwd::password(&mut inputs.2)); - }); - - ui.horizontal(|ui| { - ui.label("Confirm password: "); - ui.add(eguipwd::password(&mut inputs.3)); - }); - - ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| { - if ui.button("Cancel").clicked() { - menu_state.set(MenuState::Main).unwrap(); - } - if ui.button("Register").clicked() { - info!("todo"); - } - }) - }); -} diff --git a/sdbclient/src/plugins/menu/accountscreenloggedin.rs b/sdbclient/src/plugins/menu/accountscreenloggedin.rs index 03bd2a9..d3f5792 100644 --- a/sdbclient/src/plugins/menu/accountscreenloggedin.rs +++ b/sdbclient/src/plugins/menu/accountscreenloggedin.rs @@ -11,14 +11,21 @@ use bevy::{ ui::{JustifyContent, Size, Style, Val}, }; +use crate::cfg::CfgUser; + use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR}; /// Tag component for tagging entities on settings menu screen #[derive(Component)] pub struct OnAccountLoggedInScreen; -pub fn account_loggedin_setup(mut commands: Commands, asset_server: Res) { +pub fn account_loggedin_setup( + mut commands: Commands, + asset_server: Res, + cfg_user: ResMut, +) { let font = asset_server.load("fonts/FiraMono-Regular.ttf"); + let font_bold = asset_server.load("fonts/FiraMono-Bold.ttf"); let button_style = Style { size: Size::new(Val::Px(200.0), Val::Px(65.0)), @@ -63,6 +70,34 @@ pub fn account_loggedin_setup(mut commands: Commands, asset_server: Res)) - // Account login and register systems - .add_system_set(SystemSet::on_update(MenuState::AccountLogin).with_system(account_login_ui)) - .add_system_set(SystemSet::on_update(MenuState::AccountRegister).with_system(account_register_ui)) // Common systems .add_system_set(SystemSet::on_update(GameState::MainMenu).with_system(menu_action).with_system(button_system)); + + app.add_plugin(accountregister::AccountRegisterPlugin) + .add_plugin(accountlogin::AccountLoginPlugin); } } @@ -97,14 +94,6 @@ pub enum MenuState { #[derive(Component)] struct OnAccountScreen; -/// Tag component for tagging entities on login screen -#[derive(Component)] -struct OnLoginScreen; - -/// Tag component for tagging entities on register screen -#[derive(Component)] -struct OnRegisterScreen; - const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15); const HOVERED_BUTTON: Color = Color::rgb(0.25, 0.25, 0.25); const HOVERED_PRESSED_BUTTON: Color = Color::rgb(0.25, 0.65, 0.25); @@ -125,6 +114,7 @@ enum MenuButtonAction { Account, AccountLogin, AccountRegister, + AccountLogout, BackToMainMenu, BackToSettings, Exit, @@ -137,6 +127,8 @@ fn menu_action( >, mut app_exit_events: EventWriter, mut menu_state: ResMut>, + mut register_state: ResMut>, + mut login_state: ResMut>, //mut game_state: ResMut>, cfg_user: Res, ) { @@ -144,7 +136,7 @@ fn menu_action( if *interaction == Interaction::Clicked { match menu_button_action { MenuButtonAction::Exit => app_exit_events.send(AppExit), - MenuButtonAction::Play => println!("todo"), + MenuButtonAction::Play => warn!("todo"), MenuButtonAction::Settings => menu_state.set(MenuState::Settings).unwrap(), MenuButtonAction::SettingsDisplay => { menu_state.set(MenuState::SettingsDisplay).unwrap() @@ -160,10 +152,17 @@ fn menu_action( menu_state.set(MenuState::AccountLoggedOut).unwrap() } } - MenuButtonAction::AccountLogin => menu_state.set(MenuState::AccountLogin).unwrap(), + MenuButtonAction::AccountLogin => { + menu_state.set(MenuState::AccountLogin).unwrap(); + login_state.set(accountlogin::LoginState::Input).unwrap(); + } MenuButtonAction::AccountRegister => { - menu_state.set(MenuState::AccountRegister).unwrap() + menu_state.set(MenuState::AccountRegister).unwrap(); + register_state + .set(accountregister::RegisterState::Input) + .unwrap(); } + MenuButtonAction::AccountLogout => warn!("todo"), MenuButtonAction::BackToSettings => menu_state.set(MenuState::Settings).unwrap(), MenuButtonAction::BackToMainMenu => menu_state.set(MenuState::Main).unwrap(), } -- 2.44.1