diff --git a/godot/scenes/main.tscn b/godot/scenes/main.tscn index e7412ee..3149064 100644 --- a/godot/scenes/main.tscn +++ b/godot/scenes/main.tscn @@ -3,7 +3,7 @@ [ext_resource type="PackedScene" uid="uid://bbmc2gmy7g8ag" path="res://scenes/misc/menu_player.tscn" id="1_o5qli"] [ext_resource type="PackedScene" uid="uid://n3u0jr4wiib1" path="res://scenes/ui/settings.tscn" id="2_0wfyh"] -[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr", "popup_panel", "popup_title", "popup_text", "popup_button")] +[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr", "recent_servers_button", "popup_panel", "popup_title", "popup_text", "popup_button")] camera = NodePath("camera_3d") main_position = NodePath("main_pos") host_position = NodePath("host_pos") @@ -12,6 +12,7 @@ host_panel = NodePath("host_panel") join_panel = NodePath("join_panel") host_port = NodePath("host_panel/v_box_container/host_port") join_addr = NodePath("join_panel/v_box_container/address_text") +recent_servers_button = NodePath("join_panel/v_box_container/recent_servers") popup_panel = NodePath("popup_panel") popup_title = NodePath("popup_panel/v_box_container/title") popup_text = NodePath("popup_panel/v_box_container/text") @@ -163,6 +164,9 @@ layout_mode = 2 text = "Address" horizontal_alignment = 1 +[node name="recent_servers" type="OptionButton" parent="join_panel/v_box_container" unique_id=486398131] +layout_mode = 2 + [node name="address_text" type="LineEdit" parent="join_panel/v_box_container" unique_id=672176728] custom_minimum_size = Vector2(150, 30) layout_mode = 2 diff --git a/rust/src/game_settings.rs b/rust/src/game_settings.rs index 89510c2..c51e22d 100644 --- a/rust/src/game_settings.rs +++ b/rust/src/game_settings.rs @@ -1,3 +1,9 @@ +use std::{ + collections::VecDeque, + net::SocketAddr, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; + use godot::{ classes::{FileAccess, file_access::ModeFlags}, prelude::*, @@ -11,6 +17,13 @@ pub const NETWORK_SINGLETON_NAME: &str = "GameSettingsGlobal"; pub struct GameSettings { pub fov: f64, pub default_name: String, + pub recent_servers: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RecentServer { + pub addr: SocketAddr, + pub visited: u128, } impl Default for GameSettings { @@ -18,6 +31,7 @@ impl Default for GameSettings { Self { fov: 90., default_name: "Default McGee".to_owned(), + recent_servers: Vec::new(), } } } @@ -60,4 +74,38 @@ impl GameSettingsManager { } self.run_deferred(|s| s.signals().on_settings_changed().emit()); } + + pub fn visit_server(&mut self, server: SocketAddr) { + if let Some(server) = self + .settings + .recent_servers + .iter_mut() + .find(|s| s.addr == server) + { + server.visited = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::from_millis(0)) + .as_nanos(); + } else { + self.settings.recent_servers.push(RecentServer { + addr: server, + visited: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::from_millis(0)) + .as_nanos(), + }); + } + let mut servers = self.settings.recent_servers.clone(); + + servers.sort_by_key(|s| s.visited); + servers = servers.into_iter().rev().collect(); + while servers.len() > 10 { + servers.remove(0); + } + let settings = GameSettings { + recent_servers: servers, + ..self.settings.clone() + }; + self.update_settings(settings); + } } diff --git a/rust/src/net/protocol.rs b/rust/src/net/protocol.rs index d48ae13..07d73fb 100644 --- a/rust/src/net/protocol.rs +++ b/rust/src/net/protocol.rs @@ -17,8 +17,12 @@ impl NetworkManager { if let Some(peer) = &mut self.peer { match peer { - PeerKind::Client(peer, _) => match message { + PeerKind::Client(peer, addr) => match message { teanet::PeerMessage::NewConnection(connection) => { + GameSettingsManager::singleton() + .bind_mut() + .visit_server(addr.clone()); + cli.bind_mut().publish_message( format!("Connected to: {}", connection.address), CliColor::Info, diff --git a/rust/src/ui/main_menu.rs b/rust/src/ui/main_menu.rs index 6b0f50c..dd2eb85 100644 --- a/rust/src/ui/main_menu.rs +++ b/rust/src/ui/main_menu.rs @@ -1,10 +1,11 @@ use godot::{ - classes::{Button, Camera3D, Label, LineEdit, Panel, SpinBox}, + classes::{Button, Camera3D, Label, LineEdit, OptionButton, Panel, SpinBox}, prelude::*, register::property::SimpleVar, }; use crate::{ + game_settings::{GameSettingsManager, RecentServer}, net::network_manager::NetworkManager, popup_queue::{Popup, PopupQueue}, }; @@ -42,6 +43,8 @@ pub struct MainMenu { host_port: Option>, #[export] join_addr: Option>, + #[export] + recent_servers_button: Option>, #[export] popup_panel: Option>, @@ -57,12 +60,30 @@ pub struct MainMenu { prev_panel: Option>, + recent_servers: Vec, + base: Base, } #[godot_api] impl INode3D for MainMenu { fn ready(&mut self) { + if let Some(mut recent_servers_button) = self.recent_servers_button.clone() { + self.recent_servers = GameSettingsManager::singleton() + .bind() + .settings + .recent_servers + .clone(); + for server in &self.recent_servers { + recent_servers_button.add_item(&server.addr.to_string()); + } + recent_servers_button.select(-1); + + recent_servers_button + .signals() + .item_selected() + .connect_other(self, |s, idx| s.on_recent_server_selected(idx)); + } self.hide_popup(); } @@ -212,4 +233,17 @@ impl MainMenu { panel.set_visible(false); } } + + pub fn on_recent_server_selected(&mut self, idx: i64) { + if idx >= 0 { + if let Some(server) = self.recent_servers.get(idx as usize) + && let Some(addr) = &mut self.join_addr + { + addr.set_text(&server.addr.to_string()); + } + if let Some(button) = &mut self.recent_servers_button { + button.select(-1); + } + } + } }