DEVELOPMENT ENVIRONMENT

~liljamo/deck-builder

106a55af94523d2dcca350a05b8b53d83bb93619 — Jonni Liljamo 1 year, 4 months ago 7d5486e master
chore: remove old-client-for-ref
63 files changed, 0 insertions(+), 3923 deletions(-)

D client-old-for-ref/.gitignore
D client-old-for-ref/Cargo.toml
D client-old-for-ref/LICENSE
D client-old-for-ref/README.md
D client-old-for-ref/assets/branding/logo.png
D client-old-for-ref/assets/fonts/FiraMono-Bold.ttf
D client-old-for-ref/assets/fonts/FiraMono-Medium.ttf
D client-old-for-ref/assets/fonts/FiraMono-Regular.ttf
D client-old-for-ref/assets/fonts/OFL.txt
D client-old-for-ref/assets/scripts/ui_play.lua
D client-old-for-ref/src/api/game/all_forming.rs
D client-old-for-ref/src/api/game/create.rs
D client-old-for-ref/src/api/game/info.rs
D client-old-for-ref/src/api/game/join.rs
D client-old-for-ref/src/api/game/mod.rs
D client-old-for-ref/src/api/game/mygames.rs
D client-old-for-ref/src/api/game/patchstate.rs
D client-old-for-ref/src/api/game/types.rs
D client-old-for-ref/src/api/mod.rs
D client-old-for-ref/src/api/user/mod.rs
D client-old-for-ref/src/api/user/types.rs
D client-old-for-ref/src/cfg/mod.rs
D client-old-for-ref/src/constants.rs
D client-old-for-ref/src/lua/mod.rs
D client-old-for-ref/src/main.rs
D client-old-for-ref/src/plugins/config/mod.rs
D client-old-for-ref/src/plugins/menu/mod.rs
D client-old-for-ref/src/plugins/menu/ui/connect/mod.rs
D client-old-for-ref/src/plugins/menu/ui/connect/ui.rs
D client-old-for-ref/src/plugins/menu/ui/menu.rs
D client-old-for-ref/src/plugins/menu/ui/mod.rs
D client-old-for-ref/src/plugins/menu/ui/play/mod.rs
D client-old-for-ref/src/plugins/menu/ui/play/ui.rs
D client-old-for-ref/src/plugins/menu/ui/settings/mod.rs
D client-old-for-ref/src/plugins/menu/ui/settings/ui.rs
D client-old-for-ref/src/plugins/menu_old/accountlogin/mod.rs
D client-old-for-ref/src/plugins/menu_old/accountlogin/ui.rs
D client-old-for-ref/src/plugins/menu_old/accountregister/mod.rs
D client-old-for-ref/src/plugins/menu_old/accountregister/ui.rs
D client-old-for-ref/src/plugins/menu_old/accountscreenloggedin.rs
D client-old-for-ref/src/plugins/menu_old/accountscreenloggedout.rs
D client-old-for-ref/src/plugins/menu_old/mainmenuscreen.rs
D client-old-for-ref/src/plugins/menu_old/mod.rs
D client-old-for-ref/src/plugins/menu_old/play/allformingcall.rs
D client-old-for-ref/src/plugins/menu_old/play/creategamecall.rs
D client-old-for-ref/src/plugins/menu_old/play/joingamecall.rs
D client-old-for-ref/src/plugins/menu_old/play/mod.rs
D client-old-for-ref/src/plugins/menu_old/play/mygamescall.rs
D client-old-for-ref/src/plugins/menu_old/play/startgamecall.rs
D client-old-for-ref/src/plugins/menu_old/play/ui.rs
D client-old-for-ref/src/plugins/menu_old/settingsscreen.rs
D client-old-for-ref/src/plugins/mod.rs
D client-old-for-ref/src/plugins/phases/loading/mod.rs
D client-old-for-ref/src/plugins/phases/mod.rs
D client-old-for-ref/src/plugins/phases/splash/mod.rs
D client-old-for-ref/src/runtime/game/mod.rs
D client-old-for-ref/src/runtime/menu/mod.rs
D client-old-for-ref/src/runtime/mod.rs
D client-old-for-ref/src/util/egui/menuwindow.rs
D client-old-for-ref/src/util/egui/mod.rs
D client-old-for-ref/src/util/egui/password.rs
D client-old-for-ref/src/util/mod.rs
D client-old-for-ref/src/util/sl.rs
D client-old-for-ref/.gitignore => client-old-for-ref/.gitignore +0 -1
@@ 1,1 0,0 @@
target/

D client-old-for-ref/Cargo.toml => client-old-for-ref/Cargo.toml +0 -38
@@ 1,38 0,0 @@
[package]
name = "laurelin_client"
version = "0.1.0"
authors = ["Jonni Liljamo <jonni@liljamo.com>"]
edition = "2021"
description = "Client for a deck building game"
readme = "README.md"
repository = "https://src.quest/skye/deck-builder"
license = "GPL-3.0-only"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# one dep needs ^0.5.9, and another one needs ^0.5.10
# and for some reason cargo can't figure this out, so we're forcing it for now.
toml = "0.7.1"

# more dep shit
proc-macro2 = "1.0.50"

bevy = { version = "0.10.1" }

# lua scripting
#bevy_mod_scripting = { version = "0.2.1", features = ["lua", "lua54", "lua_script_api"] }

# http requests
reqwest = { version = "0.11.14", features = ["blocking", "json"] }

# (de)serialization
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.91"
serde_repr = "0.1.10"

# futures for async
futures-lite = "1.12.0"

# get dirs for saving data
directories = "4.0.1"

D client-old-for-ref/LICENSE => client-old-for-ref/LICENSE +0 -8
@@ 1,8 0,0 @@
sdbclient is a client for a deck building game.
Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.

D client-old-for-ref/README.md => client-old-for-ref/README.md +0 -1
@@ 1,1 0,0 @@
# Laurelin Client

D client-old-for-ref/assets/branding/logo.png => client-old-for-ref/assets/branding/logo.png +0 -0
D client-old-for-ref/assets/fonts/FiraMono-Bold.ttf => client-old-for-ref/assets/fonts/FiraMono-Bold.ttf +0 -0
D client-old-for-ref/assets/fonts/FiraMono-Medium.ttf => client-old-for-ref/assets/fonts/FiraMono-Medium.ttf +0 -0
D client-old-for-ref/assets/fonts/FiraMono-Regular.ttf => client-old-for-ref/assets/fonts/FiraMono-Regular.ttf +0 -0
D client-old-for-ref/assets/fonts/OFL.txt => client-old-for-ref/assets/fonts/OFL.txt +0 -93
@@ 1,93 0,0 @@
Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A.

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

D client-old-for-ref/assets/scripts/ui_play.lua => client-old-for-ref/assets/scripts/ui_play.lua +0 -12
@@ 1,12 0,0 @@
--[[
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
]]

function on_start()
    print_to_console("hello from lua")
    print("hello from lua")
end

D client-old-for-ref/src/api/game/all_forming.rs => client-old-for-ref/src/api/game/all_forming.rs +0 -33
@@ 1,33 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::{types, APIErrorWrapper};

pub type ResultAllForming = Vec<types::Game>;

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseAllForming {
    Error(APIErrorWrapper),
    Valid(ResultAllForming),
}

