From 920cff3bbe4ab764728f273c5bad2d8f97452ca0 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Thu, 23 Mar 2023 02:21:25 +0200 Subject: [PATCH] added basic tcp server to update inspector values --- src/inspector.rs | 74 +++++++++++++++++++++++++++++--- src/main.rs | 2 + src/pokemon.rs | 31 ++++++++++++++ src/server.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++++ src/ui.rs | 15 ++----- 5 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 src/server.rs diff --git a/src/inspector.rs b/src/inspector.rs index bce2492..172e72c 100644 --- a/src/inspector.rs +++ b/src/inspector.rs @@ -1,21 +1,27 @@ use bevy::prelude::*; use bevy_egui::EguiContexts; -use crate::{database::load_url_asset, pokemon::*, ui::UiAssets}; +use crate::{ + database::{load_url_asset, Database}, + pokemon::*, + server::Request, + ui::{UiAssets, UiState}, +}; pub struct InspectorPlugin; impl Plugin for InspectorPlugin { fn build(&self, app: &mut App) { - app.register_type::() - .insert_resource(InspectorPokemon::default()) - .add_system(state_changed); + app.register_type::() + .insert_resource(Inspector::default()) + .add_system(state_changed) + .add_system(request_handler.in_base_set(CoreSet::PreUpdate)); } } #[derive(Reflect, Resource, Default, Debug, Clone)] #[reflect(Resource)] -pub struct InspectorPokemon { +pub struct Inspector { pub pokemon: Option, pub nature: Option, pub characteristic: Option, @@ -23,7 +29,7 @@ pub struct InspectorPokemon { } fn state_changed( - inspector: Res, + inspector: Res, assets: Res, mut ui_assets: ResMut, mut contexts: EguiContexts, @@ -44,3 +50,59 @@ fn state_changed( } } } + +fn request_handler( + mut request_events: EventReader, + mut inspector: ResMut, + mut ui: ResMut, + characteristics: Res>, + natures: Res>, +) { + for request in request_events.iter() { + info!("{:#}", request); + match request.urn.as_str() { + "clear" => { + inspector.pokemon = None; + inspector.nature = None; + inspector.characteristic = None; + inspector.derived_stats = DerivedStats::default(); + ui.name = String::from(""); + ui.level = String::from(""); + ui.derived_stats.clear(); + } + "update" => { + let data = request.data.clone(); + if let Some(name) = data["name"].as_str() { + ui.name = name.to_string(); + } + if let Some(level) = data["level"].as_i32() { + ui.level = level.to_string(); + inspector.derived_stats.level = Some(level); + } + if let Some(nature) = data["nature"].as_str() { + if let Some(nature) = natures.map.get(&nature.to_lowercase()).cloned() { + inspector.nature = Some(nature); + } + } + if let Some(characteristic) = data["characteristic"].as_str() { + if let Some(characteristic) = characteristics + .map + .get(&characteristic.to_lowercase()) + .cloned() + { + inspector.characteristic = Some(characteristic); + } + } + for stat in BaseStat::all() { + if let Some(value) = data[stat.key()].as_i32() { + ui.derived_stats.insert(stat, value.to_string()); + inspector.derived_stats.set_stat_value(stat, Some(value)); + } + } + } + _ => { + warn!("Unknown urn"); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index bfdc5ca..30ab91c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use bevy::{prelude::*, window::WindowResolution}; pub mod database; pub mod inspector; pub mod pokemon; +pub mod server; pub mod ui; fn main() { @@ -23,5 +24,6 @@ fn main() { .add_plugin(database::DatabasePlugin) .add_plugin(inspector::InspectorPlugin) .add_plugin(ui::UiPlugin) + .add_plugin(server::ServerPlugin) .run(); } diff --git a/src/pokemon.rs b/src/pokemon.rs index 69d5bd9..26f8571 100644 --- a/src/pokemon.rs +++ b/src/pokemon.rs @@ -63,6 +63,11 @@ impl DerivedStats { BaseStat::Speed => &mut self.speed, } } + + pub fn set_stat_value(&mut self, stat: BaseStat, value: Option) { + let val = self.value_mut(stat); + *val = value; + } } #[derive(Default, Debug, Clone, Reflect, FromReflect, PartialEq)] @@ -167,6 +172,32 @@ pub enum BaseStat { Speed, } +impl BaseStat { + pub fn all() -> Vec { + vec![ + BaseStat::Hp, + BaseStat::Attack, + BaseStat::Defense, + BaseStat::SpecialAttack, + BaseStat::SpecialDefense, + BaseStat::Speed, + ] + } +} + +impl GetKey for BaseStat { + fn key(&self) -> String { + match self { + BaseStat::Hp => "hp".to_string(), + BaseStat::Attack => "attack".to_string(), + BaseStat::Defense => "defense".to_string(), + BaseStat::SpecialAttack => "special_attack".to_string(), + BaseStat::SpecialDefense => "special_defense".to_string(), + BaseStat::Speed => "speed".to_string(), + } + } +} + impl fmt::Display for BaseStat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match self { diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..e3f72da --- /dev/null +++ b/src/server.rs @@ -0,0 +1,107 @@ +use bevy::prelude::*; +use json::JsonValue; +use std::{ + io::Read, + net, + sync::{Arc, Mutex}, + thread, + time::Duration, +}; + +pub struct ServerPlugin; + +impl Plugin for ServerPlugin { + fn build(&self, app: &mut App) { + app.add_event::() + .insert_resource(Server::default()) + .add_startup_system(init_server) + .add_system(flush_messages.in_base_set(CoreSet::First)); + } +} + +#[derive(Resource, Default)] +pub struct Server { + messages: Arc>>, +} + +pub struct Request { + pub urn: String, + pub data: JsonValue, +} + +impl std::fmt::Display for Request { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {}", + self.urn, + json::stringify_pretty(self.data.clone(), 4) + ) + } +} + +fn init_server(server: Res) { + let messages = server.messages.clone(); + + thread::spawn(move || { + let listener = listen("localhost:8888"); + for stream in listener.incoming() { + match stream { + Ok(stream) => handle_message(stream, &messages), + _ => (), + } + } + }); +} + +fn flush_messages(server: Res, mut request_events: EventWriter) { + match server.messages.lock() { + Ok(mut messages) => { + for message in messages.drain(..) { + if let Some(request) = try_parse_request(&message) { + request_events.send(request); + } + } + } + Err(err) => { + error!("{}", err); + } + }; +} + +fn listen(address: &str) -> net::TcpListener { + net::TcpListener::bind(address).unwrap() +} + +fn handle_message(mut stream: net::TcpStream, messages: &Arc>>) { + let mut buffer = String::new(); + stream + .set_read_timeout(Some(Duration::from_secs(10))) + .expect("Could not set read timeout for socket"); + match stream.read_to_string(&mut buffer) { + Ok(_size) => { + match messages.lock() { + Ok(mut messages) => { + messages.push(buffer); + } + Err(err) => { + error!("{}", err); + } + }; + } + _ => (), + } +} + +fn try_parse_request(raw: &str) -> Option { + let obj = json::parse(raw).ok()?; + if !obj.is_object() { + return None; + } + let urn = obj["urn"].as_str()?; + let data = obj["data"].clone(); + Some(Request { + urn: urn.to_string(), + data, + }) +} diff --git a/src/ui.rs b/src/ui.rs index 45936d9..bd72855 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -3,7 +3,7 @@ use bevy_egui::{egui, EguiContexts, EguiPlugin}; use lazy_static::lazy_static; use std::collections::HashMap; -use crate::{database::Database, inspector::InspectorPokemon, pokemon::*}; +use crate::{database::Database, inspector::Inspector, pokemon::*}; const INPUT_LABEL_WIDTH: f32 = 100.0; const INPUT_TEXT_WIDTH: f32 = 150.0; @@ -69,7 +69,7 @@ fn load_assets(mut ui_assets: ResMut, assets: Res) { fn handle_state( ui_state: Res, pokemon: Res>, - mut inspector: ResMut, + mut inspector: ResMut, ) { if ui_state.is_changed() { if let Some(pokemon) = pokemon.map.get(ui_state.name.to_lowercase().as_str()) { @@ -80,7 +80,7 @@ fn handle_state( fn ui_system( mut contexts: EguiContexts, - mut inspector: ResMut, + mut inspector: ResMut, ui_assets: Res, mut ui_state: ResMut, mut rendered_texture_id: Local, @@ -243,14 +243,7 @@ fn ui_system( }); // Stats - for stat in vec![ - BaseStat::Hp, - BaseStat::Attack, - BaseStat::Defense, - BaseStat::SpecialAttack, - BaseStat::SpecialDefense, - BaseStat::Speed, - ] { + for stat in BaseStat::all() { let mut column_size = COLUMNS.iter(); ui.horizontal(|ui| { // Nature modifier