mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 17:19:15 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3548e4d802 | ||
|
|
cb3983803e |
No files matched your search
@@ -143,6 +143,8 @@ mod enderman_hurt_teleport_tests;
|
||||
mod enderman_placement_tests;
|
||||
mod entity;
|
||||
mod entity_culling;
|
||||
#[cfg(test)]
|
||||
mod entity_inside_tests;
|
||||
mod entity_sounds;
|
||||
mod event_loop;
|
||||
mod events;
|
||||
@@ -162,15 +164,6 @@ mod frame_geometry;
|
||||
mod frames;
|
||||
#[cfg(feature = "friends")]
|
||||
mod friends;
|
||||
mod held_items;
|
||||
#[cfg(test)]
|
||||
mod setblock_order_tests;
|
||||
// Without Play with Friends: nothing shared, no Friends button (game/friends_off.rs).
|
||||
#[cfg(test)]
|
||||
mod entity_inside_tests;
|
||||
#[cfg(not(feature = "friends"))]
|
||||
#[path = "game/friends_off.rs"]
|
||||
mod friends;
|
||||
mod functions;
|
||||
mod game_mode;
|
||||
mod game_tests;
|
||||
@@ -181,6 +174,7 @@ mod golems;
|
||||
mod growth;
|
||||
mod hand;
|
||||
mod headless;
|
||||
mod held_items;
|
||||
mod hud_editor;
|
||||
mod hud_room;
|
||||
mod ime;
|
||||
@@ -421,6 +415,8 @@ mod seats;
|
||||
mod server_links;
|
||||
mod server_packs;
|
||||
mod server_travel;
|
||||
#[cfg(test)]
|
||||
mod setblock_order_tests;
|
||||
mod shader_packs;
|
||||
mod share_view;
|
||||
mod sheep_dye;
|
||||
@@ -653,9 +649,9 @@ enum Screen {
|
||||
Advancements,
|
||||
/// WorldOptionsScreen (game/lan_screen.rs).
|
||||
WorldOptions,
|
||||
/// Play with Friends, from the pause menu's Friends button
|
||||
/// (the page at `Place::PauseFriends`, game/page_screen.rs).
|
||||
Friends,
|
||||
/// The page a crate registered at `Place::PauseFriends` (Play with Friends in the official
|
||||
/// build), from the pause menu's Friends icon, drawn as its own screen (game/page_screen.rs).
|
||||
AddedPage,
|
||||
/// `SocialInteractionsScreen` (`social_screen`, game/social.rs).
|
||||
Social,
|
||||
/// CommandBlockEditScreen (game/command_blocks.rs).
|
||||
@@ -699,7 +695,7 @@ impl Screen {
|
||||
Screen::Stats => "stats",
|
||||
Screen::Advancements => "advancements",
|
||||
Screen::WorldOptions => "world_options",
|
||||
Screen::Friends => "friends",
|
||||
Screen::AddedPage => "friends",
|
||||
Screen::Social => "social",
|
||||
Screen::CommandBlock => "command_block",
|
||||
Screen::BlockEditor => "block_editor",
|
||||
@@ -6241,7 +6237,7 @@ impl Game<'_> {
|
||||
Screen::Advancements => self.draw_advancements(&mut batch),
|
||||
Screen::Mod => {}
|
||||
Screen::WorldOptions => self.draw_world_options(&mut batch),
|
||||
Screen::Friends => self.draw_friends_screen(&mut batch),
|
||||
Screen::AddedPage => self.draw_added_page(&mut batch),
|
||||
Screen::Social => self.draw_social_screen(&mut batch),
|
||||
Screen::ServerLinks => self.draw_server_links(&mut batch),
|
||||
Screen::ConfirmLink => self.draw_confirm_link(&mut batch),
|
||||
@@ -11014,8 +11010,8 @@ impl Game<'_> {
|
||||
Screen::WorldOptions if button == MouseButton::Left => {
|
||||
self.world_options_release()
|
||||
}
|
||||
Screen::Friends if pressed && button == MouseButton::Left => {
|
||||
self.friends_screen_click()
|
||||
Screen::AddedPage if pressed && button == MouseButton::Left => {
|
||||
self.added_page_click()
|
||||
}
|
||||
Screen::Social if pressed && button == MouseButton::Left => {
|
||||
self.social_click()
|
||||
|
||||
@@ -479,7 +479,7 @@ impl Game<'_> {
|
||||
Screen::Stats => "stats",
|
||||
Screen::Advancements => "advancements",
|
||||
Screen::WorldOptions => "world-options",
|
||||
Screen::Friends => "friends",
|
||||
Screen::AddedPage => "friends",
|
||||
Screen::Social => "social",
|
||||
Screen::ServerLinks => "server-links",
|
||||
Screen::ConfirmLink => "confirm-link",
|
||||
|
||||
@@ -17,16 +17,19 @@ const PROTOCOL: &str = "0.1.0";
|
||||
|
||||
/// The command groups the game serves, as `help` chains them: what `hello` reports as its features.
|
||||
const FEATURES: &[&str] = &[
|
||||
"game", "station", "blocks", "lan", "friends", "render", "sculk", "agent", "ui", "options",
|
||||
"events", "saves", "pad", "studio",
|
||||
"game", "station", "blocks", "lan", "render", "sculk", "agent", "ui", "options", "events",
|
||||
"saves", "pad", "studio",
|
||||
];
|
||||
|
||||
/// `hello`: the protocol the socket speaks, the game's release and the command groups it serves.
|
||||
/// `hello`: the protocol the socket speaks, the game's release and the command groups it serves
|
||||
/// (the engine's, then those of the crates that answer commands, `Hooks::control_commands`).
|
||||
fn hello() -> Value {
|
||||
let groups = horizon_hooks::engine::control_commands().map(|(group, _)| group);
|
||||
let features: Vec<&str> = FEATURES.iter().copied().chain(groups).collect();
|
||||
json!({
|
||||
"protocol": PROTOCOL,
|
||||
"release": horizon_core::release::VERSION,
|
||||
"features": FEATURES,
|
||||
"features": features,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -400,7 +403,7 @@ impl Game<'_> {
|
||||
if let Some(value) = self.lan_control(&words)? {
|
||||
return Ok(Some(value));
|
||||
}
|
||||
if let Some(value) = self.friends_control(&words)? {
|
||||
if let Some(value) = self.hooks_control(&words)? {
|
||||
return Ok(Some(value));
|
||||
}
|
||||
if let Some(value) = self.studio_control(&words)? {
|
||||
@@ -417,7 +420,7 @@ impl Game<'_> {
|
||||
.chain(super::station_control::HELP)
|
||||
.chain(super::blockctl::HELP)
|
||||
.chain(super::lan::HELP)
|
||||
.chain(super::friends::HELP)
|
||||
.chain(horizon_hooks::engine::control_commands().flat_map(|(_, help)| help))
|
||||
.chain(super::render_control::HELP)
|
||||
.chain(super::sculk::HELP)
|
||||
.chain(super::agent::HELP)
|
||||
@@ -1370,7 +1373,7 @@ impl Game<'_> {
|
||||
Screen::Stats => "stats",
|
||||
Screen::Advancements => "advancements",
|
||||
Screen::WorldOptions => "world-options",
|
||||
Screen::Friends => "friends",
|
||||
Screen::AddedPage => "friends",
|
||||
Screen::Social => "social",
|
||||
Screen::ServerLinks => "server-links",
|
||||
Screen::ConfirmLink => "confirm-link",
|
||||
|
||||
@@ -123,7 +123,7 @@ fn screen_ids_are_distinct_and_stable() {
|
||||
Screen::Stats,
|
||||
Screen::Advancements,
|
||||
Screen::WorldOptions,
|
||||
Screen::Friends,
|
||||
Screen::AddedPage,
|
||||
Screen::Social,
|
||||
Screen::CommandBlock,
|
||||
Screen::BlockEditor,
|
||||
|
||||
@@ -523,7 +523,7 @@ fn debug_options_interrupts(screen: Screen, playing_overlay: bool) -> bool {
|
||||
| Screen::EmoteWheel
|
||||
| Screen::Stats
|
||||
| Screen::Advancements
|
||||
| Screen::Friends
|
||||
| Screen::AddedPage
|
||||
| Screen::Social
|
||||
| Screen::ServerLinks
|
||||
| Screen::ConfirmLink
|
||||
@@ -584,7 +584,7 @@ mod tests {
|
||||
Screen::GameModeSwitcher,
|
||||
Screen::Stats,
|
||||
Screen::Advancements,
|
||||
Screen::Friends,
|
||||
Screen::AddedPage,
|
||||
Screen::Social,
|
||||
Screen::ServerLinks,
|
||||
Screen::ConfirmLink,
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
//!
|
||||
//! It sees the game only through `horizon_hooks` (the `ShareView` its `Hooks::share` tick
|
||||
//! gets, `Hooks::lan_closed`, its page, `tr`) and keeps what it shares in its own statics, so
|
||||
//! it can leave the engine for horizon-friends (spec #14167). The `horizon-ctl friends` command
|
||||
//! below still reaches it from `Game` until then.
|
||||
use super::Game;
|
||||
use horizon_client::Result;
|
||||
//! it can leave the engine for horizon-friends (spec #14167). `horizon-ctl friends` reaches it
|
||||
//! through `Hooks::control`.
|
||||
use horizon_friends::{Host, HostConfig, HostEvent, PathMode, host::DEFAULT_PORT, reach, stun};
|
||||
use horizon_hooks::{
|
||||
Hooks,
|
||||
@@ -25,13 +23,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, MutexGuard, PoisonError};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// The English title: the fallback of [`title`], and what the screen's widgets are named by.
|
||||
pub const TITLE: &str = "Play with Friends";
|
||||
/// The title in the current language.
|
||||
pub fn title() -> String {
|
||||
tr("horizon.friends.title", TITLE)
|
||||
}
|
||||
|
||||
struct Sharing {
|
||||
host: Host,
|
||||
/// Sharing opened the world to LAN, so stopping closes it again.
|
||||
@@ -106,6 +97,20 @@ pub struct Friends;
|
||||
pub static HOOKS: Friends = Friends;
|
||||
|
||||
impl Hooks for Friends {
|
||||
fn control_commands(&self) -> Option<(&'static str, &'static [&'static str])> {
|
||||
Some(("friends", HELP))
|
||||
}
|
||||
|
||||
/// `horizon-ctl friends host|stop|status`.
|
||||
fn control(
|
||||
&self,
|
||||
words: &[&str],
|
||||
world: &mut dyn ShareView,
|
||||
) -> Option<std::result::Result<String, String>> {
|
||||
(words.first() == Some(&"friends"))
|
||||
.then(|| control(words.get(1).copied(), world).map(|answer| answer.to_string()))
|
||||
}
|
||||
|
||||
/// What the page left for the game: chat lines, a button's press, the guests while it is
|
||||
/// open. With nothing left, one relaxed load.
|
||||
fn share(&self, world: &mut dyn ShareView) {
|
||||
@@ -541,9 +546,10 @@ fn control(
|
||||
Ok(json!({ "ok": true }))
|
||||
}
|
||||
Some("status") => {
|
||||
poll();
|
||||
say(world);
|
||||
// A splitscreen guest asking hears nothing of P1's sharing, and its chat stays its own.
|
||||
let view = if world.can_share() {
|
||||
poll();
|
||||
say(world);
|
||||
view(|| {
|
||||
let mut guests = Vec::new();
|
||||
world.players(&mut |name| guests.push(name.to_owned()));
|
||||
@@ -574,19 +580,6 @@ fn control(
|
||||
}
|
||||
}
|
||||
|
||||
impl Game<'_> {
|
||||
/// `horizon-ctl friends ...`.
|
||||
pub(super) fn friends_control(&mut self, words: &[&str]) -> Result<Option<serde_json::Value>> {
|
||||
if words[0] != "friends" {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(control(
|
||||
words.get(1).copied(),
|
||||
&mut self.share_view(),
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -650,6 +643,15 @@ mod tests {
|
||||
stop(&mut guest);
|
||||
copy(&mut guest);
|
||||
assert!(guest.did.is_empty(), "{:?}", guest.did);
|
||||
// horizon-ctl reaches the same through the hook; other commands aren't its.
|
||||
let status = HOOKS
|
||||
.control(&["friends", "status"], &mut guest)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let status: serde_json::Value = serde_json::from_str(&status).unwrap();
|
||||
assert_eq!(status["sharing"], false);
|
||||
assert!(HOOKS.control(&["lan", "status"], &mut guest).is_none());
|
||||
assert!(guest.did.is_empty(), "{:?}", guest.did);
|
||||
assert!(!ON.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
//! Play with Friends left out of the build (the `friends` feature off, as in the server build):
|
||||
//! no world is shared, the pause menu has no Friends button (`friends_shown` is false, as on
|
||||
//! another server) and `horizon-ctl friends` says it isn't there. game/friends.rs is the real one.
|
||||
use super::Game;
|
||||
use horizon_client::Result;
|
||||
|
||||
pub const HELP: &[&str] = &[];
|
||||
|
||||
/// The pause menu's Friends icon is vanilla's, drawn inactive: its name for the widget tree.
|
||||
pub const TITLE: &str = "Play with Friends";
|
||||
pub fn title() -> String {
|
||||
horizon_client::lang::get_or("horizon.friends.title", TITLE)
|
||||
}
|
||||
|
||||
impl Game<'_> {
|
||||
/// `horizon-ctl friends ...`.
|
||||
pub(super) fn friends_control(&mut self, words: &[&str]) -> Result<Option<serde_json::Value>> {
|
||||
if words[0] != "friends" {
|
||||
return Ok(None);
|
||||
}
|
||||
Err("this build has no Play with Friends (the `friends` feature)".into())
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,15 @@ fn friends_page() -> Option<&'static dyn Page> {
|
||||
.find(|page| page.place() == Place::PauseFriends)
|
||||
}
|
||||
|
||||
/// What the Friends icon and its screen are named, for its tooltip, the narrator, controller
|
||||
/// focus and the control socket's widget tree: the page's title, else vanilla's tooltip.
|
||||
pub(super) fn friends_name() -> String {
|
||||
friends_page().map_or_else(
|
||||
|| horizon_client::lang::get("gui.friends.open"),
|
||||
|page| horizon_client::lang::get(page.title()),
|
||||
)
|
||||
}
|
||||
|
||||
impl Game<'_> {
|
||||
/// Whether the pause menu's Friends icon is active: a page is registered for it and this
|
||||
/// window plays a singleplayer world (another server's world can't be shared).
|
||||
@@ -210,7 +219,7 @@ impl Game<'_> {
|
||||
}
|
||||
|
||||
/// The Friends icon's page. Its rows are asked each time it is drawn.
|
||||
pub(super) fn draw_friends_screen(&mut self, batch: &mut HudBatch) {
|
||||
pub(super) fn draw_added_page(&mut self, batch: &mut HudBatch) {
|
||||
let Some(page) = friends_page() else {
|
||||
// Only reached by naming the screen (`horizon-ctl`): back to the pause menu.
|
||||
self.screen = Screen::Paused;
|
||||
@@ -219,12 +228,12 @@ impl Game<'_> {
|
||||
let mut rows = Vec::new();
|
||||
page.rows(&mut rows);
|
||||
horizon_client::widgets::begin_screen(|| {
|
||||
horizon_client::widgets::ScreenInfo::new("friends", super::friends::TITLE)
|
||||
horizon_client::widgets::ScreenInfo::new("friends", friends_name())
|
||||
});
|
||||
draw(batch, &self.gui.atlas, self.cursor, page.title(), &rows);
|
||||
}
|
||||
|
||||
pub(super) fn friends_screen_click(&mut self) {
|
||||
pub(super) fn added_page_click(&mut self) {
|
||||
let Some(page) = friends_page() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Game<'_> {
|
||||
self.death_confirm_answer(false)?;
|
||||
}
|
||||
}
|
||||
Screen::Stats | Screen::Friends => self.screen = Screen::Paused,
|
||||
Screen::Stats | Screen::AddedPage => self.screen = Screen::Paused,
|
||||
Screen::Social => self.close_social(),
|
||||
Screen::ServerLinks => self.screen = Screen::Paused,
|
||||
// ConfirmScreen.keyPressed: Escape answers no; both return to
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Screen {
|
||||
| Self::Stats
|
||||
| Self::Advancements
|
||||
| Self::WorldOptions
|
||||
| Self::Friends
|
||||
| Self::AddedPage
|
||||
| Self::ServerLinks
|
||||
| Self::ConfirmLink
|
||||
| Self::DialogWarning
|
||||
@@ -92,7 +92,7 @@ impl Screen {
|
||||
| Self::Stats
|
||||
| Self::Advancements
|
||||
| Self::WorldOptions
|
||||
| Self::Friends
|
||||
| Self::AddedPage
|
||||
| Self::Social
|
||||
| Self::ServerLinks
|
||||
| Self::ConfirmLink
|
||||
@@ -191,8 +191,8 @@ fn pause_buttons(
|
||||
}
|
||||
|
||||
/// The icon row: report bugs, feedback, friends, player reporting.
|
||||
/// `FRIENDS_ICON` (vanilla's `CommonButtons.friends`) opens Horizon's
|
||||
/// Play with Friends; the first two open their link through
|
||||
/// `FRIENDS_ICON` (vanilla's `CommonButtons.friends`) opens the page registered at
|
||||
/// `Place::PauseFriends` (Play with Friends in the official build); the first two open their link through
|
||||
/// `ConfirmLinkScreen`; player reporting opens `SocialInteractionsScreen`.
|
||||
const FRIENDS_ICON: usize = 2;
|
||||
/// `CommonLinks.SNAPSHOT_BUGS_FEEDBACK`, behind `PauseScreen.REPORT_BUGS`.
|
||||
@@ -312,6 +312,7 @@ impl Game<'_> {
|
||||
}
|
||||
pub(super) fn draw_pause_menu(&mut self, batch: &mut HudBatch) {
|
||||
let friends = self.friends_shown();
|
||||
let friends_name = super::page_screen::friends_name();
|
||||
let atlas = &self.gui.atlas;
|
||||
let (w, h) = (batch.width, batch.height);
|
||||
ui::menu_background(batch, atlas, 0.0, 0.0, w, h, true);
|
||||
@@ -373,7 +374,7 @@ impl Game<'_> {
|
||||
// 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(super::friends::TITLE, [x, y, 20.0, 20.0], active);
|
||||
horizon_client::widgets::record(&friends_name, [x, y, 20.0, 20.0], active);
|
||||
}
|
||||
ui::button(
|
||||
batch,
|
||||
@@ -410,7 +411,7 @@ impl Game<'_> {
|
||||
batch.sprite_scaled(atlas, sprite, x + 3.0, y + 3.0, 15.0, 15.0, u32::MAX);
|
||||
if hover {
|
||||
tooltip = Some(if i == FRIENDS_ICON {
|
||||
Text::literal(super::friends::title())
|
||||
Text::literal(friends_name.clone())
|
||||
} else {
|
||||
Text::translate(ICON_LABELS[i], &[])
|
||||
});
|
||||
@@ -438,7 +439,7 @@ impl Game<'_> {
|
||||
3 => self.open_stats(),
|
||||
4 => self.open_advancements(true),
|
||||
9 => self.open_world_options(),
|
||||
10 => self.screen = Screen::Friends,
|
||||
10 => self.screen = Screen::AddedPage,
|
||||
// DialogConnectionAccess.openDialog(Dialogs.SERVER_LINKS).
|
||||
11 => {
|
||||
self.links_ui.scroll = 0.0;
|
||||
@@ -993,7 +994,7 @@ mod tests {
|
||||
Screen::Stats,
|
||||
Screen::Advancements,
|
||||
Screen::WorldOptions,
|
||||
Screen::Friends,
|
||||
Screen::AddedPage,
|
||||
Screen::ServerLinks,
|
||||
Screen::ConfirmLink,
|
||||
Screen::DialogWarning,
|
||||
|
||||
@@ -36,6 +36,18 @@ impl Game<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Hooks::control`: a `horizon-ctl` command a crate answers, with this window's world.
|
||||
pub(super) fn hooks_control(
|
||||
&mut self,
|
||||
words: &[&str],
|
||||
) -> horizon_client::Result<Option<serde_json::Value>> {
|
||||
match horizon_hooks::engine::control(words, &mut World(self)) {
|
||||
None => Ok(None),
|
||||
Some(Ok(json)) => Ok(Some(serde_json::from_str(&json)?)),
|
||||
Some(Err(why)) => Err(why.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// A singleplayer world runs on this window's server thread, so it can be shared, as Open
|
||||
/// to LAN publishes it; another server can't be.
|
||||
pub(super) fn can_share(&self) -> bool {
|
||||
@@ -43,15 +55,6 @@ impl Game<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'g> Game<'g> {
|
||||
/// The view of this window's world, for the engine's own Friends screen and command until
|
||||
/// they move to the crate.
|
||||
#[cfg(feature = "friends")]
|
||||
pub(super) fn share_view(&mut self) -> World<'_, 'g> {
|
||||
World(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct World<'a, 'g>(&'a mut Game<'g>);
|
||||
|
||||
impl ShareView for World<'_, '_> {
|
||||
|
||||
@@ -57,6 +57,23 @@ pub trait Hooks: Sync {
|
||||
/// The window's world stopped being open to LAN: Open to LAN's stop, or the player left the
|
||||
/// world. Called at that moment, by the window's own player only.
|
||||
fn lan_closed(&self) {}
|
||||
|
||||
/// The `horizon-ctl` commands this crate answers in the game: their group, as `hello` lists
|
||||
/// it, and the lines `help` adds for them.
|
||||
fn control_commands(&self) -> Option<(&'static str, &'static [&'static str])> {
|
||||
None
|
||||
}
|
||||
|
||||
/// A `horizon-ctl` command (its words) on the window's game: `Some` with the answer as JSON
|
||||
/// text, or why it failed, when it is this crate's. The first hook that answers wins; the
|
||||
/// hooks are asked before most of the engine's own commands.
|
||||
fn control(
|
||||
&self,
|
||||
_words: &[&str],
|
||||
_world: &mut dyn share::ShareView,
|
||||
) -> Option<Result<String, String>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// What [`Hooks::started`] gets.
|
||||
@@ -167,6 +184,19 @@ pub mod engine {
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Hooks::control`]: the first hook's answer.
|
||||
pub fn control(
|
||||
words: &[&str],
|
||||
world: &mut dyn share::ShareView,
|
||||
) -> Option<Result<String, String>> {
|
||||
all().iter().find_map(|hooks| hooks.control(words, world))
|
||||
}
|
||||
|
||||
/// [`Hooks::control_commands`] of every hook, in registration order.
|
||||
pub fn control_commands() -> impl Iterator<Item = (&'static str, &'static [&'static str])> {
|
||||
all().iter().filter_map(|hooks| hooks.control_commands())
|
||||
}
|
||||
|
||||
/// [`Hooks::tunnel`]: the first registered hook's resolver.
|
||||
pub fn tunnel() -> Option<&'static dyn tunnel::Resolver> {
|
||||
all().iter().find_map(|hooks| hooks.tunnel())
|
||||
|
||||
+2
-1
@@ -290,7 +290,8 @@ The hooks today: `started` (once, when someone plays: not a dedicated server, a
|
||||
`published` (the world opened to or closed from LAN or friends, on change), `shutdown` (the window closed, the
|
||||
world saved), `tunnel` (the resolver of `friend:` addresses, Play with Friends'), `share` (each client tick, the window's world
|
||||
through `share::ShareView`: open it to LAN, its players, chat, the clipboard) and `lan_closed` (it stopped being open
|
||||
to LAN). **A hook you need and don't find:** add a method with an empty default to `Hooks` and a function in
|
||||
to LAN), `control_commands` and `control` (the `horizon-ctl` commands a crate answers in the game, with that view).
|
||||
**A hook you need and don't find:** add a method with an empty default to `Hooks` and a function in
|
||||
`horizon_hooks::engine` that the engine calls where vanilla does the matching thing; give it plain data or a type
|
||||
the engine owns, never one from above. A hook that needs engine types the platform doesn't expose yet (a draw
|
||||
context for HUD and chat overlays, a settings page) comes with that type's platform API (M6). An extension point
|
||||
|
||||
@@ -59,7 +59,8 @@ game only through `horizon_hooks`: `Hooks::share` each client tick with a `share
|
||||
LAN, its players, chat, the clipboard, a link), `Hooks::lan_closed`, its page (`pages::Place::PauseFriends`, what
|
||||
the pause menu's Friends icon opens; a press runs on the next tick) and `tr`. It keeps what it shares in its own
|
||||
statics. It still lives in horizon-client (`game/friends.rs`, the `friends` feature, which comes with
|
||||
`horizon-extras`) until `horizon-ctl` offers commands to crates above the engine (#14167).
|
||||
`horizon-extras`), and answers `horizon-ctl friends` through `Hooks::control`, until it moves to horizon-friends
|
||||
(#14167).
|
||||
|
||||
**Codes.** Crockford base32 (`0-9 A-Z` without `I L O U`; input ignores case, spaces and dashes and reads
|
||||
`O` as `0`, `I`/`L` as `1`) in groups of five, with a 16-bit checksum so a typo is caught before
|
||||
|
||||
Reference in new issue
Block a user