pub fn all_forming(api_address: String, token: String) -> ResponseAllForming {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .get(&format!("{}/game/all_forming", api_address))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/create.rs => client-old-for-ref/src/api/game/create.rs +0 -31
@@ 1,31 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::{types::Game, APIErrorWrapper};

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseCreateGame {
    Error(APIErrorWrapper),
    Valid(Game),
}

pub fn create(api_address: String, token: String) -> ResponseCreateGame {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .post(&format!("{}/game/create", api_address))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/info.rs => client-old-for-ref/src/api/game/info.rs +0 -31
@@ 1,31 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::{types, APIErrorWrapper};

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseInfo {
    Error(APIErrorWrapper),
    Valid(types::Game),
}

pub fn info(api_address: String, token: String, game_id: String) -> ResponseInfo {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .get(&format!("{}/game/{}", api_address, game_id))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/join.rs => client-old-for-ref/src/api/game/join.rs +0 -34
@@ 1,34 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::APIErrorWrapper;

#[derive(Debug, Serialize, Deserialize)]
pub struct ResultJoinGame {}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseJoinGame {
    Error(APIErrorWrapper),
    Valid(ResultJoinGame),
}

pub fn join(api_address: String, token: String, game_id: String) -> ResponseJoinGame {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .post(&format!("{}/game/{}/join", api_address, game_id))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/mod.rs => client-old-for-ref/src/api/game/mod.rs +0 -29
@@ 1,29 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use super::APIErrorWrapper;

pub mod types;

mod create;
pub use create::*;

mod all_forming;
pub use all_forming::*;

mod info;
pub use info::*;

mod join;
pub use join::*;

mod mygames;
pub use mygames::*;

mod patchstate;
pub use patchstate::*;

D client-old-for-ref/src/api/game/mygames.rs => client-old-for-ref/src/api/game/mygames.rs +0 -33
@@ 1,33 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::{types, APIErrorWrapper};

pub type ResultMyGames = Vec<types::Game>;

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseMyGames {
    Error(APIErrorWrapper),
    Valid(ResultMyGames),
}

pub fn my_games(api_address: String, token: String) -> ResponseMyGames {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .get(&format!("{}/game/my_games", api_address))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/patchstate.rs => client-old-for-ref/src/api/game/patchstate.rs +0 -45
@@ 1,45 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::{
    types::{Game, GameState},
    APIErrorWrapper,
};

#[derive(Serialize)]
pub struct PostState {
    pub state: GameState,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponsePatchGameState {
    Error(APIErrorWrapper),
    Valid(Game),
}

pub fn patch_state(
    api_address: String,
    token: String,
    game_id: String,
    state: GameState,
) -> ResponsePatchGameState {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .patch(&format!("{}/game/{}/state", api_address, game_id))
        .header("Authorization", token)
        .json(&PostState { state })
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/game/types.rs => client-old-for-ref/src/api/game/types.rs +0 -67
@@ 1,67 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

use crate::api::user::types::UserPub;

#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, PartialEq)]
#[repr(u8)]
pub enum GameState {
    Forming = 0,
    InProgress = 1,
    Finished = 2,
    Cancelled = 3,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Game {
    pub id: String,
    pub created_at: String,
    pub updated_at: String,
    pub host_id: String,
    pub host: UserPub,
    pub guest_id: String,
    pub guest: UserPub,
    pub state: GameState,
    pub ended_at: String,
    pub game_data_id: String,
    pub game_data: GameData,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Action {
    pub id: String,
    pub created_at: String,
    pub updated_at: String,
    pub game_data_id: String,
    pub invoker: String,
    pub data: String,
    pub timestamp: String,
}

#[derive(Debug, Serialize_repr, Deserialize_repr, Clone, PartialEq)]
#[repr(u8)]
pub enum GameTurn {
    Host = 0,
    Guest = 1,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GameData {
    pub id: String,
    pub created_at: String,
    pub updated_at: String,
    pub actions: Option<Vec<Action>>,
    pub turn: GameTurn,
    pub host_hand: Option<String>,
    pub host_deck: Option<String>,
    pub guest_hand: Option<String>,
    pub guest_deck: Option<String>,
}

D client-old-for-ref/src/api/mod.rs => client-old-for-ref/src/api/mod.rs +0 -47
@@ 1,47 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

pub mod game;
pub mod user;

#[derive(Debug, Deserialize, Serialize)]
pub struct APIError {
    pub id: u16,
    pub name: String,
    pub description: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct APIErrorWrapper {
    pub error: APIError,
}

impl std::fmt::Display for APIErrorWrapper {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} {}", self.error.id, self.error.description)
    }
}

#[derive(Serialize, Deserialize)]
pub struct APIInfo {
    pub info: String,
    pub ver: String,
}

pub fn info(api_address: String) -> Result<APIInfo, String> {
    let client = reqwest::blocking::Client::new();

    let resp = client.get(&format!("{}/info", api_address)).send();
    match resp {
        Ok(r) => Ok(r.json().unwrap()),
        Err(_) => Err("Could not reach API".to_string()),
    }
}

D client-old-for-ref/src/api/user/mod.rs => client-old-for-ref/src/api/user/mod.rs +0 -106
@@ 1,106 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use reqwest;
use serde::{Deserialize, Serialize};

use super::APIErrorWrapper;

pub mod types;

#[derive(Serialize)]
pub struct PostToken {
    pub email: String,
    pub password: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ResultToken {
    pub token: String,
    pub id: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseToken {
    Error(APIErrorWrapper),
    Valid(ResultToken),
}

pub fn token(api_address: String, email: String, password: String) -> ResponseToken {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .post(&format!("{}/user/token", api_address))
        .json(&PostToken { email, password })
        .send()
        .unwrap();

    resp.json().unwrap()
}

#[derive(Serialize)]
pub struct PostRegister {
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseRegister {
    Error(APIErrorWrapper),
    Valid(types::UserPub),
}

pub fn register(
    api_address: String,
    username: String,
    email: String,
    password: String,
) -> ResponseRegister {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .post(&format!("{}/user/register", api_address))
        .json(&PostRegister {
            username,
            email,
            password,
        })
        .send()
        .unwrap();

    resp.json().unwrap()
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ResultUserInfoP {
    pub id: String,
    pub username: String,
    pub email: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseUserInfoP {
    Error(APIErrorWrapper),
    Valid(ResultUserInfoP),
}

pub fn userinfop(api_address: String, token: String, user_id: String) -> ResponseUserInfoP {
    let client = reqwest::blocking::Client::new();

    let resp = client
        .get(&format!("{}/user/_/{}", api_address, user_id))
        .header("Authorization", token)
        .send()
        .unwrap();

    resp.json().unwrap()
}

D client-old-for-ref/src/api/user/types.rs => client-old-for-ref/src/api/user/types.rs +0 -17
@@ 1,17 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserPub {
    pub id: String,
    pub created_at: String,
    pub updated_at: String,
    pub username: String,
}

D client-old-for-ref/src/cfg/mod.rs => client-old-for-ref/src/cfg/mod.rs +0 -83
@@ 1,83 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;

use serde::{Deserialize, Serialize};

// Constants for file names
pub const FILE_CFG_SETTINGS: &str = "cfgsettings.json";
pub const FILE_CFG_USER: &str = "cfguser.json";
pub const FILE_CFG_DEV: &str = ".cfgdev.json";

/// Stores a directories::ProjectDirs for easy access
#[derive(Resource, Debug, Component, Clone)]
pub struct CfgDirs(pub directories::ProjectDirs);

/// Various settings that can be changed from the... Settings.
#[derive(Serialize, Deserialize, Resource, Debug, Component, PartialEq, Clone)]
pub struct CfgSettings {
    /// Master Volume
    pub volume_master: u32,
    /// Fullscreen
    pub fullscreen: bool,
    /// Resolution
    pub resolution: (f32, f32),
}

impl Default for CfgSettings {
    fn default() -> Self {
        CfgSettings {
            volume_master: 7,
            fullscreen: false,
            resolution: (1280., 720.),
        }
    }
}

/// User details and status
#[derive(Serialize, Deserialize, Resource, Debug, Component, PartialEq, Clone)]
pub struct CfgUser {
    /// User logged in status
    pub logged_in: bool,
    /// User Token
    pub user_token: String,
    /// User ID
    pub id: String,
    /// Username
    pub username: String,
    /// User email
    pub email: String,
}

impl Default for CfgUser {
    fn default() -> Self {
        CfgUser {
            logged_in: false,
            user_token: "".to_string(),
            id: "".to_string(),
            username: "".to_string(),
            email: "".to_string(),
        }
    }
}

/// Settings that the user has no access to, or can only access through developer settings
#[derive(Serialize, Deserialize, Resource, Debug, Component, PartialEq, Clone)]
pub struct CfgDev {
    /// API Server
    pub api_server: String,
}

impl Default for CfgDev {
    fn default() -> Self {
        CfgDev {
            api_server: "http://localhost:8080/api".to_string(),
        }
    }
}

D client-old-for-ref/src/constants.rs => client-old-for-ref/src/constants.rs +0 -11
@@ 1,11 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;

pub const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);

D client-old-for-ref/src/lua/mod.rs => client-old-for-ref/src/lua/mod.rs +0 -83
@@ 1,83 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use std::sync::Mutex;

use bevy_mod_scripting::prelude::*;

use bevy::prelude::*;
use bevy_console::PrintConsoleLine;

pub struct LuaPlugin;

impl Plugin for LuaPlugin {
    fn build(&self, app: &mut App) {
        app.add_startup_system(load_scripts)
            .add_script_host::<LuaScriptHost<()>, _>(CoreStage::PostUpdate)
            .add_script_handler_stage::<LuaScriptHost<()>, _, 0, 0>(CoreStage::PostUpdate)
            .add_api_provider::<LuaScriptHost<()>>(Box::new(LuaAPIProvider))
            .add_api_provider::<LuaScriptHost<()>>(Box::new(LuaBevyAPIProvider));
    }
}

fn load_scripts(asset_server: Res<AssetServer>, mut commands: Commands) {
    let script_paths = vec!["ui_play.lua"];
    let mut scripts = vec![];

    for path in script_paths {
        let handle = asset_server.load::<LuaFile, &str>(&format!("scripts/{}", path));
        scripts.push(Script::<LuaFile>::new(path.to_string(), handle));
    }

    commands
        .spawn(())
        .insert(ScriptCollection::<LuaFile> { scripts });
}

pub struct LuaAPIProvider;

impl APIProvider for LuaAPIProvider {
    type APITarget = Mutex<Lua>;
    type DocTarget = LuaDocFragment;
    type ScriptContext = Mutex<Lua>;

    fn attach_api(&mut self, api: &mut Self::APITarget) -> Result<(), ScriptError> {
        let api = api.get_mut().unwrap();

        api.globals()
            .set(
                "print_to_console",
                api.create_function(|ctx, msg: String| {
                    // retrieve the world pointer
                    let world = ctx.get_world()?;
                    let mut world = world.write();

                    let mut events: Mut<Events<PrintConsoleLine>> =
                        world.get_resource_mut().unwrap();
                    events.send(PrintConsoleLine::new(msg.into()));

                    Ok(())
                })
                .map_err(ScriptError::new_other)?,
            )
            .map_err(ScriptError::new_other)?;

        api.globals()
            .set(
                "print",
                api.create_function(|_ctx, msg: String| {
                    info!("{}", msg);
                    Ok(())
                })
                .map_err(ScriptError::new_other)?,
            )
            .map_err(ScriptError::new_other)?;

        Ok(())
    }
}

D client-old-for-ref/src/main.rs => client-old-for-ref/src/main.rs +0 -114
@@ 1,114 0,0 @@
/*
 * This file is part of laurelin_client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    prelude::*,
    window::{
        CompositeAlphaMode, Cursor, PresentMode, WindowLevel, WindowMode, WindowResizeConstraints,
        WindowResolution,
    },
};

//use bevy_mod_scripting::prelude::*;

mod api;
mod cfg;
mod constants;
//mod lua;
mod plugins;
mod runtime;
mod util;

/// Used to control the state of the game
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum GameState {
    Splash,
    Loading,
    MainMenu,
    Game,
}

fn main() {
    let mut app = App::new();

    app.add_plugins(DefaultPlugins.set(WindowPlugin {
        primary_window: Some(Window {
            cursor: Cursor::default(),
            present_mode: PresentMode::Fifo,
            mode: WindowMode::Windowed,
            position: WindowPosition::Centered(MonitorSelection::Primary),
            resolution: WindowResolution::new(1280., 720.),
            title: "Laurelin".to_string(),
            composite_alpha_mode: CompositeAlphaMode::Auto,
            resize_constraints: WindowResizeConstraints {
                min_width: 1280.,
                min_height: 720.,
                max_width: 3840.,
                max_height: 2160.,
            },
            resizable: false,
            decorations: true,
            transparent: false,
            focused: true,
            window_level: WindowLevel::Normal,
            canvas: None,
            fit_canvas_to_parent: false,
            prevent_default_event_handling: false,
            ime_enabled: false,
            ..Default::default()
        }),
        ..Default::default()
    }));

    //app.add_plugin(ScriptingPlugin).add_plugin(lua::LuaPlugin);

    app.insert_resource(cfg::CfgDirs(
        directories::ProjectDirs::from("com", "liljamo", "deckbuilder")
            .expect("failed to get project directories"),
    ));

    app.insert_resource(cfg::CfgSettings::default())
        .insert_resource(cfg::CfgUser::default())
        .insert_resource(cfg::CfgDev::default());

    app.add_startup_system(setup)
        .add_loopless_state(GameState::Splash);

    app.add_plugin(plugins::config::ConfigPlugin)
        .add_plugin(plugins::phases::splash::SplashPlugin)
        .add_plugin(plugins::phases::loading::LoadingPlugin)
        .add_plugin(plugins::menu::MenuPlugin);

    // Lastly, add in runtime data structs
    app.insert_resource(runtime::menu::RTDMenu::default())
        .insert_resource(runtime::game::RTDGame::default());

    app.run();
}

fn setup(mut commands: Commands) {
    // Spawn a camera
    commands.spawn(Camera3dBundle::default());
}

/// Utility function do despawn an entity and all its children
pub fn despawn_screen<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
    for entity in &to_despawn {
        commands.entity(entity).despawn_recursive();
    }
}

/// Utility function to remove UI nodes, which is usually just one node
/// NOTE: So, UI shows up as just nodes that don't have parents, so this
///       works mighty fine, at least for now.
///       MAY break something in the future, but that's why this note is here.
pub fn remove_ui(mut commands: Commands, nodes: Query<Entity, (With<Node>, Without<Parent>)>) {
    for node in &nodes {
        commands.entity(node).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/config/mod.rs => client-old-for-ref/src/plugins/config/mod.rs +0 -101
@@ 1,101 0,0 @@
/*
 * This file is part of laurelin_client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;

use crate::{cfg, util};

/// This plugin will handle config related tasks, like saving and loading
pub struct ConfigPlugin;

impl Plugin for ConfigPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<Events<LoadEvent>>()
            .init_resource::<Events<SaveEvent>>()
            .add_systems((handle_load_events, handle_save_events));
    }
}

pub enum LoadEventValue {
    Settings,
    User,
    Dev,
}

pub struct LoadEvent {
    pub value: LoadEventValue,
}

fn handle_load_events(
    mut events: EventReader<LoadEvent>,
    mut cfg_settings: ResMut<cfg::CfgSettings>,
    mut cfg_user: ResMut<cfg::CfgUser>,
    mut cfg_dev: ResMut<cfg::CfgDev>,
    cfg_dirs: ResMut<cfg::CfgDirs>,
) {
    for event in events.iter() {
        match &event.value {
            LoadEventValue::Settings => {
                *cfg_settings = util::sl::load(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_SETTINGS,
                );
            }
            LoadEventValue::User => {
                *cfg_user = util::sl::load(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_USER,
                );
            }
            LoadEventValue::Dev => {
                *cfg_dev = util::sl::load(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_DEV,
                );
            }
        }
    }
}

pub enum SaveEventValue {
    Settings(cfg::CfgSettings),
    User(cfg::CfgUser),
    Dev(cfg::CfgDev),
}

pub struct SaveEvent {
    pub value: SaveEventValue,
}

fn handle_save_events(mut events: EventReader<SaveEvent>, cfg_dirs: Res<cfg::CfgDirs>) {
    for event in events.iter() {
        match &event.value {
            SaveEventValue::Settings(value) => {
                util::sl::save(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_SETTINGS,
                    &value,
                );
            }
            SaveEventValue::User(value) => {
                util::sl::save(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_USER,
                    &value,
                );
            }
            SaveEventValue::Dev(value) => {
                util::sl::save(
                    cfg_dirs.0.data_local_dir().to_str().unwrap(),
                    cfg::FILE_CFG_DEV,
                    &value,
                );
            }
        }
    }
}

D client-old-for-ref/src/plugins/menu/mod.rs => client-old-for-ref/src/plugins/menu/mod.rs +0 -51
@@ 1,51 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;

use crate::{GameState, MainLoop};

pub mod ui;

pub struct MenuPlugin;

impl Plugin for MenuPlugin {
    fn build(&self, app: &mut App) {
        app.
            // Start with no menu. The menu is loaded when the GameState::MainMenu is entered.
            add_state::<MenuState>()
            .add_system(menu_setup.in_schedule(OnEnter(GameState::MainMenu)))
            .insert_resource(ui::connect::ConnectScreenData::default())
            .register_type::<ui::connect::ConnectScreenData>()
            .insert_resource(ui::play::PlayScreenData::default())
            .register_type::<ui::play::PlayScreenData>()
            .add_systems(
                (
                    ui::connect::ui.run_if(in_state(MenuState::Connect)),
                    ui::menu::ui.run_if(in_state(MenuState::Menu)),
                    ui::settings::ui.run_if(in_state(MenuState::Settings)),
                    ui::play::ui.run_if(in_state(MenuState::Play)),
                ).in_set(MainLoop)
            );
    }
}

/// Menu State
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
pub enum MenuState {
    #[default]
    None,
    Connect,
    Menu,
    Play,
    Settings,
}

fn menu_setup(mut commands: Commands) {
    commands.insert_resource(NextState(Some(MenuState::Connect)))
}

D client-old-for-ref/src/plugins/menu/ui/connect/mod.rs => client-old-for-ref/src/plugins/menu/ui/connect/mod.rs +0 -35
@@ 1,35 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    ecs::{reflect::ReflectResource, system::Resource},
    reflect::Reflect,
};

mod ui;
pub use ui::*;

#[derive(Default, Resource, Reflect)]
#[reflect(Resource)]
pub struct ConnectScreenData {
    pub state: ConnectState,
    pub username: String,
    pub email: String,
    pub password: String,
    pub password_confirm: String,
    pub error: String,
}

#[derive(Default, PartialEq, Reflect)]
pub enum ConnectState {
    #[default]
    Login,
    LoggingIn,
    Register,
    Registering,
}

D client-old-for-ref/src/plugins/menu/ui/connect/ui.rs => client-old-for-ref/src/plugins/menu/ui/connect/ui.rs +0 -120
@@ 1,120 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::ecs::system::{Res, ResMut};
use bevy_egui::{egui, EguiContexts};
use laurelin_shared::server::messages::Auth;
use naia_bevy_client::{transport::webrtc, Client};

use crate::{
    cfg::CfgDev,
    util::egui::{menuwindow, password},
};

use super::{ConnectScreenData, ConnectState};

pub fn ui(
    mut egui_contexts: EguiContexts,
    mut data: ResMut<ConnectScreenData>,
    mut client: Client,
    cfg_dev: Res<CfgDev>,
) {
    menuwindow(
        egui_contexts.ctx_mut(),
        "Connect",
        &egui::Vec2::new(400., 600.),
        |ui| match data.state {
            ConnectState::Login => {
                ui.horizontal(|ui| {
                    ui.label("Email:");
                    ui.text_edit_singleline(&mut data.email)
                });
                ui.horizontal(|ui| {
                    ui.label("Password:");
                    ui.add(password(&mut data.password));
                });

                if !data.error.is_empty() {
                    ui.label(egui::RichText::new(&data.error).color(egui::Color32::RED));
                }

                ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
                    if ui.button("Login").clicked() {
                        data.error.clear();
                        data.state = ConnectState::LoggingIn;

                        client.auth(Auth::new(None, &data.email, &data.password));
                        connect(&mut client, &cfg_dev);
                    }
                });

                ui.vertical_centered(|ui| {
                    ui.label("I don't have an account:");
                    if ui.link("Register").clicked() {
                        *data = ConnectScreenData::default();
                        data.state = ConnectState::Register;
                    }
                });
            }
            ConnectState::Register => {
                ui.horizontal(|ui| {
                    ui.label("Username:");
                    ui.text_edit_singleline(&mut data.username)
                });
                ui.horizontal(|ui| {
                    ui.label("Email:");
                    ui.text_edit_singleline(&mut data.email)
                });
                ui.horizontal(|ui| {
                    ui.label("Password:");
                    ui.add(password(&mut data.password));
                });
                ui.horizontal(|ui| {
                    ui.label("Confirm password:");
                    ui.add(password(&mut data.password_confirm));
                });

                if !data.error.is_empty() {
                    ui.label(egui::RichText::new(&data.error).color(egui::Color32::RED));
                }

                ui.add_enabled_ui(data.password == data.password_confirm, |ui| {
                    ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {
                        if ui.button("Register").clicked() {
                            data.error.clear();
                            data.state = ConnectState::Registering;

                            client.auth(Auth::new(
                                Some(data.username.clone()),
                                &data.email,
                                &data.password,
                            ));
                            connect(&mut client, &cfg_dev);
                        }
                    });
                });

                ui.vertical_centered(|ui| {
                    ui.label("I have an account:");
                    if ui.link("Login").clicked() {
                        *data = ConnectScreenData::default();
                        data.state = ConnectState::Login;
                    }
                });
            }
            _ => {
                ui.spinner();
            }
        },
    );
}

fn connect(client: &mut Client, cfg_dev: &CfgDev) {
    let socket = webrtc::Socket::new(&cfg_dev.server_address, client.socket_config());
    client.connect(socket);
}

D client-old-for-ref/src/plugins/menu/ui/menu.rs => client-old-for-ref/src/plugins/menu/ui/menu.rs +0 -43
@@ 1,43 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};

use crate::{plugins::menu::MenuState, util::egui::menuwindow, GracefulExit};

pub fn ui(
    mut commands: Commands,
    mut egui_contexts: EguiContexts,
    mut exit_events: EventWriter<GracefulExit>,
) {
    menuwindow(
        egui_contexts.ctx_mut(),
        "Laurelin",
        &egui::Vec2::new(400., 600.),
        |ui| {
            ui.vertical_centered(|ui| {
                if ui.button("Play").clicked() {
                    commands.insert_resource(NextState(Some(MenuState::Play)));
                }

                if ui.button("Account").clicked() {
                    //
                }

                if ui.button("Settings").clicked() {
                    commands.insert_resource(NextState(Some(MenuState::Settings)));
                }

                if ui.button("Quit").clicked() {
                    exit_events.send(GracefulExit);
                }
            });
        },
    );
}

D client-old-for-ref/src/plugins/menu/ui/mod.rs => client-old-for-ref/src/plugins/menu/ui/mod.rs +0 -13
@@ 1,13 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

pub mod menu;

pub mod connect;
pub mod play;
pub mod settings;

D client-old-for-ref/src/plugins/menu/ui/play/mod.rs => client-old-for-ref/src/plugins/menu/ui/play/mod.rs +0 -60
@@ 1,60 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    ecs::{reflect::ReflectResource, system::Resource},
    reflect::Reflect,
};

mod ui;
use laurelin_shared::types::game::GamePub;
pub use ui::*;

#[derive(Default, Resource, Reflect)]
#[reflect(Resource)]
pub struct PlayScreenData {
    pub state: PlayScreenState,
    pub browse_state: PlayScreenBrowseState,

    pub waiting_for_create_game: bool,
    pub waiting_for_all_forming: bool,
    pub waiting_for_my_games: bool,

    #[reflect(ignore)]
    pub all_forming: Vec<GamePub>,
    #[reflect(ignore)]
    pub my_games: Vec<GamePub>,
}

#[derive(Default, PartialEq, Clone, Reflect)]
pub enum PlayScreenState {
    #[default]
    Main,
    CreateGame,
    InLobbyHost,
    InLobbyGuest,
}

impl PlayScreenState {
    pub fn display(&self) -> &str {
        match self {
            PlayScreenState::Main => "Play",
            PlayScreenState::CreateGame => "Create",
            PlayScreenState::InLobbyHost => "Lobby (Host)",
            PlayScreenState::InLobbyGuest => "Lobby (Guest)",
        }
    }
}

#[derive(Default, PartialEq, Reflect)]
pub enum PlayScreenBrowseState {
    #[default]
    Forming,
    InProgress,
    Finished,
}

D client-old-for-ref/src/plugins/menu/ui/play/ui.rs => client-old-for-ref/src/plugins/menu/ui/play/ui.rs +0 -402
@@ 1,402 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};
use laurelin_shared::types::{
    game::{GamePub, GAMESTATE_FINISHED, GAMESTATE_FORMING, GAMESTATE_INPROGRESS},
    user::UserPub,
};

use crate::{
    cfg::CfgUser,
    plugins::{
        menu::MenuState,
        networking::send::game::{GameAllFormingEvent, GameCreateEvent, GameMyGamesEvent},
    },
    util::egui::menuwindow,
    Global,
};

use super::{PlayScreenBrowseState, PlayScreenData, PlayScreenState};

pub fn ui(
    mut commands: Commands,
    mut egui_contexts: EguiContexts,
    mut data: ResMut<PlayScreenData>,
    cfg_user: Res<CfgUser>,
    mut global: ResMut<Global>,
    mut creategame_ev_w: EventWriter<GameCreateEvent>,
    mut allforming_ev_w: EventWriter<GameAllFormingEvent>,
    mut mygames_ev_w: EventWriter<GameMyGamesEvent>,
) {
    menuwindow(
        egui_contexts.ctx_mut(),
        data.state.clone().display(),
        &egui::Vec2::new(800., 600.),
        |ui| match data.state {
            PlayScreenState::Main => {
                egui::SidePanel::left("play_side_panel")
                    .resizable(false)
                    .show_inside(ui, |ui| {
                        ui.vertical_centered(|ui| {
                            if ui.button("Create").clicked() {
                                data.state = PlayScreenState::CreateGame;
                            }

                            if ui.button("Back").clicked() {
                                commands.insert_resource(NextState(Some(MenuState::Menu)));
                            }
                        });
                    });

                egui::TopBottomPanel::top("play_top_panel")
                    .resizable(false)
                    .show_inside(ui, |ui| {
                        ui.horizontal(|ui| {
                            ui.label(egui::RichText::new("Browse: ").size(24.));

                            ui.selectable_value(
                                &mut data.browse_state,
                                PlayScreenBrowseState::Forming,
                                "Forming",
                            );
                            ui.selectable_value(
                                &mut data.browse_state,
                                PlayScreenBrowseState::InProgress,
                                "In Progress",
                            );
                            ui.selectable_value(
                                &mut data.browse_state,
                                PlayScreenBrowseState::Finished,
                                "Finished",
                            );

                            ui.with_layout(
                                egui::Layout::right_to_left(egui::Align::Center),
                                |ui| {
                                    if ui.button("Refresh").clicked() {
                                        match data.browse_state {
                                            PlayScreenBrowseState::Forming => {
                                                if !data.waiting_for_all_forming {
                                                    allforming_ev_w.send(GameAllFormingEvent);
                                                }
                                            }
                                            PlayScreenBrowseState::InProgress => {
                                                if !data.waiting_for_my_games {
                                                    mygames_ev_w.send(GameMyGamesEvent);
                                                }
                                            }
                                            PlayScreenBrowseState::Finished => {
                                                if !data.waiting_for_my_games {
                                                    mygames_ev_w.send(GameMyGamesEvent);
                                                }
                                            }
                                        }
                                    }
                                },
                            );
                        });
                    });

                ui.vertical_centered(|ui| {
                    egui::ScrollArea::vertical().show(ui, |ui| match data.browse_state {
                        PlayScreenBrowseState::Forming => {
                            browse_forming(ui, &mut data, &cfg_user, &mut global);
                        }
                        PlayScreenBrowseState::InProgress => {
                            browse_inprogress(ui, &mut data, &mut global, &cfg_user);
                        }
                        PlayScreenBrowseState::Finished => {
                            browse_finished(ui, &mut data, &global, &cfg_user);
                        }
                    });
                });
            }
            PlayScreenState::CreateGame => {
                ui.vertical_centered(|ui| {
                    ui.add_enabled_ui(!data.waiting_for_create_game, |ui| {
                        if ui.button("Confirm").clicked() {
                            data.waiting_for_create_game = true;
                            creategame_ev_w.send(GameCreateEvent);
                        }

                        if ui.button("Cancel").clicked() {
                            data.state = PlayScreenState::Main;
                        }
                    });
                });
            }
            PlayScreenState::InLobbyHost => {
                ui.label(egui::RichText::new(format!(
                    "Host: {}",
                    global
                        .users_cache
                        .get(&global.cur_game.as_ref().unwrap().host_id)
                        .unwrap_or(&UserPub {
                            id: "".to_string(),
                            created_at: "".to_string(),
                            username: "N/A".to_string()
                        })
                        .username
                )));

                ui.label(egui::RichText::new(format!(
                    "Guest: {}",
                    global
                        .users_cache
                        .get(
                            global
                                .cur_game
                                .as_ref()
                                .unwrap()
                                .guest_id
                                .as_ref()
                                .unwrap_or(&"".to_string())
                        )
                        .unwrap_or(&UserPub {
                            id: "".to_string(),
                            created_at: "".to_string(),
                            username: "N/A".to_string()
                        })
                        .username
                )));

                if ui.button("Start").clicked() {
                    //startgame_ev_w.send(StartGameEvent);
                }

                if ui.button("Back").clicked() {
                    data.state = PlayScreenState::Main;
                }
            }
            PlayScreenState::InLobbyGuest => {
                ui.label(egui::RichText::new(format!(
                    "Host: {}",
                    global
                        .users_cache
                        .get(&global.cur_game.as_ref().unwrap().host_id)
                        .unwrap_or(&UserPub {
                            id: "".to_string(),
                            created_at: "".to_string(),
                            username: "N/A".to_string()
                        })
                        .username
                )));

                ui.label(egui::RichText::new(format!(
                    "Guest: {}",
                    global
                        .users_cache
                        .get(
                            global
                                .cur_game
                                .as_ref()
                                .unwrap()
                                .guest_id
                                .as_ref()
                                .unwrap_or(&"".to_string())
                        )
                        .unwrap_or(&UserPub {
                            id: "".to_string(),
                            created_at: "".to_string(),
                            username: "N/A".to_string()
                        })
                        .username
                )));

                if ui.button("Back").clicked() {
                    data.state = PlayScreenState::Main;
                }
            }
        },
    );
}

fn browse_forming(
    ui: &mut egui::Ui,
    data: &mut PlayScreenData,
    cfg_user: &CfgUser,
    global: &mut Global,
) {
    if data.waiting_for_all_forming {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
        return;
    }

    let mut all_forming: Vec<&GamePub> = global
        .games_cache
        .values()
        .filter(|g| g.state == GAMESTATE_FORMING)
        .collect();

    // sort by created_at, and reverse so it's the right way round
    all_forming.sort_by_key(|&g| &g.created_at);
    all_forming = all_forming.into_iter().rev().collect();

    if all_forming.is_empty() {
        ui.label("No forming games found.");
    } else {
        for game in all_forming {
            egui::Frame::none()
                .fill(egui::Color32::BLACK)
                .rounding(4.)
                .outer_margin(4.)
                .inner_margin(4.)
                .show(ui, |ui| {
                    ui.horizontal(|ui| {
                        ui.label(format!(
                            "Host: {}",
                            global
                                .users_cache
                                .get(&game.host_id)
                                .unwrap_or(&UserPub {
                                    id: "".to_string(),
                                    created_at: "".to_string(),
                                    username: "".to_string()
                                })
                                .username
                        ));
                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                            if game.guest_id.clone().unwrap_or_default() == cfg_user.id {
                                ui.add_enabled(false, egui::Button::new("Joined"));
                                if ui.button("Inspect").clicked() {
                                    global.cur_game = Some(game.clone());
                                    data.state = PlayScreenState::InLobbyGuest;
                                }
                            } else if game.host_id == cfg_user.id {
                                ui.add_enabled(false, egui::Button::new("Host"));
                                if ui.button("Inspect").clicked() {
                                    global.cur_game = Some(game.clone());
                                    data.state = PlayScreenState::InLobbyHost;
                                }
                            } else if ui.button("Join").clicked() {
                                //data.cur_game = Some(game.clone());
                                //joingame_ev_w.send(JoinGameEvent);
                            }
                        });
                    });
                });
        }
    }
}

fn browse_inprogress(
    ui: &mut egui::Ui,
    data: &mut PlayScreenData,
    global: &mut Global,
    cfg_user: &CfgUser,
) {
    if data.waiting_for_my_games {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
        return;
    }

    let my_games_inprogress: Vec<&GamePub> = global
        .games_cache
        .values()
        .filter(|g| {
            g.state == GAMESTATE_INPROGRESS
                && (g.host_id == cfg_user.id
                    || g.guest_id.clone().unwrap_or("".to_string()) == cfg_user.id)
        })
        .collect();

    if my_games_inprogress.is_empty() {
        ui.label("No games found.");
    } else {
        for game in my_games_inprogress {
            egui::Frame::none()
                .fill(egui::Color32::BLACK)
                .rounding(4.)
                .outer_margin(4.)
                .inner_margin(4.)
                .show(ui, |ui| {
                    ui.horizontal(|ui| {
                        ui.label(format!(
                            "Host: {}",
                            global
                                .users_cache
                                .get(&game.host_id)
                                .unwrap_or(&UserPub {
                                    id: "".to_string(),
                                    created_at: "".to_string(),
                                    username: "N/A".to_string()
                                })
                                .username
                        ));
                        ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                            if ui.button("Resume").clicked() {
                                global.cur_game = Some(game.clone());
                                //resumegame_ev_w.send(ResumeGameEvent);
                            }
                        });
                    });
                });
        }
    }
}

fn browse_finished(
    ui: &mut egui::Ui,
    data: &mut PlayScreenData,
    global: &Global,
    cfg_user: &CfgUser,
) {
    if data.waiting_for_my_games {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
        return;
    }

    let my_games_finished: Vec<&GamePub> = global
        .games_cache
        .values()
        .filter(|g| {
            g.state == GAMESTATE_FINISHED
                && (g.host_id == cfg_user.id
                    || g.guest_id.clone().unwrap_or("".to_string()) == cfg_user.id)
        })
        .collect();

    if my_games_finished.is_empty() {
        ui.label("No games found.");
    } else {
        for game in my_games_finished {
            egui::Frame::none()
                .fill(egui::Color32::BLACK)
                .rounding(4.)
                .outer_margin(4.)
                .inner_margin(4.)
                .show(ui, |ui| {
                    ui.horizontal(|ui| {
                        ui.label(format!(
                            "Host: {}",
                            global
                                .users_cache
                                .get(&game.host_id)
                                .unwrap_or(&UserPub {
                                    id: "".to_string(),
                                    created_at: "".to_string(),
                                    username: "".to_string()
                                })
                                .username
                        ));
                    });
                });
        }
    }
}

D client-old-for-ref/src/plugins/menu/ui/settings/mod.rs => client-old-for-ref/src/plugins/menu/ui/settings/mod.rs +0 -10
@@ 1,10 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

mod ui;
pub use ui::*;

D client-old-for-ref/src/plugins/menu/ui/settings/ui.rs => client-old-for-ref/src/plugins/menu/ui/settings/ui.rs +0 -25
@@ 1,25 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts};

use crate::{plugins::menu::MenuState, util::egui::menuwindow};

pub fn ui(mut commands: Commands, mut egui_contexts: EguiContexts) {
    menuwindow(
        egui_contexts.ctx_mut(),
        "Settings",
        &egui::Vec2::new(400., 600.),
        |ui| {
            if ui.button("Back").clicked() {
                commands.insert_resource(NextState(Some(MenuState::Menu)));
            }
        },
    );
}

D client-old-for-ref/src/plugins/menu_old/accountlogin/mod.rs => client-old-for-ref/src/plugins/menu_old/accountlogin/mod.rs +0 -156
@@ 1,156 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    prelude::*,
    tasks::{AsyncComputeTaskPool, Task},
};
use bevy_console::PrintConsoleLine;
use iyes_loopless::prelude::*;

use futures_lite::future;

use crate::{
    api::{
        self,
        user::{ResponseToken, ResponseUserInfoP},
    },
    cfg::{CfgDev, CfgUser},
    plugins::config::{SaveEvent, SaveEventValue},
};

use super::MenuState;

pub mod ui;

pub struct AccountLoginPlugin;

impl Plugin for AccountLoginPlugin {
    fn build(&self, app: &mut App) {
        app.add_loopless_state(LoginState::None)
            // UI system
            .insert_resource(ui::InputsUserLogin::new())
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(LoginState::Input)
                    .with_system(ui::account_login_ui)
                    .into(),
            )
            // Login system, as in calling the API
            .add_enter_system(LoginState::LoggingIn, start_login_call)
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(LoginState::LoggingIn)
                    .with_system(handle_login_call)
                    .into(),
            );
    }
}

/// Login State
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum LoginState {
    None,
    Input,
    LoggingIn,
}

struct LoginCallResponse {
    token: ResponseToken,
    user_infop: Option<ResponseUserInfoP>,
}

#[derive(Component)]
struct LoginCall(Task<LoginCallResponse>);

fn start_login_call(
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    inputs: Res<ui::InputsUserLogin>,
) {
    let api_address = cfg_dev.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.clone(), i.email, i.password);

        let mut user_infop_response = None;
        match &token_response {
            ResponseToken::Valid(res) => {
                user_infop_response = Some(api::user::userinfop(
                    api_address,
                    res.token.clone(),
                    res.id.clone(),
                ));
            }
            ResponseToken::Error(_error) => {}
        }

        LoginCallResponse {
            token: token_response,
            user_infop: user_infop_response,
        }
    });
    commands.spawn(LoginCall(task));
}

fn handle_login_call(
    mut commands: Commands,
    mut login_call_tasks: Query<(Entity, &mut LoginCall)>,
    mut inputs: ResMut<ui::InputsUserLogin>,
    mut cfg_user: ResMut<CfgUser>,
    mut save_event_writer: EventWriter<SaveEvent>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    let (entity, mut task) = login_call_tasks.single_mut();
    if let Some(login_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        match login_call_response.token {
            ResponseToken::Valid(res) => {
                console.send(PrintConsoleLine::new(
                    format!("Logged in with: {}", inputs.email).into(),
                ));

                match login_call_response.user_infop.unwrap() {
                    ResponseUserInfoP::Valid(infop) => {
                        *cfg_user = CfgUser {
                            logged_in: true,
                            user_token: res.token,
                            id: res.id,
                            username: infop.username,
                            email: infop.email,
                        };

                        save_event_writer.send(SaveEvent {
                            value: SaveEventValue::User(cfg_user.into_inner().clone()),
                        });

                        commands.insert_resource(NextState(LoginState::None));
                        commands.insert_resource(NextState(MenuState::AccountLoggedIn));
                    }
                    ResponseUserInfoP::Error(error) => {
                        console.send(PrintConsoleLine::new(format!(
                            "Something went wrong with getting the user information after logging in, got error: '{}'", error
                        ).into()));

                        commands.insert_resource(NextState(LoginState::None));
                        commands.insert_resource(NextState(MenuState::AccountLoggedIn));
                    }
                }
            }
            ResponseToken::Error(error) => {
                inputs.error = error.error.description;
                commands.insert_resource(NextState(LoginState::Input));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<LoginCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/accountlogin/ui.rs => client-old-for-ref/src/plugins/menu_old/accountlogin/ui.rs +0 -75
@@ 1,75 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::{egui, EguiContext};
use iyes_loopless::prelude::*;

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 commands: Commands,
    mut egui_context: ResMut<EguiContext>,
    mut inputs: ResMut<InputsUserLogin>,
) {
    egui::Window::new("Login")
        .collapsible(false)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .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() {
                    commands.insert_resource(NextState(LoginState::None));
                    commands.insert_resource(NextState(MenuState::AccountLoggedOut));
                }
                if ui.button("Login").clicked() {
                    commands.insert_resource(NextState(LoginState::LoggingIn));
                    // Reset error field
                    inputs.error = "".to_string();
                }
            })
        });
}

D client-old-for-ref/src/plugins/menu_old/accountregister/mod.rs => client-old-for-ref/src/plugins/menu_old/accountregister/mod.rs +0 -155
@@ 1,155 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    prelude::*,
    tasks::{AsyncComputeTaskPool, Task},
};
use bevy_console::PrintConsoleLine;
use iyes_loopless::prelude::*;

use futures_lite::future;

use crate::{
    api::{
        self,
        user::{ResponseRegister, ResponseToken},
    },
    cfg::{CfgDev, CfgUser},
    plugins::config::{SaveEvent, SaveEventValue},
};

use super::MenuState;

pub mod ui;

pub struct AccountRegisterPlugin;

impl Plugin for AccountRegisterPlugin {
    fn build(&self, app: &mut App) {
        app.add_loopless_state(RegisterState::None)
            // UI system
            .insert_resource(ui::InputsUserRegister::new())
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(RegisterState::Input)
                    .with_system(ui::account_register_ui)
                    .into(),
            )
            // Register system, as in calling the API
            .add_enter_system(RegisterState::Registering, start_register_call)
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(RegisterState::Registering)
                    .with_system(handle_register_call)
                    .into(),
            );
    }
}

/// 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<RegisterCallResponse>);

fn start_register_call(
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    inputs: Res<ui::InputsUserRegister>,
) {
    let api_address = cfg_dev.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<ui::InputsUserRegister>,
    mut cfg_user: ResMut<CfgUser>,
    mut save_event_writer: EventWriter<SaveEvent>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    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,).into(),
                ));

                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: inputs.email.clone(),
                        };

                        save_event_writer.send(SaveEvent {
                            value: SaveEventValue::User(cfg_user.into_inner().clone()),
                        });

                        commands.insert_resource(NextState(RegisterState::None));
                        commands.insert_resource(NextState(MenuState::AccountLoggedIn));
                    }
                    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
                        ).into()));

                        commands.insert_resource(NextState(RegisterState::None));
                        commands.insert_resource(NextState(MenuState::AccountLoggedOut));
                    }
                }
            }
            ResponseRegister::Error(error) => {
                inputs.error = error.error.description;
                commands.insert_resource(NextState(RegisterState::Input));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<RegisterCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/accountregister/ui.rs => client-old-for-ref/src/plugins/menu_old/accountregister/ui.rs +0 -102
@@ 1,102 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::{egui, EguiContext};
use iyes_loopless::prelude::*;

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 commands: Commands,
    mut egui_context: ResMut<EguiContext>,
    mut inputs: ResMut<InputsUserRegister>,
) {
    egui::Window::new("Register")
        .collapsible(false)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .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() {
                    commands.insert_resource(NextState(RegisterState::None));
                    commands.insert_resource(NextState(MenuState::AccountLoggedOut));
                }

                ui.add_enabled_ui(inputs.passwords_match, |ui| {
                    if ui.button("Register").clicked() {
                        commands.insert_resource(NextState(RegisterState::Registering));
                        // Reset error field
                        inputs.error = "".to_string();
                    }
                });
            })
        });
}

D client-old-for-ref/src/plugins/menu_old/accountscreenloggedin.rs => client-old-for-ref/src/plugins/menu_old/accountscreenloggedin.rs +0 -69
@@ 1,69 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use iyes_loopless::prelude::*;

use belly::prelude::*;

use crate::{
    cfg::CfgUser,
    plugins::config::{SaveEvent, SaveEventValue},
};

use super::MenuState;

pub(super) struct LogoutEvent;

pub(super) fn account_loggedin_setup(mut commands: Commands, cfg_user: Res<CfgUser>) {
    let username = cfg_user.username.clone();

    commands.add(eml! {
        <body>
            <div c:menu>
                <span c:menutitle>
                    "Account"
                </span>
                <span>
                    "Logged in as:"
                </span>
                <span>
                    { username }
                </span>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.send_event(LogoutEvent)
                })>
                    "Logout"
                </button>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::Main))
                })>
                    "Back"
                </button>
            </div>
        </body>
    });
}

pub(super) fn handle_logout_event(
    mut events: EventReader<LogoutEvent>,
    mut commands: Commands,
    mut cfg_user: ResMut<CfgUser>,
    mut save_event_writer: EventWriter<SaveEvent>,
) {
    for _event in events.iter() {
        // Reset CfgUser to default
        *cfg_user = CfgUser::default();

        save_event_writer.send(SaveEvent {
            value: SaveEventValue::User(cfg_user.clone()),
        });

        commands.insert_resource(NextState(MenuState::AccountLoggedOut));
    }
}

D client-old-for-ref/src/plugins/menu_old/accountscreenloggedout.rs => client-old-for-ref/src/plugins/menu_old/accountscreenloggedout.rs +0 -43
@@ 1,43 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use iyes_loopless::prelude::*;

use belly::prelude::*;

use super::{accountlogin::LoginState, accountregister::RegisterState, MenuState};

pub(super) fn account_loggedout_setup(mut commands: Commands) {
    commands.add(eml! {
        <body>
            <div c:menu>
                <span c:menutitle>
                    "Account"
                </span>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::AccountLogin));
                    ctx.commands().insert_resource(NextState(LoginState::Input));
                })>
                    "Login"
                </button>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::AccountRegister));
                    ctx.commands().insert_resource(NextState(RegisterState::Input));
                })>
                    "Register"
                </button>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::Main))
                })>
                    "Back"
                </button>
            </div>
        </body>
    });
}

D client-old-for-ref/src/plugins/menu_old/mainmenuscreen.rs => client-old-for-ref/src/plugins/menu_old/mainmenuscreen.rs +0 -81
@@ 1,81 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{app::AppExit, prelude::*};
use iyes_loopless::prelude::*;

use belly::prelude::*;

use crate::cfg::CfgUser;

use super::MenuState;

pub(super) struct ToAccountEvent;
pub(super) struct ExitEvent;

pub(super) fn main_menu_setup(mut commands: Commands, cfg_user: Res<CfgUser>) {
    let logged_in = cfg_user.logged_in.clone();

    // TODO: Change the title to a fancy image logo thingy
    commands.add(eml! {
        <body>
            <div c:menu>
                <span c:mainmenutitle>
                    "Deck Builder"
                </span>
                <button c:menubutton s:width="250px" on:press=connect!(|ctx| {
                    // FIXME: I guess hiding the button would be better, but this is something for
                    // now.
                    if logged_in {
                        ctx.commands().insert_resource(NextState(MenuState::Play))
                    }
                })>
                    "Play"
                </button>
                <button c:menubutton s:width="250px" on:press=connect!(|ctx| {
                    ctx.send_event(ToAccountEvent)
                })>
                    "Account"
                </button>
                <button c:menubutton s:width="250px" on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::Settings))
                })>
                    "Settings"
                </button>
                <button c:menubutton s:width="250px" on:press=connect!(|ctx| {
                    ctx.send_event(ExitEvent)
                })>
                    "Exit"
                </button>
            </div>
        </body>
    });
}

pub(super) fn handle_to_account_event(
    mut events: EventReader<ToAccountEvent>,
    mut commands: Commands,
    cfg_user: Res<CfgUser>,
) {
    for _event in events.iter() {
        if cfg_user.logged_in {
            commands.insert_resource(NextState(MenuState::AccountLoggedIn))
        } else {
            commands.insert_resource(NextState(MenuState::AccountLoggedOut))
        }
    }
}

pub(super) fn handle_exit_event(
    mut events: EventReader<ExitEvent>,
    mut app_exit_event_writer: EventWriter<AppExit>,
) {
    for _event in events.iter() {
        app_exit_event_writer.send(AppExit);
    }
}

D client-old-for-ref/src/plugins/menu_old/mod.rs => client-old-for-ref/src/plugins/menu_old/mod.rs +0 -91
@@ 1,91 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use iyes_loopless::prelude::*;

use crate::{remove_ui, GameState};

mod mainmenuscreen;
use mainmenuscreen::*;

mod settingsscreen;
use settingsscreen::*;

mod accountscreenloggedout;
use accountscreenloggedout::*;

mod accountscreenloggedin;
use accountscreenloggedin::*;

mod accountlogin;
mod accountregister;

mod play;

pub struct MenuPlugin;

impl Plugin for MenuPlugin {
    fn build(&self, app: &mut App) {
        app.
            // Start with no menu. The menu is loaded when the GameState::MainMenu is entered.
            add_loopless_state(MenuState::None)
            .add_enter_system(GameState::MainMenu, menu_setup)
            // Systems for main menu screen
            .add_enter_system(MenuState::Main, remove_ui.label("msm_enter_first"))
            .add_enter_system(MenuState::Main, main_menu_setup.before("msm_enter_first"))
            .add_exit_system(MenuState::Main, remove_ui)
            .add_event::<ToAccountEvent>()
            .add_event::<ExitEvent>()
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(MenuState::Main)
                    .with_system(handle_to_account_event.run_on_event::<ToAccountEvent>())
                    .with_system(handle_exit_event.run_on_event::<ExitEvent>())
                    .into()
            )
            // Systems for the settings screen
            .add_enter_system(MenuState::Settings, settings_setup)
            .add_exit_system(MenuState::Settings, remove_ui)
            // Systems for account loggedout screen
            .add_enter_system(MenuState::AccountLoggedOut, account_loggedout_setup)
            .add_exit_system(MenuState::AccountLoggedOut, remove_ui)
            // Systems for account loggedin screen
            .add_enter_system(MenuState::AccountLoggedIn, account_loggedin_setup)
            .add_exit_system(MenuState::AccountLoggedIn, remove_ui)
            .add_event::<LogoutEvent>()
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(MenuState::AccountLoggedIn)
                    .with_system(handle_logout_event.run_on_event::<LogoutEvent>())
                    .into()
            );

        app.add_plugin(accountregister::AccountRegisterPlugin)
            .add_plugin(accountlogin::AccountLoginPlugin);

        app.add_plugin(play::PlayMenuPlugin);
    }
}

/// Menu State
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum MenuState {
    None,
    Main,
    Play,
    Settings,
    AccountLoggedIn,
    AccountLoggedOut,
    AccountLogin,
    AccountRegister,
}

fn menu_setup(mut commands: Commands) {
    commands.insert_resource(NextState(MenuState::Main))
}

D client-old-for-ref/src/plugins/menu_old/play/allformingcall.rs => client-old-for-ref/src/plugins/menu_old/play/allformingcall.rs +0 -82
@@ 1,82 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * 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, game::ResponseAllForming},
    cfg::{CfgDev, CfgUser},
    runtime::menu::RTDMenu,
};

use super::AllFormingEvent;

struct AllFormingCallResponse {
    all_forming: ResponseAllForming,
}

#[derive(Component)]
pub(super) struct AllFormingCall(Task<AllFormingCallResponse>);

pub(super) fn start(
    mut events: EventReader<AllFormingEvent>,
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    cfg_user: Res<CfgUser>,
    mut rtdmenu: ResMut<RTDMenu>,
) {
    for _event in events.iter() {
        let api_address = cfg_dev.api_server.clone();
        let token = cfg_user.user_token.clone();

        let thread_pool = AsyncComputeTaskPool::get();
        let task = thread_pool.spawn(async move {
            let all_forming_response = api::game::all_forming(api_address, token);

            AllFormingCallResponse {
                all_forming: all_forming_response,
            }
        });
        commands.spawn(AllFormingCall(task));
        rtdmenu.waiting_for_all_forming_call = true;
    }
}

pub(super) fn handle(
    mut commands: Commands,
    mut all_forming_call_tasks: Query<(Entity, &mut AllFormingCall)>,
    mut rtdmenu: ResMut<RTDMenu>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    if all_forming_call_tasks.is_empty() {
        return;
    }

    let (entity, mut task) = all_forming_call_tasks.single_mut();
    if let Some(all_forming_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        rtdmenu.waiting_for_all_forming_call = false;
        match all_forming_call_response.all_forming {
            ResponseAllForming::Valid(res) => rtdmenu.all_forming_games = res,
            ResponseAllForming::Error(error) => {
                console.send(PrintConsoleLine::new(
                    format!("Fetching all forming games failed, got error: '{}'", error).into(),
                ));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<AllFormingCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/play/creategamecall.rs => client-old-for-ref/src/plugins/menu_old/play/creategamecall.rs +0 -89
@@ 1,89 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * 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, game::ResponseCreateGame},
    cfg::{CfgDev, CfgUser},
    runtime::menu::{PlayMenuUIState, RTDMenu},
};

use super::CreateGameEvent;

struct CreateGameCallResponse {
    game: ResponseCreateGame,
}

#[derive(Component)]
pub(super) struct CreateGameCall(Task<CreateGameCallResponse>);

pub(super) fn start(
    mut events: EventReader<CreateGameEvent>,
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    cfg_user: Res<CfgUser>,
    mut rtdmenu: ResMut<RTDMenu>,
) {
    for _event in events.iter() {
        let api_address = cfg_dev.api_server.clone();
        let token = cfg_user.user_token.clone();

        let thread_pool = AsyncComputeTaskPool::get();
        let task = thread_pool.spawn(async move {
            let create_game_response = api::game::create(api_address, token);

            CreateGameCallResponse {
                game: create_game_response,
            }
        });
        commands.spawn(CreateGameCall(task));
        rtdmenu.waiting_for_create_game_call = true;
    }
}

pub(super) fn handle(
    mut commands: Commands,
    mut create_game_call_tasks: Query<(Entity, &mut CreateGameCall)>,
    mut rtdmenu: ResMut<RTDMenu>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    if create_game_call_tasks.is_empty() {
        return;
    }

    let (entity, mut task) = create_game_call_tasks.single_mut();
    if let Some(create_game_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        rtdmenu.waiting_for_create_game_call = false;
        match create_game_call_response.game {
            ResponseCreateGame::Valid(res) => {
                rtdmenu.cur_game = Some(res.clone());
                rtdmenu.play_menu_ui_state = PlayMenuUIState::InLobbyHost;

                console.send(PrintConsoleLine::new(
                    format!("Created game with id: '{}'", res.id).into(),
                ));
            }
            ResponseCreateGame::Error(error) => {
                console.send(PrintConsoleLine::new(
                    format!("Game creation failed, got error: '{}'", error).into(),
                ));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<CreateGameCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/play/joingamecall.rs => client-old-for-ref/src/plugins/menu_old/play/joingamecall.rs +0 -93
@@ 1,93 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * 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, game::ResponseJoinGame},
    cfg::{CfgDev, CfgUser},
    runtime::menu::{PlayMenuUIState, RTDMenu},
};

use super::JoinGameEvent;

struct JoinGameCallResponse {
    res: ResponseJoinGame,
}

#[derive(Component)]
pub(super) struct JoinGameCall(Task<JoinGameCallResponse>);

pub(super) fn start(
    mut events: EventReader<JoinGameEvent>,
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    cfg_user: Res<CfgUser>,
    mut rtdmenu: ResMut<RTDMenu>,
) {
    for _event in events.iter() {
        let api_address = cfg_dev.api_server.clone();
        let token = cfg_user.user_token.clone();
        let game_id = rtdmenu.cur_game.as_ref().unwrap().id.clone();

        let thread_pool = AsyncComputeTaskPool::get();
        let task = thread_pool.spawn(async move {
            let join_game_response = api::game::join(api_address, token, game_id);

            JoinGameCallResponse {
                res: join_game_response,
            }
        });
        commands.spawn(JoinGameCall(task));
        rtdmenu.waiting_for_join_game_call = true;
    }
}

pub(super) fn handle(
    mut commands: Commands,
    mut join_game_call_tasks: Query<(Entity, &mut JoinGameCall)>,
    mut rtdmenu: ResMut<RTDMenu>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    if join_game_call_tasks.is_empty() {
        return;
    }

    let (entity, mut task) = join_game_call_tasks.single_mut();
    if let Some(join_game_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        rtdmenu.waiting_for_join_game_call = false;
        match join_game_call_response.res {
            ResponseJoinGame::Valid(_res) => {
                rtdmenu.play_menu_ui_state = PlayMenuUIState::InLobbyGuest;

                console.send(PrintConsoleLine::new(
                    format!(
                        "Joined game with id: '{}'",
                        rtdmenu.cur_game.as_ref().unwrap().id
                    )
                    .into(),
                ));
            }
            ResponseJoinGame::Error(error) => {
                console.send(PrintConsoleLine::new(
                    format!("Join game failed, got error: '{}'", error).into(),
                ));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<JoinGameCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/play/mod.rs => client-old-for-ref/src/plugins/menu_old/play/mod.rs +0 -89
@@ 1,89 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use iyes_loopless::prelude::*;

use crate::runtime::menu::RTDMenu;

use super::MenuState;

mod ui;

mod allformingcall;
mod creategamecall;
mod joingamecall;
mod mygamescall;
mod startgamecall;

struct CreateGameEvent;
struct AllFormingEvent;
struct JoinGameEvent;
struct MyGamesEvent;
struct StartGameEvent;

pub(super) struct PlayMenuPlugin;

impl Plugin for PlayMenuPlugin {
    fn build(&self, app: &mut App) {
        app.add_loopless_state(PlayMenuState::None)
            .add_event::<CreateGameEvent>()
            .add_event::<AllFormingEvent>()
            .add_event::<JoinGameEvent>()
            .add_event::<MyGamesEvent>()
            .add_event::<StartGameEvent>()
            .add_enter_system(MenuState::Play, play_menu_setup)
            .add_system_set(
                ConditionSet::new()
                    .run_in_state(PlayMenuState::Visible)
                    .with_system(ui::show)
                    .with_system(creategamecall::start.run_on_event::<CreateGameEvent>())
                    .with_system(creategamecall::handle.run_if(waiting_for_create_game_call))
                    .with_system(allformingcall::start.run_on_event::<AllFormingEvent>())
                    .with_system(allformingcall::handle.run_if(waiting_for_all_forming_call))
                    .with_system(joingamecall::start.run_on_event::<JoinGameEvent>())
                    .with_system(joingamecall::handle.run_if(waiting_for_join_game_call))
                    .with_system(mygamescall::start.run_on_event::<MyGamesEvent>())
                    .with_system(mygamescall::handle.run_if(waiting_for_my_games_call))
                    .with_system(startgamecall::start.run_on_event::<StartGameEvent>())
                    .with_system(startgamecall::handle.run_if(waiting_for_start_game_call))
                    .into(),
            );
    }
}

/// Play Menu State
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(super) enum PlayMenuState {
    None,
    Visible,
}

fn play_menu_setup(mut commands: Commands) {
    commands.insert_resource(NextState(PlayMenuState::Visible))
}

fn waiting_for_create_game_call(rtdmenu: Res<RTDMenu>) -> bool {
    rtdmenu.waiting_for_create_game_call
}

fn waiting_for_all_forming_call(rtdmenu: Res<RTDMenu>) -> bool {
    rtdmenu.waiting_for_all_forming_call
}

fn waiting_for_join_game_call(rtdmenu: Res<RTDMenu>) -> bool {
    rtdmenu.waiting_for_join_game_call
}

fn waiting_for_my_games_call(rtdmenu: Res<RTDMenu>) -> bool {
    rtdmenu.waiting_for_my_games_call
}

fn waiting_for_start_game_call(rtdmenu: Res<RTDMenu>) -> bool {
    rtdmenu.waiting_for_start_game_call
}

D client-old-for-ref/src/plugins/menu_old/play/mygamescall.rs => client-old-for-ref/src/plugins/menu_old/play/mygamescall.rs +0 -82
@@ 1,82 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * 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, game::ResponseMyGames},
    cfg::{CfgDev, CfgUser},
    runtime::menu::RTDMenu,
};

use super::MyGamesEvent;

struct MyGamesCallResponse {
    my_games: ResponseMyGames,
}

#[derive(Component)]
pub(super) struct MyGamesCall(Task<MyGamesCallResponse>);

pub(super) fn start(
    mut events: EventReader<MyGamesEvent>,
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    cfg_user: Res<CfgUser>,
    mut rtdmenu: ResMut<RTDMenu>,
) {
    for _event in events.iter() {
        let api_address = cfg_dev.api_server.clone();
        let token = cfg_user.user_token.clone();

        let thread_pool = AsyncComputeTaskPool::get();
        let task = thread_pool.spawn(async move {
            let my_games_response = api::game::my_games(api_address, token);

            MyGamesCallResponse {
                my_games: my_games_response,
            }
        });
        commands.spawn(MyGamesCall(task));
        rtdmenu.waiting_for_my_games_call = true;
    }
}

pub(super) fn handle(
    mut commands: Commands,
    mut my_games_call_tasks: Query<(Entity, &mut MyGamesCall)>,
    mut rtdmenu: ResMut<RTDMenu>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    if my_games_call_tasks.is_empty() {
        return;
    }

    let (entity, mut task) = my_games_call_tasks.single_mut();
    if let Some(my_games_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        rtdmenu.waiting_for_my_games_call = false;
        match my_games_call_response.my_games {
            ResponseMyGames::Valid(res) => rtdmenu.my_games = res,
            ResponseMyGames::Error(error) => {
                console.send(PrintConsoleLine::new(
                    format!("Fetching own games failed, got error: '{}'", error).into(),
                ));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<MyGamesCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/play/startgamecall.rs => client-old-for-ref/src/plugins/menu_old/play/startgamecall.rs +0 -105
@@ 1,105 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * 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 iyes_loopless::state::NextState;

use crate::{
    api::{
        self,
        game::{types::GameState, ResponsePatchGameState},
    },
    cfg::{CfgDev, CfgUser},
    runtime::{
        game::RTDGame,
        menu::{PlayMenuUIState, RTDMenu},
    },
};

use super::{PlayMenuState, StartGameEvent};

struct StartGameCallResponse {
    res: ResponsePatchGameState,
}

#[derive(Component)]
pub(super) struct StartGameCall(Task<StartGameCallResponse>);

pub(super) fn start(
    mut events: EventReader<StartGameEvent>,
    mut commands: Commands,
    cfg_dev: Res<CfgDev>,
    cfg_user: Res<CfgUser>,
    mut rtdmenu: ResMut<RTDMenu>,
) {
    for _event in events.iter() {
        let api_address = cfg_dev.api_server.clone();
        let token = cfg_user.user_token.clone();
        let game_id = rtdmenu.cur_game.as_ref().unwrap().id.clone();

        let thread_pool = AsyncComputeTaskPool::get();
        let task = thread_pool.spawn(async move {
            let patch_game_state_response =
                api::game::patch_state(api_address, token, game_id, GameState::InProgress);

            StartGameCallResponse {
                res: patch_game_state_response,
            }
        });
        commands.spawn(StartGameCall(task));
        rtdmenu.waiting_for_start_game_call = true;
    }
}

pub(super) fn handle(
    mut commands: Commands,
    mut start_game_call_tasks: Query<(Entity, &mut StartGameCall)>,
    mut rtdmenu: ResMut<RTDMenu>,
    mut rtdgame: ResMut<RTDGame>,
    mut console: EventWriter<PrintConsoleLine>,
) {
    if start_game_call_tasks.is_empty() {
        return;
    }

    let (entity, mut task) = start_game_call_tasks.single_mut();
    if let Some(start_game_call_response) = future::block_on(future::poll_once(&mut task.0)) {
        rtdmenu.waiting_for_start_game_call = false;
        match start_game_call_response.res {
            ResponsePatchGameState::Valid(res) => {
                rtdmenu.play_menu_ui_state = PlayMenuUIState::Main;
                rtdgame.cur_game = Some(res);
                commands.insert_resource(NextState(PlayMenuState::None));
                commands.insert_resource(NextState(crate::GameState::Game));

                console.send(PrintConsoleLine::new(
                    format!(
                        "Started game with id: '{}'",
                        rtdmenu.cur_game.as_ref().unwrap().id
                    )
                    .into(),
                ));
            }
            ResponsePatchGameState::Error(error) => {
                console.send(PrintConsoleLine::new(
                    format!("Starting game failed, got error: '{}'", error).into(),
                ));
            }
        }

        // Remove the task, since it's done now
        commands.entity(entity).remove::<StartGameCall>();
        commands.entity(entity).despawn_recursive();
    }
}

D client-old-for-ref/src/plugins/menu_old/play/ui.rs => client-old-for-ref/src/plugins/menu_old/play/ui.rs +0 -318
@@ 1,318 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::{egui, EguiContext};
use iyes_loopless::prelude::*;

use crate::{
    api::game::types::GameState,
    cfg::CfgUser,
    plugins::menu::MenuState,
    runtime::menu::{BrowseMenuTab, PlayMenuUIState, RTDMenu},
};

use super::{
    AllFormingEvent, CreateGameEvent, JoinGameEvent, MyGamesEvent, PlayMenuState, StartGameEvent,
};

pub(super) fn show(
    mut commands: Commands,
    mut egui_context: ResMut<EguiContext>,
    mut rtdmenu: ResMut<RTDMenu>,
    cfg_user: Res<CfgUser>,
    mut creategame_ev_w: EventWriter<CreateGameEvent>,
    mut allforming_ev_w: EventWriter<AllFormingEvent>,
    mut joingame_ev_w: EventWriter<JoinGameEvent>,
    mut mygames_ev_w: EventWriter<MyGamesEvent>,
    mut startgame_ev_w: EventWriter<StartGameEvent>,
) {
    egui::Window::new(egui::RichText::new(rtdmenu.play_menu_ui_state.display()).size(32.))
        .resizable(false)
        .collapsible(false)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .fixed_size(egui::Vec2::new(800., 600.))
        .show(egui_context.ctx_mut(), |ui| {
            // Override egui style for this scope.
            let mut egui_style = (*ui.style_mut()).clone();
            egui_style.text_styles = [
                (
                    egui::TextStyle::Heading,
                    egui::FontId::new(30.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Body,
                    egui::FontId::new(18.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Monospace,
                    egui::FontId::new(14.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Button,
                    egui::FontId::new(24.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Small,
                    egui::FontId::new(10.0, egui::FontFamily::Proportional),
                ),
            ]
            .into();
            ui.set_style(egui_style);

            match rtdmenu.play_menu_ui_state {
                PlayMenuUIState::Main => {
                    egui::SidePanel::left("play_side_panel")
                        .resizable(false)
                        .show_inside(ui, |ui| {
                            ui.vertical_centered(|ui| {
                                if ui.button("Create").clicked() {
                                    rtdmenu.play_menu_ui_state = PlayMenuUIState::CreateGame
                                }

                                if ui.button("Back").clicked() {
                                    commands.insert_resource(NextState(PlayMenuState::None));
                                    commands.insert_resource(NextState(MenuState::Main));
                                }
                            });
                        });

                    egui::TopBottomPanel::top("play_top_panel")
                        .resizable(false)
                        .show_inside(ui, |ui| {
                            ui.horizontal(|ui| {
                                ui.label(egui::RichText::new("Browse: ").size(24.));

                                ui.selectable_value(
                                    &mut rtdmenu.browse_tab,
                                    BrowseMenuTab::Forming,
                                    "Forming",
                                );
                                ui.selectable_value(
                                    &mut rtdmenu.browse_tab,
                                    BrowseMenuTab::InProgress,
                                    "In Progress",
                                );
                                ui.selectable_value(
                                    &mut rtdmenu.browse_tab,
                                    BrowseMenuTab::Finished,
                                    "Finished",
                                );

                                ui.with_layout(
                                    egui::Layout::right_to_left(egui::Align::Center),
                                    |ui| {
                                        if ui.button("Refresh").clicked() {
                                            match rtdmenu.browse_tab {
                                                BrowseMenuTab::Forming => {
                                                    if !rtdmenu.waiting_for_all_forming_call {
                                                        allforming_ev_w.send(AllFormingEvent);
                                                    }
                                                }
                                                BrowseMenuTab::InProgress => {
                                                    if !rtdmenu.waiting_for_my_games_call {
                                                        mygames_ev_w.send(MyGamesEvent);
                                                    }
                                                }
                                                BrowseMenuTab::Finished => {
                                                    if !rtdmenu.waiting_for_my_games_call {
                                                        mygames_ev_w.send(MyGamesEvent);
                                                    }
                                                }
                                            }
                                        }
                                    },
                                );
                            });
                        });

                    ui.vertical_centered(|ui| {
                        egui::ScrollArea::vertical().show(ui, |mut ui| match rtdmenu.browse_tab {
                            BrowseMenuTab::Forming => {
                                browse_forming(
                                    &mut ui,
                                    &mut rtdmenu,
                                    &cfg_user,
                                    &mut joingame_ev_w,
                                );
                            }
                            BrowseMenuTab::InProgress => {
                                browse_inprogress(&mut ui, &mut rtdmenu);
                            }
                            BrowseMenuTab::Finished => {
                                browse_finished(&mut ui, &mut rtdmenu);
                            }
                        });
                    });
                }
                PlayMenuUIState::CreateGame => {
                    ui.vertical_centered(|ui| {
                        ui.add_enabled_ui(!rtdmenu.waiting_for_create_game_call, |ui| {
                            if ui.button("Confirm").clicked() {
                                creategame_ev_w.send(CreateGameEvent);
                            }

                            if ui.button("Cancel").clicked() {
                                rtdmenu.play_menu_ui_state = PlayMenuUIState::Main
                            }
                        });
                    });
                }
                PlayMenuUIState::InLobbyHost => {
                    if ui.button("Start").clicked() {
                        startgame_ev_w.send(StartGameEvent);
                    }

                    if ui.button("Back").clicked() {
                        rtdmenu.play_menu_ui_state = PlayMenuUIState::Main
                    }
                }
                PlayMenuUIState::InLobbyGuest => {
                    ui.label(egui::RichText::new(format!(
                        "Host: {}",
                        rtdmenu.cur_game.as_ref().unwrap().host.username
                    )));

                    if ui.button("Back").clicked() {
                        rtdmenu.play_menu_ui_state = PlayMenuUIState::Main
                    }
                }
            }
        });
}

fn browse_forming(
    ui: &mut egui::Ui,
    rtdmenu: &mut RTDMenu,
    cfg_user: &CfgUser,
    joingame_ev_w: &mut EventWriter<JoinGameEvent>,
) {
    if rtdmenu.waiting_for_all_forming_call {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
    } else {
        if rtdmenu.all_forming_games.is_empty() {
            ui.label("No forming games found.");
        } else {
            for game in rtdmenu.all_forming_games.clone() {
                egui::Frame::none()
                    .fill(egui::Color32::BLACK)
                    .rounding(4.)
                    .outer_margin(4.)
                    .inner_margin(4.)
                    .show(ui, |ui| {
                        ui.horizontal(|ui| {
                            ui.label(format!("Host: {}", game.host.username));
                            ui.with_layout(
                                egui::Layout::right_to_left(egui::Align::Center),
                                |ui| {
                                    if game.guest_id == cfg_user.id {
                                        ui.add_enabled(false, egui::Button::new("Joined"));
                                        if ui.button("Inspect").clicked() {
                                            rtdmenu.cur_game = Some(game.clone());
                                            rtdmenu.play_menu_ui_state =
                                                PlayMenuUIState::InLobbyGuest;
                                        }
                                    } else if game.host_id == cfg_user.id {
                                        ui.add_enabled(false, egui::Button::new("Host"));
                                        if ui.button("Inspect").clicked() {
                                            rtdmenu.cur_game = Some(game.clone());
                                            rtdmenu.play_menu_ui_state =
                                                PlayMenuUIState::InLobbyHost;
                                        }
                                    } else {
                                        if ui.button("Join").clicked() {
                                            rtdmenu.cur_game = Some(game.clone());
                                            joingame_ev_w.send(JoinGameEvent);
                                        }
                                    }
                                },
                            );
                        });
                    });
            }
        }
    }
}

fn browse_inprogress(
    ui: &mut egui::Ui,
    rtdmenu: &mut RTDMenu,
    /*resumegame_ev_w: &mut EventWriter<ResumeGameEvent>*/
) {
    if rtdmenu.waiting_for_my_games_call {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
    } else {
        if rtdmenu.my_games.is_empty() {
            ui.label("No games found.");
        } else {
            let mut games = rtdmenu.my_games.clone();
            games.retain(|g| g.state == GameState::InProgress);

            for game in games {
                egui::Frame::none()
                    .fill(egui::Color32::BLACK)
                    .rounding(4.)
                    .outer_margin(4.)
                    .inner_margin(4.)
                    .show(ui, |ui| {
                        ui.horizontal(|ui| {
                            ui.label(format!("Host: {}", game.host.username));
                            ui.with_layout(
                                egui::Layout::right_to_left(egui::Align::Center),
                                |ui| {
                                    if ui.button("Resume").clicked() {
                                        rtdmenu.cur_game = Some(game.clone());
                                        //resumegame_ev_w.send(ResumeGameEvent);
                                    }
                                },
                            );
                        });
                    });
            }
        }
    }
}

fn browse_finished(
    ui: &mut egui::Ui,
    rtdmenu: &mut RTDMenu,
    /*resumegame_ev_w: &mut EventWriter<ResumeGameEvent>*/
) {
    if rtdmenu.waiting_for_my_games_call {
        ui.horizontal(|ui| {
            ui.spinner();
            ui.label("loading...");
        });
    } else {
        if rtdmenu.my_games.is_empty() {
            ui.label("No games found.");
        } else {
            let mut games = rtdmenu.my_games.clone();
            games.retain(|g| g.state == GameState::Finished);

            for game in games {
                egui::Frame::none()
                    .fill(egui::Color32::BLACK)
                    .rounding(4.)
                    .outer_margin(4.)
                    .inner_margin(4.)
                    .show(ui, |ui| {
                        ui.horizontal(|ui| {
                            ui.label(format!("Host: {}", game.host.username));
                        });
                    });
            }
        }
    }
}

D client-old-for-ref/src/plugins/menu_old/settingsscreen.rs => client-old-for-ref/src/plugins/menu_old/settingsscreen.rs +0 -42
@@ 1,42 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::*;
use iyes_loopless::prelude::*;

use belly::prelude::*;

use super::MenuState;

pub(super) fn settings_setup(mut commands: Commands) {
    commands.add(eml! {
        <body>
            <div c:settingsmenu>
                <buttongroup on:value_change=connect!(|ctx| {
                    let ev = ctx.event();
                    ctx.select(ev.old_value()).add_class("hidden");
                    ctx.select(ev.new_value()).remove_class("hidden");
                })>
                    <button c:settingstab value=".tabdisplay" pressed>"Display"</button>
                    <button c:settingstab value=".tabaudio">"Audio"</button>
                    <button c:settingstab value=".tabmisc">"Misc"</button>
                </buttongroup>
                <div>
                    <div c:tabdisplay>"display"</div>
                    <div c:tabaudio c:hidden>"audio"</div>
                    <div c:tabmisc c:hidden>"misc"</div>
                </div>
                <button c:menubutton on:press=connect!(|ctx| {
                    ctx.commands().insert_resource(NextState(MenuState::Main))
                })>
                    "Back"
                </button>
            </div>
        </body>
    });
}

D client-old-for-ref/src/plugins/mod.rs => client-old-for-ref/src/plugins/mod.rs +0 -11
@@ 1,11 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

pub mod config;
pub mod menu;
pub mod phases;

D client-old-for-ref/src/plugins/phases/loading/mod.rs => client-old-for-ref/src/plugins/phases/loading/mod.rs +0 -91
@@ 1,91 0,0 @@
/*
 * This file is part of laurelin_client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

// FIXME: I hate this. Need to rework whole loading system at some point with a real loading state and screen.

use bevy::{
    prelude::*,
    ui::{JustifyContent, Size, Style, Val},
};

use crate::{
    constants::TEXT_COLOR,
    despawn_screen,
    plugins::config::{LoadEvent, LoadEventValue},
    GameState,
};

/// This plugin is used to load all configs during startup
pub struct LoadingPlugin;

impl Plugin for LoadingPlugin {
    fn build(&self, app: &mut App) {
        app
            // Load the loading screen when we enter the Loading state
            .add_system(loading_setup.in_schedule(OnEnter(GameState::Loading)))
            // Despawn when we exit the Loading state
            .add_system(despawn_screen::<OnLoadingScreen>.in_schedule(OnExit(GameState::Loading)));
    }
}

/// Tag component for tagging entities on the loading screen
#[derive(Component)]
struct OnLoadingScreen;

fn loading_setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut load_event_writer: EventWriter<LoadEvent>,
) {
    let font = asset_server.load("fonts/FiraMono-Regular.ttf");

    commands
        .spawn((
            NodeBundle {
                style: Style {
                    align_items: AlignItems::Center,
                    justify_content: JustifyContent::Center,
                    size: Size::new(Val::Percent(100.), Val::Percent(100.)),
                    ..Default::default()
                },
                ..Default::default()
            },
            OnLoadingScreen,
        ))
        .with_children(|parent| {
            parent.spawn(TextBundle {
                text: Text::from_section(
                    "...",
                    TextStyle {
                        font: font.clone(),
                        font_size: 80.0,
                        color: TEXT_COLOR,
                    },
                ),
                style: Style {
                    size: Size::new(Val::Px(1000.), Val::Auto),
                    align_self: AlignSelf::Center,
                    ..Default::default()
                },
                ..Default::default()
            });
        });

    // Queue up load events for all configs
    load_event_writer.send(LoadEvent {
        value: LoadEventValue::Settings,
    });
    load_event_writer.send(LoadEvent {
        value: LoadEventValue::User,
    });
    load_event_writer.send(LoadEvent {
        value: LoadEventValue::Dev,
    });

    commands.insert_resource(NextState(Some(GameState::MainMenu)));
}

D client-old-for-ref/src/plugins/phases/mod.rs => client-old-for-ref/src/plugins/phases/mod.rs +0 -10
@@ 1,10 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

pub mod loading;
pub mod splash;

D client-old-for-ref/src/plugins/phases/splash/mod.rs => client-old-for-ref/src/plugins/phases/splash/mod.rs +0 -78
@@ 1,78 0,0 @@
/*
 * This file is part of laurelin_client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::{
    prelude::*,
    ui::{JustifyContent, Size, Style, Val},
};

use crate::{despawn_screen, GameState};

/// This plugin will display a splsh logo on startup
pub struct SplashPlugin;

impl Plugin for SplashPlugin {
    fn build(&self, app: &mut App) {
        app
            // Load the splash when we enter the Splash state
            .add_system(splash_setup.in_schedule(OnEnter(GameState::Splash)))
            // Run a timer for a few seconds
            .add_system(splash_timer.run_if(in_state(GameState::Splash)))
            // Despawn when we exit the Splash state
            .add_system(despawn_screen::<OnSplashScreen>.in_schedule(OnExit(GameState::Splash)));
    }
}

/// Tag component for tagging entities on the splash screen
#[derive(Component)]
struct OnSplashScreen;

/// Timer resource
#[derive(Resource, Deref, DerefMut)]
struct SplashTimer(Timer);

fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    let logo = asset_server.load("branding/logo.png");

    commands
        .spawn((
            NodeBundle {
                style: Style {
                    align_items: AlignItems::Center,
                    justify_content: JustifyContent::Center,
                    size: Size::new(Val::Percent(100.), Val::Percent(100.)),
                    ..Default::default()
                },
                ..Default::default()
            },
            OnSplashScreen,
        ))
        .with_children(|parent| {
            parent.spawn(ImageBundle {
                style: Style {
                    size: Size::new(Val::Px(1000.), Val::Auto),
                    ..Default::default()
                },
                image: UiImage {
                    texture: logo,
                    flip_x: false,
                    flip_y: false,
                },
                ..Default::default()
            });
        });

    // Insert the timer resource
    commands.insert_resource(SplashTimer(Timer::from_seconds(2., TimerMode::Once)));
}

fn splash_timer(mut commands: Commands, time: Res<Time>, mut timer: ResMut<SplashTimer>) {
    if timer.tick(time.delta()).finished() {
        commands.insert_resource(NextState(Some(GameState::Loading)))
    }
}

D client-old-for-ref/src/runtime/game/mod.rs => client-old-for-ref/src/runtime/game/mod.rs +0 -24
@@ 1,24 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::Resource;

use crate::api::game::types::Game;

/// Runtime data for use when in game
#[derive(Resource)]
pub(crate) struct RTDGame {
    /// Current game
    pub cur_game: Option<Game>,
}

impl Default for RTDGame {
    fn default() -> Self {
        Self { cur_game: None }
    }
}

D client-old-for-ref/src/runtime/menu/mod.rs => client-old-for-ref/src/runtime/menu/mod.rs +0 -71
@@ 1,71 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy::prelude::Resource;

use crate::api::game::types::Game;

/// Runtime data for use in the menu
#[derive(Resource)]
pub(crate) struct RTDMenu {
    pub play_menu_ui_state: PlayMenuUIState,
    pub browse_tab: BrowseMenuTab,
    pub waiting_for_create_game_call: bool,
    /// Current game
    pub cur_game: Option<Game>,
    /// List of all forming games
    pub all_forming_games: Vec<Game>,
    pub waiting_for_all_forming_call: bool,
    pub waiting_for_join_game_call: bool,
    /// Stores games where the player is involved
    pub my_games: Vec<Game>,
    pub waiting_for_my_games_call: bool,
    pub waiting_for_start_game_call: bool,
}

impl Default for RTDMenu {
    fn default() -> Self {
        Self {
            play_menu_ui_state: PlayMenuUIState::Main,
            browse_tab: BrowseMenuTab::Forming,
            waiting_for_create_game_call: false,
            cur_game: None,
            all_forming_games: vec![],
            waiting_for_all_forming_call: false,
            waiting_for_join_game_call: false,
            my_games: vec![],
            waiting_for_my_games_call: false,
            waiting_for_start_game_call: false,
        }
    }
}

pub(crate) enum PlayMenuUIState {
    Main,
    CreateGame,
    InLobbyHost,
    InLobbyGuest,
}

impl PlayMenuUIState {
    pub fn display(&self) -> &str {
        match self {
            PlayMenuUIState::Main => "Play",
            PlayMenuUIState::CreateGame => "Create",
            PlayMenuUIState::InLobbyHost => "Lobby (Host)",
            PlayMenuUIState::InLobbyGuest => "Lobby (Guest)",
        }
    }
}

#[derive(PartialEq)]
pub(crate) enum BrowseMenuTab {
    Forming,
    InProgress,
    Finished,
}

D client-old-for-ref/src/runtime/mod.rs => client-old-for-ref/src/runtime/mod.rs +0 -10
@@ 1,10 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

pub(crate) mod game;
pub(crate) mod menu;

D client-old-for-ref/src/util/egui/menuwindow.rs => client-old-for-ref/src/util/egui/menuwindow.rs +0 -53
@@ 1,53 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy_egui::egui;

/// wrapper for menu windows
pub fn menuwindow(
    ctx: &egui::Context,
    title: &str,
    fixed_size: &egui::Vec2,
    show: impl FnOnce(&mut egui::Ui),
) {
    egui::Window::new(egui::RichText::new(title).size(32.))
        .collapsible(false)
        .resizable(false)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .fixed_size(*fixed_size)
        .show(ctx, |ui| {
            // Override egui style for this scope.
            let mut egui_style = (*ui.style_mut()).clone();
            egui_style.text_styles = [
                (
                    egui::TextStyle::Heading,
                    egui::FontId::new(30.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Body,
                    egui::FontId::new(18.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Monospace,
                    egui::FontId::new(14.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Button,
                    egui::FontId::new(24.0, egui::FontFamily::Proportional),
                ),
                (
                    egui::TextStyle::Small,
                    egui::FontId::new(10.0, egui::FontFamily::Proportional),
                ),
            ]
            .into();
            ui.set_style(egui_style);

            show(ui);
        });
}

D client-old-for-ref/src/util/egui/mod.rs => client-old-for-ref/src/util/egui/mod.rs +0 -13
@@ 1,13 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

mod password;
pub use password::password;

mod menuwindow;
pub use menuwindow::menuwindow;

D client-old-for-ref/src/util/egui/password.rs => client-old-for-ref/src/util/egui/password.rs +0 -37
@@ 1,37 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

use bevy_egui::egui;

fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {
    let state_id = ui.id().with("show_plaintext");

    let mut show_plaintext = ui.data_mut(|d| d.get_temp::<bool>(state_id).unwrap_or(false));

    let result = ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
        // TODO: this was previously add_sized() with a max of ui.available_size()
        // just, a note if... some usecase breaks or something
        ui.add(egui::TextEdit::singleline(password).password(!show_plaintext));

        let response = ui
            .add(egui::SelectableLabel::new(show_plaintext, "👁"))
            .on_hover_text("Show/hide password");

        if response.clicked() {
            show_plaintext = !show_plaintext;
        }
    });

    ui.data_mut(|d| d.insert_temp(state_id, show_plaintext));

    result.response
}

pub fn password(password: &mut String) -> impl egui::Widget + '_ {
    move |ui: &mut egui::Ui| password_ui(ui, password)
}

D client-old-for-ref/src/util/mod.rs => client-old-for-ref/src/util/mod.rs +0 -10
@@ 1,10 0,0 @@
/*
 * This file is part of sdbclient
 * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

pub mod egui;
pub mod sl;

D client-old-for-ref/src/util/sl.rs => client-old-for-ref/src/util/sl.rs +0 -66
@@ 1,66 0,0 @@
/*
 * This file is part of laurelin/client
 * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
 *
 * Licensed under GPL-3.0-only.
 * See LICENSE for licensing information.
 */

// TODO: We should encode the configs as base64

use bevy::prelude::*;

pub fn load<T: Default + for<'a> serde::Deserialize<'a>>(target_dir: &str, file_name: &str) -> T {
    let file_path: String = format!("{}/{}", target_dir, file_name);

    match std::path::Path::new(&file_path).exists() {
        true => {
            info!("sl::load found '{}'", file_path);
            let json: Vec<u8> = std::fs::read(file_path.clone()).unwrap();
            serde_json::from_str(std::str::from_utf8(&json).unwrap()).unwrap_or_else(|_| {
                panic!(
                    "sl::load couldn't deserialize the config at '{}'",
                    file_path
                )
            })
        }
        false => {
            warn!("sl::load couldn't find '{}', using defaults", file_path);
            T::default()
        }
    }
}

pub fn save<T>(target_dir: &str, file_name: &str, value: &T) -> bool
where
    T: ?Sized + serde::Serialize,
{
    let file_path: String = format!("{}/{}", target_dir, file_name);
    let json: String = serde_json::to_string(&value).unwrap();

    match std::path::Path::new(target_dir).exists() {
        true => {}
        false => {
            info!("sl::save creating target dir '{}'", target_dir);
            match std::fs::create_dir_all(target_dir) {
                Ok(_) => {}
                Err(_) => {
                    warn!("sl::save couldn't create target dir '{}'", target_dir);
                    warn!("sl::save nothing will be saved!");
                    return false;
                }
            }
        }
    }

    match std::fs::write(&file_path, json) {
        Ok(_) => {
            info!("sl::save wrote file '{}'", file_path);
            true
        }
        Err(_) => {
            info!("sl::save couldn't write file '{}'", file_path);
            false
        }
    }
}