diff --git a/crates/horizon-client/src/friends_screen.rs b/crates/horizon-client/src/friends_screen.rs index 82dbbb995..7577e2add 100644 --- a/crates/horizon-client/src/friends_screen.rs +++ b/crates/horizon-client/src/friends_screen.rs @@ -38,6 +38,8 @@ pub enum Action { Share, Copy, Stop, + /// Open the troubleshooting page. + Help, Done, } @@ -108,9 +110,6 @@ fn layout( // for something) when only the LAN and VPNs can. let tint = if view.reachable { grey() } else { WHITE }; add(&mut lines, &view.status, tint, &mut y); - if let Some(help) = &view.help { - add(&mut lines, &format!("Help: {help}"), grey(), &mut y); - } y += 4.0; } let here = if view.guests.is_empty() { @@ -122,7 +121,10 @@ fn layout( if let Some(notice) = &view.notice { add(&mut lines, notice, WHITE, &mut y); } - add(&mut lines, HINT, grey(), &mut y); + // Where friends paste the code, while it fits above the footer. + if y + 10.0 * wrap(HINT, width, atlas).len() as f32 <= h - 33.0 { + add(&mut lines, HINT, grey(), &mut y); + } } else { add( &mut lines, @@ -141,15 +143,35 @@ fn layout( active: view.shown, }); } - // HeaderAndFooterLayout's footer: Done. - buttons.push(Button { - x: cx - 100.0, - y: h - 27.0, - w: 200.0, - label: "gui.done", - action: Action::Done, - active: true, - }); + // HeaderAndFooterLayout's footer: Done, beside Troubleshooting when + // friends on the internet can't reach the host. + if view.sharing && view.help.is_some() { + buttons.push(Button { + x: cx - 154.0, + y: h - 27.0, + w: 150.0, + label: "Troubleshooting...", + action: Action::Help, + active: true, + }); + buttons.push(Button { + x: cx + 4.0, + y: h - 27.0, + w: 150.0, + label: "gui.done", + action: Action::Done, + active: true, + }); + } else { + buttons.push(Button { + x: cx - 100.0, + y: h - 27.0, + w: 200.0, + label: "gui.done", + action: Action::Done, + active: true, + }); + } (lines, buttons) } /// The code's groups packed into lines of at most `width`. @@ -251,11 +273,17 @@ mod tests { let copy = buttons.iter().find(|b| b.action == Action::Copy).unwrap(); assert!(!copy.active, "nothing to copy before the code"); let with_code = FriendsView { - code: Some("ABCD-EFGH-IJKL".into()), + code: Some("0B33K-Q405Q-1Z5CV-X6NHY-R12N0-VK8T1-60N00-RA134-952PE-1HA08-910P0-K5M01-ST1NB-JDTB6-K4W1P-0".into()), + status: "Friends on your network or VPN can join. For friends on the internet: turn on UPnP on your router, or forward TCP port 25580 to 10.0.0.2, or use a relay, or a VPN such as Tailscale or ZeroTier (the code already holds your VPN address).".into(), + help: Some("https://example.invalid/help".into()), ..sharing }; - let (_, buttons) = layout(&atlas, size, &with_code); - for action in [Action::Copy, Action::Stop, Action::Done] { + let (lines, buttons) = layout(&atlas, size, &with_code); + assert!( + lines.iter().all(|(_, _, y)| y + 9.0 <= size.1 - 27.0), + "the text stays above the footer" + ); + for action in [Action::Copy, Action::Stop, Action::Help, Action::Done] { let b = buttons.iter().find(|b| b.action == action).unwrap(); assert_eq!( click(&atlas, size, (b.x + 1.0, b.y + 1.0), &with_code), diff --git a/crates/horizon-client/src/game/friends.rs b/crates/horizon-client/src/game/friends.rs index 35f7c6fea..5cffcadd3 100644 --- a/crates/horizon-client/src/game/friends.rs +++ b/crates/horizon-client/src/game/friends.rs @@ -198,6 +198,12 @@ impl Game<'_> { } Action::Copy => self.friends_copy(), Action::Stop => self.friends_stop(), + // The system browser, as the Shader Packs page opens its folder. + Action::Help => { + if let Some(url) = &view.help { + super::packs::open_folder(std::path::Path::new(url)); + } + } Action::Done => self.screen = super::Screen::Paused, } } diff --git a/crates/horizon-client/src/game/screens.rs b/crates/horizon-client/src/game/screens.rs index 17d3b119b..7f09251bf 100644 --- a/crates/horizon-client/src/game/screens.rs +++ b/crates/horizon-client/src/game/screens.rs @@ -105,6 +105,15 @@ impl Game<'_> { for (i, (x, y, sprite)) in pause_icons(w, h).into_iter().enumerate() { let active = i == FRIENDS_ICON && friends; let hover = ui::hovered(self.cursor, x, y, 20.0, 20.0); + // The icon has no label: name it for controller focus and the + // control socket's widget tree. + if i == FRIENDS_ICON { + horizon_client::widgets::record( + horizon_client::friends_screen::TITLE, + [x, y, 20.0, 20.0], + active, + ); + } ui::button( batch, atlas, diff --git a/docs/graphics.md b/docs/graphics.md index 6a7e671ad..7629ad47d 100644 --- a/docs/graphics.md +++ b/docs/graphics.md @@ -4,13 +4,15 @@ Horizon's graphics settings are two screens built from vanilla's widgets, opened from the top of **Video Settings** (**Horizon Graphics...** and **Advanced Graphics...**, beside **Shader Packs...**, where Sodium and Iris put theirs) or from **Options → Horizon Settings...**: -- **Graphics** (Simple): a preset (Performance, Balanced, Quality, Ultra or Custom), render distance, LOD distance, FOV, max framerate, VSync and GUI scale. -- **Advanced Graphics**: every setting, grouped under Rendering, Quality, Display, Preferences, Performance and Overlay. +- **Graphics** (Simple): a preset (Performance, Balanced, Quality, Ultra or Custom) and LOD distance. +- **Advanced Graphics**: every Horizon setting, grouped under Rendering, Quality, Display, Preferences, Performance and Overlay. + +Vanilla's own settings (render distance, FOV, framerate, VSync, GUI scale, smooth lighting...) stay where vanilla has them, in Video Settings; both screens leave them out. Every row has a one-line description and its cost in frame time (none, low, medium, high, or saves time for culling that makes frames faster), and applies at once, except worker threads, which apply at the next start. A preset sets the quality settings together; changing one of them turns it into Custom. Display preferences (FOV, framerate, VSync, GUI scale, fullscreen, present mode, pacing) leave the preset as it is. -![Simple](media/settings-simple.png) -![Advanced](media/settings-advanced.png) +![Video Settings, with Horizon's buttons at the top](media/settings-simple.png) +![Advanced Graphics](media/settings-advanced.png) | Group | Setting | Stored in | | --- | --- | --- | diff --git a/docs/play-with-friends.md b/docs/play-with-friends.md index c788aa27c..2a318c972 100644 --- a/docs/play-with-friends.md +++ b/docs/play-with-friends.md @@ -61,7 +61,8 @@ port closes, the router mapping is deleted). Sharing again makes a new secret. ## When friends can't reach the host -The host's panel says what to expect, from what the game found: +Play with Friends says what to expect, from what the game found; when friends on the internet can't +reach the host, its **Troubleshooting...** button (beside Done) opens this section in the browser: | What the game found | What the host reads | | --- | --- | diff --git a/website/docs/settings.md b/website/docs/settings.md index fae69e015..e1860cd4a 100644 --- a/website/docs/settings.md +++ b/website/docs/settings.md @@ -4,14 +4,16 @@ Horizon plays like vanilla out of the box. This page covers the settings that ar The game's menus are vanilla's. Horizon adds its own settings to vanilla's **Options** screen the way mods do: **Horizon Settings...** and **Mods...** take the last two buttons of the grid. **Horizon Settings** opens Graphics, Advanced Graphics, Shader Packs, Controller Settings, Extras and Mods, each a screen of vanilla's buttons and sliders. The graphics and shader pack buttons are also at the top of **Video Settings**, and Controller Settings at the bottom of **Controls**. -![Horizon's Graphics settings](/media/settings-800.webp) +![Video Settings, with Horizon's graphics buttons at the top](/media/settings-800.webp) ## Graphics: presets, Simple and Advanced **Options, Video Settings** starts with **Horizon Graphics...** and **Advanced Graphics...** (also in **Options, Horizon Settings**). -- **Horizon Graphics** has a preset (Performance, Balanced, Quality, Ultra or Custom), render distance, LOD distance, field of view, maximum frame rate, VSync and GUI scale. -- **Advanced Graphics** has every setting, in groups: Rendering, Quality, Display, Preferences, Performance and Overlay. +- **Horizon Graphics** has a preset (Performance, Balanced, Quality, Ultra or Custom) and LOD distance. +- **Advanced Graphics** has every Horizon setting, in groups: Rendering, Quality, Display, Preferences, Performance and Overlay. + +Vanilla's own settings (render distance, field of view, frame rate, VSync, GUI scale and the rest) stay on Video Settings, where vanilla has them. Every row has a one-line description and its cost in frame time: none, low, medium or high. Changes apply at once, except worker threads, which apply at the next start. A preset sets the quality settings together, and changing one of them turns the preset into **Custom**.