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
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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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 => +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
- }
- }
-}