use std::{net::UdpSocket, time::SystemTime}; use renet::transport::{NetcodeClientTransport, NetcodeTransportError, NETCODE_USER_DATA_BYTES}; use store::{GameEvent, GameState, CheckerMove}; use bevy::prelude::*; use bevy::window::PrimaryWindow; use bevy_renet::{ renet::{transport::ClientAuthentication, ConnectionConfig, RenetClient}, transport::{client_connected, NetcodeClientPlugin}, RenetClientPlugin, }; #[derive(Debug, Resource)] struct CurrentClientId(u64); #[derive(Resource)] struct BevyGameState(GameState); impl Default for BevyGameState { fn default() -> Self { Self { 0: GameState::default(), } } } #[derive(Resource, Deref, DerefMut)] struct GameUIState { selected_tile: Option, } impl Default for GameUIState { fn default() -> Self { Self { selected_tile: None, } } } #[derive(Event)] struct BevyGameEvent(GameEvent); // This id needs to be the same as the server is using const PROTOCOL_ID: u64 = 2878; fn main() { // Get username from stdin args let args = std::env::args().collect::>(); let username = &args[1]; let (client, transport, client_id) = new_renet_client(&username).unwrap(); App::new() // Lets add a nice dark grey background color .insert_resource(ClearColor(Color::hex("282828").unwrap())) .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { // Adding the username to the window title makes debugging a whole lot easier. title: format!("TricTrac <{}>", username), resolution: (1080.0, 1080.0).into(), ..default() }), ..default() })) // Add our game state and register GameEvent as a bevy event .insert_resource(BevyGameState::default()) .insert_resource(GameUIState::default()) .add_event::() // Renet setup .add_plugins(RenetClientPlugin) .add_plugins(NetcodeClientPlugin) .insert_resource(client) .insert_resource(transport) .insert_resource(CurrentClientId(client_id)) .add_systems(Startup, setup) .add_systems(Update, (update_waiting_text, input, update_board, panic_on_error_system)) .add_systems( PostUpdate, receive_events_from_server.run_if(client_connected()), ) .run(); } ////////// COMPONENTS ////////// #[derive(Component)] struct UIRoot; #[derive(Component)] struct WaitingText; #[derive(Component)] struct Board { squares: [Square; 26] } impl Default for Board { fn default() -> Self { Self { squares: [Square { count: 0, color: None, position: 0}; 26] } } } impl Board { fn square_at(&self, position: usize) -> Square { self.squares[position] } } #[derive(Component, Clone, Copy)] struct Square { count: usize, color: Option, position: usize, } ////////// UPDATE SYSTEMS ////////// fn update_board( mut commands: Commands, game_state: Res, mut game_events: EventReader, asset_server: Res, ) { for event in game_events.iter() { match event.0 { GameEvent::Move { player_id, moves } => { // trictrac positions, TODO : dépend de player_id let (x, y) = if moves.0.get_to() < 13 { (13 - moves.0.get_to(), 1) } else { (moves.0.get_to() - 13, 0)}; let texture = asset_server.load(match game_state.0.players[&player_id].color { store::Color::Black => "tac.png", store::Color::White => "tic.png", }); info!("spawning tictac sprite"); commands.spawn(SpriteBundle { transform: Transform::from_xyz( 83.0 * (x as f32 - 1.0), -30.0 + 540.0 * (y as f32 - 1.0), 0.0, ), sprite: Sprite { custom_size: Some(Vec2::new(83.0, 83.0)), ..default() }, texture: texture.into(), ..default() }); } _ => {} } } } fn update_waiting_text(mut text_query: Query<&mut Text, With>, time: Res