DEVELOPMENT ENVIRONMENT

~liljamo/deck-builder

b8bd27dd7fcbade514bc75433e7a0ed068be3f65 — Jonni Liljamo 1 year, 5 months ago ce344ce
feat(client): finish new refresh, impl game lock
M client/src/game_status/mod.rs => client/src/game_status/mod.rs +3 -0
@@ 35,6 35,7 @@ pub struct Card {
}

/// a supply pile holds an amount of some card
#[derive(Clone)]
pub struct SupplyPile {
    /// the card type that the supply pile holds
    pub card: Card,


@@ 52,6 53,7 @@ pub enum PlayerState {
    BuyPhase,
}

#[derive(Clone)]
pub struct PlayerStatus {
    pub display_name: String,
    pub state: PlayerState,


@@ 65,6 67,7 @@ pub struct PlayerStatus {
}

/// constructed from a vector of [`Action`]s
#[derive(Clone)]
pub struct GameStatus {
    /// a modifiable Actions Vector, will be modified when parsing actions,
    /// used for showing the log

M client/src/macros/async_task.rs => client/src/macros/async_task.rs +29 -0
@@ 147,3 147,32 @@ macro_rules! async_task_handle_call {
        }
    };
}

#[macro_export]
macro_rules! async_task_handle_call_refresh {
    ($call_type:ty, |$response:ident, $game_data:ident, $parse_ev_w:ident| $handler_func:expr) => {
        pub fn handle_call(
            mut commands: Commands,
            mut tasks: Query<(Entity, &mut $call_type)>,
            mut $game_data: ResMut<GameData>,
            mut $parse_ev_w: EventWriter<ParseGameStatusEvent>,
            mut rg_ev_w: EventWriter<RefreshGameEvent>,
        ) {
            match tasks.get_single_mut() {
                Ok((entity, mut task)) => {
                    if let Some($response) = future::block_on(future::poll_once(&mut task.0)) {
                        $handler_func;

                        rg_ev_w.send(RefreshGameEvent);

                        // remove the task
                        commands.entity(entity).remove::<$call_type>();
                        commands.entity(entity).despawn_recursive();
                    }
                }
                // NOTE: don't do anything if the wanted thingy doesn't exist
                _ => {}
            }
        }
    };
}

M client/src/plugins/async_tasks/parse_game_status.rs => client/src/plugins/async_tasks/parse_game_status.rs +5 -2
@@ 6,7 6,7 @@
 * See LICENSE for licensing information.
 */

use crate::{api::game::Game, game_status::GameStatus, plugins::game::GameData};
use crate::{api::game::Game, game_status::GameStatus, plugins::{game::GameData, FinishedRefreshGameEvent}};

use bevy::{
    prelude::*,


@@ 35,11 35,14 @@ pub fn handle_call(
    mut commands: Commands,
    mut tasks: Query<(Entity, &mut ParseGameStatus)>,
    mut game_data: ResMut<GameData>,
    mut frg_ev_w: EventWriter<FinishedRefreshGameEvent>,
) {
    if let Ok((entity, mut task)) = tasks.get_single_mut() {
        if let Some(response) = future::block_on(future::poll_once(&mut task.0)) {
            game_data.game_status = Some(response);
            game_data.parsing_data = false;
            game_data.locked = false;

            frg_ev_w.send(FinishedRefreshGameEvent);

            // remove the task
            commands.entity(entity).remove::<ParseGameStatus>();

M client/src/plugins/async_tasks/req_game_action_create.rs => client/src/plugins/async_tasks/req_game_action_create.rs +4 -5
@@ 11,8 11,8 @@ use crate::{
        self,
        game::{Action, CreateActionResponse},
    },
    async_task_handle_call, async_task_start_call,
    plugins::GameData,
    async_task_handle_call_refresh, async_task_start_call,
    plugins::{GameData, RefreshGameEvent},
    NetworkingOptions,
};



@@ 36,9 36,8 @@ async_task_start_call!(GameActionCreateCallEvent, GameActionCreateCall, |ev, no|
    api::game::create_action(&no, &ev.action)
});

async_task_handle_call!(
    GameActionCreateCall,
    |_u0, _u1, response, _game_data, _u2| {
async_task_handle_call_refresh!(
    GameActionCreateCall, |response, _game_data, _u2| {
        match response {
            Err(_err) => panic!("login failed, handle me"),
            Ok(resp) => {

M client/src/plugins/async_tasks/req_game_details.rs => client/src/plugins/async_tasks/req_game_details.rs +0 -1
@@ 41,7 41,6 @@ async_task_handle_call!(
                game_data.game = Some(resp);

                // start data parsing
                game_data.parsing_data = true;
                parse_ev_w.send(ParseGameStatusEvent {
                    game: game_data.game.as_ref().unwrap().clone(),
                });

M client/src/plugins/game/mod.rs => client/src/plugins/game/mod.rs +21 -11
@@ 11,7 11,7 @@ use bevy_rapier3d::prelude::*;

use crate::{api::game::Game, game_status::GameStatus, AppState, Global};

use self::card::VisualCardBundle;
use self::supply::SpawnSupplyPilesEvent;

use super::GameDetailsCallEvent;



@@ 29,17 29,20 @@ impl Plugin for GamePlugin {
            .add_plugin(card::CardPlugin)
            .add_plugin(supply::SupplyPlugin)
            .add_plugin(hand::HandPlugin)
            .add_event::<RefreshGameEvent>()
            .add_event::<FinishedRefreshGameEvent>()
            .add_system(game_setup.in_schedule(OnEnter(AppState::InGame)))
            .add_system(handle_refresh_game_event.run_if(on_event::<RefreshGameEvent>()));
            .add_system(handle_refresh_game_event.run_if(on_event::<RefreshGameEvent>()))
            .add_system(handle_finished_refresh_game_event.run_if(on_event::<FinishedRefreshGameEvent>()));
    }
}

#[derive(Resource)]
#[derive(Resource, Clone)]
pub struct GameData {
    pub game: Option<Game>,
    pub game_status: Option<GameStatus>,

    pub parsing_data: bool,
    pub locked: bool,
}

impl Default for GameData {


@@ 48,19 51,16 @@ impl Default for GameData {
            game: None,
            game_status: None,

            parsing_data: false,
            locked: true,
        }
    }
}

fn game_setup(
    mut commands: Commands,
    mut details_ev_w: EventWriter<GameDetailsCallEvent>,
    global: Res<Global>,
    mut rg_ev_w: EventWriter<RefreshGameEvent>,
) {
    details_ev_w.send(GameDetailsCallEvent {
        game_id: global.cur_game_id.clone(),
    });
    rg_ev_w.send(RefreshGameEvent);

    // create the playing surface
    commands


@@ 73,11 73,21 @@ pub struct RefreshGameEvent;
fn handle_refresh_game_event(
    mut details_ev_w: EventWriter<GameDetailsCallEvent>,
    mut game_data: ResMut<GameData>,
    global: Res<Global>,
) {
    game_data.locked = true;
    game_data.game = None;
    game_data.game_status = None;

    details_ev_w.send(GameDetailsCallEvent {
        game_id: game_data.game.as_ref().unwrap().id.clone(),
        game_id: global.cur_game_id.clone(),
    });
}

pub struct FinishedRefreshGameEvent;

fn handle_finished_refresh_game_event(
    mut ssp_ev_w: EventWriter<SpawnSupplyPilesEvent>,
) {
    ssp_ev_w.send(SpawnSupplyPilesEvent);
}

M client/src/plugins/game/supply/mod.rs => client/src/plugins/game/supply/mod.rs +13 -3
@@ 23,8 23,13 @@ impl Plugin for SupplyPlugin {
    fn build(&self, app: &mut App) {
        app.add_event::<SpawnSupplyPilesEvent>()
            .add_event::<PositionSupplyPilesEvent>()
            .add_system(spawn_supply_piles.run_if(on_event::<SpawnSupplyPilesEvent>()))
            .add_system(position_supply_piles.run_if(on_event::<PositionSupplyPilesEvent>()))
            .add_systems(
                (
                    spawn_supply_piles.run_if(on_event::<SpawnSupplyPilesEvent>()),
                    apply_system_buffers,
                    position_supply_piles.run_if(on_event::<PositionSupplyPilesEvent>())
                ).chain()
            )
            .add_system(handle_clicked_supply_pile);
    }
}


@@ 106,9 111,13 @@ fn handle_clicked_supply_pile(
    mut commands: Commands,
    mut card_query: Query<(Entity, &VisualCard, &visual_card_kind::Supply), (With<visual_card_kind::Supply>, With<ClickedCard>)>,
    mut gac_ev_w: EventWriter<GameActionCreateCallEvent>,
    game_data: Res<GameData>,
    mut game_data: ResMut<GameData>,
    global: Res<Global>,
) {
    if game_data.locked {
        return;
    }

    let Ok((entity, card, card_kind)) = card_query.get_single() else {
        return;
    };


@@ 130,6 139,7 @@ fn handle_clicked_supply_pile(
        return;
    }

    game_data.locked = true;
    gac_ev_w.send(GameActionCreateCallEvent {
        action: Action::new(
            &game_data.game.as_ref().unwrap().id,

M client/src/plugins/game/ui/mod.rs => client/src/plugins/game/ui/mod.rs +14 -16
@@ 16,7 16,7 @@ use crate::{
    AppState, Global, seed_gen,
};

use super::{supply::SpawnSupplyPilesEvent, GameData};
use super::{supply::{SpawnSupplyPilesEvent, PositionSupplyPilesEvent}, GameData, RefreshGameEvent};

mod state_button;



@@ 362,7 362,7 @@ pub fn dev_details_ui(
    global: Res<Global>,
    game_data: Res<GameData>,
    mut create_action_ev_w: EventWriter<GameActionCreateCallEvent>,
    mut ssp_ev_w: EventWriter<SpawnSupplyPilesEvent>,
    mut rg_ev_w: EventWriter<RefreshGameEvent>,
) {
    egui::Window::new("Game Details").show(contexts.ctx_mut(), |ui| {
        let Some(game) = &game_data.game else {


@@ 370,25 370,23 @@ pub fn dev_details_ui(
                return;
            };
        let Some(status) = &game_data.game_status else {
                if game_data.parsing_data {
                    // early return if game_status is None, and we're parsing it
                    return;
                }
                // game_status is None, but we're not parsing it...
                // should be unreachable, I think?
                // early return if game_status is None
                return;
            };

        if status.actions.is_empty() && game.host_id == global.user.as_ref().unwrap().id {
            if ui.button("Init Game").clicked() {
                // NOTE/FIXME: hardcoded game init
                hardcoded_init(game, &mut create_action_ev_w);
        ui.add_enabled_ui(!game_data.locked, |ui| {
            if status.actions.is_empty() && game.host_id == global.user.as_ref().unwrap().id {
                if ui.button("Init Game").clicked() {
                    // NOTE/FIXME: hardcoded game init
                    hardcoded_init(game, &mut create_action_ev_w);
                    rg_ev_w.send(RefreshGameEvent);
                }
            }
        }

        if ui.button("spawn things").clicked() {
            ssp_ev_w.send(SpawnSupplyPilesEvent);
        }
            if ui.button("Force Refresh").clicked() {
                    rg_ev_w.send(RefreshGameEvent);
            }
        });

        ui.separator();


M client/src/plugins/game/ui/state_button.rs => client/src/plugins/game/ui/state_button.rs +10 -3
@@ 84,15 84,16 @@ fn update_state_button(
        (Changed<Interaction>, With<StateButton>)
    >,
    mut text_query: Query<&mut Text>,
    game_data: Res<GameData>,
    mut game_data: ResMut<GameData>,
    global: Res<Global>,
    mut gac_ev_w: EventWriter<GameActionCreateCallEvent>,
) {
    for (interaction, mut color, mut visibility, children) in &mut interaction_query {
        let Some(game) = &game_data.game else {
        // NOTE: horrible clones because of borrow funnies
        let Some(game) = game_data.clone().game else {
            return;
        };
        let Some(status) = &game_data.game_status else {
        let Some(status) = game_data.clone().game_status else {
            return;
        };
        let Some(user) = &global.user else {


@@ 116,10 117,16 @@ fn update_state_button(
        }
        *visibility = Visibility::Inherited;

        if game_data.locked {
            // input locked rn, so don't show any signs of interactivity
            return;
        }

        match interaction {
            Interaction::Clicked => {
                *color = PRESSED_BUTTON.into();

                game_data.locked = true;
                match player.state {
                    PlayerState::PlayPhase => {
                        gac_ev_w.send(GameActionCreateCallEvent {