From 6292d982110a478e1549edb011caba7827389ed5 Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Tue, 14 Jan 2025 17:34:11 +0100 Subject: [PATCH] Change start arg parsing in client to directly change the config after loaded --- game/client-console/src/console/console.rs | 26 +-- .../src/console/local_console.rs | 112 +++++++----- src/client/client.rs | 166 ++++++++++++++---- 3 files changed, 194 insertions(+), 110 deletions(-) diff --git a/game/client-console/src/console/console.rs b/game/client-console/src/console/console.rs index 97918a1..de0ccd1 100644 --- a/game/client-console/src/console/console.rs +++ b/game/client-console/src/console/console.rs @@ -3,12 +3,11 @@ use std::collections::VecDeque; use base::system::{self, SystemTimeInterface}; use client_containers::skins::SkinContainer; use client_render_base::render::tee::RenderTee; -use client_types::console::{entries_to_parser, ConsoleEntry}; +use client_types::console::ConsoleEntry; use client_ui::console::{page::ConsoleUi, user_data::UserData}; -use command_parser::parser::{parse, ParserCache}; -use config::config::ConfigEngine; +use command_parser::parser::ParserCache; use egui::Color32; -use game_config::config::{Config, ConfigGame}; +use game_config::config::Config; use graphics::graphics::graphics::Graphics; use ui_base::{ types::{UiRenderPipe, UiState}, @@ -77,25 +76,6 @@ impl ConsoleRender { } } - pub fn parse_cmd( - &mut self, - cmd: &str, - config_game: &mut ConfigGame, - config_engine: &mut ConfigEngine, - ) { - if !cmd.is_empty() { - let cmds = parse(cmd, &entries_to_parser(&self.entries), &mut self.cache); - client_ui::console::utils::run_commands( - &cmds, - &self.entries, - config_engine, - config_game, - &mut String::new(), - true, - ); - } - } - #[must_use] pub fn render( &mut self, diff --git a/game/client-console/src/console/local_console.rs b/game/client-console/src/console/local_console.rs index fe48ad4..ab81256 100644 --- a/game/client-console/src/console/local_console.rs +++ b/game/client-console/src/console/local_console.rs @@ -93,8 +93,66 @@ impl super::console::ConsoleEvents for LocalConsoleEvents { pub type LocalConsole = ConsoleRender>>; -#[derive(Debug, Default)] -pub struct LocalConsoleBuilder {} +#[derive(Debug)] +pub struct LocalConsoleBuilder { + pub entries: Vec, + pub console_events: LocalConsoleEvents, + pub parser_cache: Rc>, +} + +impl Default for LocalConsoleBuilder { + fn default() -> Self { + let console_events: LocalConsoleEvents = Default::default(); + let mut entries: Vec = Vec::new(); + + let val = ConfigEngine::conf_value(); + let events_var = console_events.clone(); + let var_on_set = Rc::new(move |name: &str| { + events_var.push(LocalConsoleEvent::ConfigVariable { + name: name.to_string(), + }); + }); + parse_conf_values_as_str_list( + "".to_string(), + &mut |entry, _| { + entries.push(ConsoleEntry::Var(ConsoleEntryVariable { + full_name: entry.name, + usage: entry.usage, + description: entry.description, + args: entry.args, + on_set: var_on_set.clone(), + })); + }, + val, + "".into(), + Default::default(), + ); + let val = ConfigGame::conf_value(); + parse_conf_values_as_str_list( + "".to_string(), + &mut |entry, _| { + entries.push(ConsoleEntry::Var(ConsoleEntryVariable { + full_name: entry.name, + usage: entry.usage, + description: entry.description, + args: entry.args, + on_set: var_on_set.clone(), + })); + }, + val, + "".into(), + Default::default(), + ); + let parser_cache = Rc::new(RefCell::new(ParserCache::default())); + Self::register_commands(console_events.clone(), &mut entries, parser_cache.clone()); + + Self { + console_events, + entries, + parser_cache, + } + } +} impl LocalConsoleBuilder { fn register_commands( @@ -804,55 +862,13 @@ impl LocalConsoleBuilder { })); } - pub fn build(creator: &UiCreator) -> LocalConsole { - let console_events: LocalConsoleEvents = Default::default(); - let mut entries: Vec = Vec::new(); - let val = ConfigEngine::conf_value(); - let events_var = console_events.clone(); - let var_on_set = Rc::new(move |name: &str| { - events_var.push(LocalConsoleEvent::ConfigVariable { - name: name.to_string(), - }); - }); - parse_conf_values_as_str_list( - "".to_string(), - &mut |entry, _| { - entries.push(ConsoleEntry::Var(ConsoleEntryVariable { - full_name: entry.name, - usage: entry.usage, - description: entry.description, - args: entry.args, - on_set: var_on_set.clone(), - })); - }, - val, - "".into(), - Default::default(), - ); - let val = ConfigGame::conf_value(); - parse_conf_values_as_str_list( - "".to_string(), - &mut |entry, _| { - entries.push(ConsoleEntry::Var(ConsoleEntryVariable { - full_name: entry.name, - usage: entry.usage, - description: entry.description, - args: entry.args, - on_set: var_on_set.clone(), - })); - }, - val, - "".into(), - Default::default(), - ); - let parser_cache = Rc::new(RefCell::new(ParserCache::default())); - Self::register_commands(console_events.clone(), &mut entries, parser_cache.clone()); + pub fn build(self, creator: &UiCreator) -> LocalConsole { ConsoleRender::new( creator, - entries, - Box::new(console_events), + self.entries, + Box::new(self.console_events), Color32::from_rgba_unmultiplied(0, 0, 0, 150), - parser_cache, + self.parser_cache, ) } } diff --git a/src/client/client.rs b/src/client/client.rs index 07f6e86..5195c88 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -17,7 +17,7 @@ use base_io::io::{Io, IoFileSys}; use binds::binds::{BindActionsHotkey, BindActionsLocalPlayer}; use client_accounts::accounts::{Accounts, AccountsLoading}; use client_console::console::{ - console::ConsoleRenderPipe, + console::{ConsoleEvents, ConsoleRenderPipe}, local_console::{LocalConsole, LocalConsoleBuilder, LocalConsoleEvent}, remote_console::RemoteConsoleEvent, }; @@ -41,7 +41,7 @@ use client_render_game::render_game::{ RenderGameCreateOptions, RenderGameForPlayer, RenderGameInput, RenderGameInterface, RenderGameSettings, RenderModTy, RenderPlayerCameraMode, }; -use client_types::console::entries_to_parser; +use client_types::console::{entries_to_parser, ConsoleEntry}; use client_ui::{ chat::user_data::{ChatEvent, ChatMode}, connect::{ @@ -69,6 +69,7 @@ use client_ui::{ spectator_selection::user_data::SpectatorSelectionEvent, utils::render_tee_for_ui, }; +use command_parser::parser::ParserCache; use config::config::{ConfigEngine, ConfigMonitor}; use demo::recorder::DemoRecorder; use editor::editor::{EditorInterface, EditorResult}; @@ -182,13 +183,81 @@ pub fn ddnet_main( ) }); - let config_engine = config_fs::load(&io).unwrap_or_default(); + let mut config_engine = config_fs::load(&io).unwrap_or_default(); let benchmark = Benchmark::new(config_engine.dbg.bench); - let config_game = game_config_fs::fs::load(&io).unwrap_or_default(); + let mut config_game = game_config_fs::fs::load(&io).unwrap_or_default(); benchmark.bench("loading client config"); + let mut has_startup_errors = false; + let local_console_builder = if !start_arguments.is_empty() { + let local_console_builder = LocalConsoleBuilder::default(); + let parser_entries = entries_to_parser(&local_console_builder.entries); + for line in start_arguments.iter().filter(|l| !l.is_empty()) { + let cmds = command_parser::parser::parse( + line, + &parser_entries, + &mut local_console_builder.parser_cache.borrow_mut(), + ); + let mut res = String::default(); + let cur_cmds_succeeded = run_commands( + &cmds, + &local_console_builder.entries, + &mut config_engine, + &mut config_game, + &mut res, + true, + ); + log::debug!("{}", res); + if !cur_cmds_succeeded { + log::error!("{}", res); + } + let mut has_events = true; + let mut count = 0; + while has_events { + has_events = false; + let events = local_console_builder.console_events.take(); + for ev in events { + if let LocalConsoleEvent::Exec { file_path } = &ev { + ClientNativeImpl::handle_exec( + &io, + file_path.clone(), + &mut config_engine, + &mut config_game, + &local_console_builder.entries, + &mut local_console_builder.parser_cache.borrow_mut(), + |err| { + log::error!("{}", err); + has_startup_errors = true; + }, + |msg| { + log::info!("{}", msg); + }, + ); + + has_events = true; + } else { + local_console_builder.console_events.push(ev); + } + } + + count += 1; + + if count >= 16 { + has_startup_errors = true; + log::error!("Exec recursion count reached 16, which is the upper limit."); + break; + } + } + has_startup_errors |= !cur_cmds_succeeded; + } + benchmark.bench("parsing start arguments"); + Some(local_console_builder) + } else { + None + }; + let graphics_backend_io_loading = GraphicsBackendIoLoading::new(&config_engine.gfx, &io); // first prepare all io tasks of all components benchmark.bench("load_io of graphics backend"); @@ -207,6 +276,8 @@ pub fn ddnet_main( config_game, graphics_backend_io_loading, graphics_backend_loading: None, + local_console_builder, + has_startup_errors, }; Native::run_loop::( client, @@ -278,6 +349,9 @@ struct ClientNativeLoadingImpl { config_game: ConfigGame, graphics_backend_io_loading: GraphicsBackendIoLoading, graphics_backend_loading: Option, + + local_console_builder: Option, + has_startup_errors: bool, } struct ClientNativeImpl { @@ -1968,10 +2042,19 @@ impl ClientNativeImpl { .unwrap(); } - fn handle_exec(&mut self, file_path: PathBuf) { - let fs = self.io.fs.clone(); - let cmds_file = match self - .io + fn handle_exec( + io: &IoFileSys, + file_path: PathBuf, + config_engine: &mut ConfigEngine, + config_game: &mut ConfigGame, + + entries: &[ConsoleEntry], + parser_cache: &mut ParserCache, + mut on_err: impl FnMut(String), + mut on_log: impl FnMut(String), + ) { + let fs = io.fs.clone(); + let cmds_file = match io .rt .spawn(async move { fs.read_file(&file_path) @@ -1995,40 +2078,29 @@ impl ClientNativeImpl { { Ok(cmds_file) => cmds_file, Err(err) => { - self.notifications - .add_err(err.to_string(), Duration::from_secs(10)); + on_err(err.to_string()); return; } }; let mut cmds_succeeded = true; - let parser_entries = entries_to_parser(&self.local_console.entries); + let parser_entries = entries_to_parser(entries); for line in cmds_file.lines().filter(|l| !l.is_empty()) { - let cmds = command_parser::parser::parse( - line, - &parser_entries, - &mut self.local_console.user.borrow_mut(), - ); + let cmds = command_parser::parser::parse(line, &parser_entries, parser_cache); let mut res = String::default(); - let cur_cmds_succeeded = run_commands( - &cmds, - &self.local_console.entries, - &mut self.config.engine, - &mut self.config.game, - &mut res, - true, - ); + let cur_cmds_succeeded = + run_commands(&cmds, entries, config_engine, config_game, &mut res, true); log::debug!("{}", res); if !cur_cmds_succeeded { - self.console_logs.push_str(&res); + on_log(res); } cmds_succeeded &= cur_cmds_succeeded; } if !cmds_succeeded { - self.notifications.add_err( + on_err( "At least one command failed to be executed, \ - see local console for more info.", - Duration::from_secs(5), + see local console for more info." + .to_string(), ); } } @@ -2107,7 +2179,20 @@ impl ClientNativeImpl { } } } - LocalConsoleEvent::Exec { file_path } => self.handle_exec(file_path), + LocalConsoleEvent::Exec { file_path } => Self::handle_exec( + &self.io.clone().into(), + file_path, + &mut self.config.engine, + &mut self.config.game, + &self.local_console.entries, + &mut self.local_console.user.borrow_mut(), + |err| { + self.notifications.add_err(err, Duration::from_secs(10)); + }, + |msg| { + self.console_logs.push_str(&msg); + }, + ), LocalConsoleEvent::Echo { text } => { self.notifications.add_info(text, Duration::from_secs(2)); } @@ -2398,7 +2483,11 @@ impl FromNativeLoadingImpl for ClientNativeImpl { ui_creator.load_font(&font_data); benchmark.bench("loading font"); - let mut local_console = LocalConsoleBuilder::build(&ui_creator); + let mut local_console = loading + .local_console_builder + .take() + .unwrap_or_default() + .build(&ui_creator); benchmark.bench("local console"); // then prepare components allocations etc. @@ -2493,7 +2582,14 @@ impl FromNativeLoadingImpl for ClientNativeImpl { graphics_memory_usage.staging_memory_usage, &ui_creator, ); - let notifications = ClientNotifications::new(&graphics, &loading.sys, &ui_creator); + let mut notifications = ClientNotifications::new(&graphics, &loading.sys, &ui_creator); + if loading.has_startup_errors { + notifications.add_err( + "Some startup commands failed to be parsed, \ + please read the logs for more information.", + Duration::from_secs(5), + ); + } let loading_page = Box::new(LoadingPage::new()); let page_err = UiWasmManagerErrorPageErr::default(); @@ -2639,15 +2735,7 @@ impl FromNativeLoadingImpl for ClientNativeImpl { ); benchmark.bench("global binds"); - let start_cmd = native.start_arguments().join(" "); - local_console.parse_cmd( - &start_cmd, - &mut loading.config_game, - &mut loading.config_engine, - ); - local_console.ui.ui_state.is_ui_open = false; - benchmark.bench("parsing start args"); let mut client = Self { menu_map,