Merge #6520 into the hook stack

This commit is contained in:
Ludovic Marie committed 2026-11-02 07:41:16 +01:00
commit 509a5e82c5
5 files changed
+93

No files matched your search

@@ -1784,6 +1784,25 @@ impl Game<'_> {
// SWAP_ITEM_WITH_OFFHAND: the hands swap and the item in use stops
// (ServerGamePacketListenerImpl.handlePlayerAction), not for spectators.
7 if !spectator => {
// The mods hear the swap first, and may stop it: the
// client, which swapped at once, gets its slots back.
let (uuid, name) = {
let g = &host.hub.guests[gi];
(g.uuid, g.name.clone())
};
// With the player's state swapped in, so that the helpers
// reach them.
let cancel = self
.as_guest(host, conn, |g| {
let mut cancel = false;
g.native_player_swap_hands(uuid, &name, &mut cancel);
cancel
})
.unwrap_or(false);
if cancel {
host.hub.send_inventory(gi);
return;
}
let g = &mut host.hub.guests[gi];
let slot = g.selected;
std::mem::swap(&mut g.inventory[slot], &mut g.offhand);
@@ -3340,6 +3340,19 @@ impl Game<'_> {
previous,
);
}
/// `player` (`name`) pressed the swap-hands key: heard before the main and
/// off hand items swap (the `SWAP_ITEM_WITH_OFFHAND` player action that
/// `ServerGamePacketListenerImpl.handlePlayerAction` answers; Bukkit's
/// `PlayerSwapHandItemsEvent`), not for a spectator. Set `cancel` to keep
/// the items where they are: the player's client, which swapped them at
/// once, gets its slots back. The player's state is swapped in, so
/// `native_acting_player`, `native_tell` and the item helpers reach them.
/// Once for each press, on a server. Not heard: the same swap made in an
/// open inventory (hovering a slot and pressing the key, a menu click) and
/// the host of a LAN world. @API:native_player_swap_hands@.
pub(super) fn native_player_swap_hands(&mut self, player: u128, name: &str, cancel: &mut bool) {
mod_api::dispatch_player_swap_hands(self, mod_api::mods(), player, name, cancel);
}
/// `player` (`name`) opened a menu (NeoForge's `PlayerContainerEvent.Open`,
/// posted when `ServerPlayer.openMenu` sets the new `containerMenu`): any
/// menu, vanilla or a mod's. `menu` is the menu type's name
+41
View File
@@ -2656,6 +2656,23 @@ pub trait Hooks: Send + Sync {
let _ = event;
Flow::Continue
}
/// `player` (`name`) pressed the swap-hands key: heard before the main and
/// off hand items swap (the `SWAP_ITEM_WITH_OFFHAND` player action that
/// `ServerGamePacketListenerImpl.handlePlayerAction` answers; Bukkit's
/// `PlayerSwapHandItemsEvent`), not for a spectator. Set `cancel` to keep
/// the items where they are: the player's client, which swapped them at
/// once, gets its slots back. The player's state is swapped in, so
/// `native_acting_player`, `native_tell` and the item helpers reach them.
/// Once for each press, on a server. Not heard: the same swap made in an
/// open inventory (hovering a slot and pressing the key, a menu click) and
/// the host of a LAN world. @API:native_player_swap_hands@.
///
/// Return `Flow::Cancel` to take it: this sets `cancel`, and later mods are skipped.
fn player_swap_hands(&self, host: &mut dyn Host, event: &mut PlayerSwapHandsEvent<'_>) -> Flow {
let _ = host;
let _ = event;
Flow::Continue
}
}
/// What [`Hooks::frame`] is told. Fields may be added: the struct is `#[non_exhaustive]`.
#[non_exhaustive]
@@ -7328,3 +7345,27 @@ pub fn dispatch_player_game_mode_changed(
}
}
}
/// What [`Hooks::player_swap_hands`] is told. Fields may be added: the struct is `#[non_exhaustive]`.
#[non_exhaustive]
pub struct PlayerSwapHandsEvent<'a> {
pub player: u128,
pub name: &'a str,
}
pub fn dispatch_player_swap_hands(
host: &mut dyn Host,
mods: &[Box<dyn Hooks>],
player: u128,
name: &str,
cancel: &mut bool,
) {
if *cancel {
return;
}
let mut event = PlayerSwapHandsEvent { player, name };
for mod_hook in mods {
if matches!(mod_hook.player_swap_hands(host, &mut event), Flow::Cancel) {
*cancel = true;
break;
}
}
}
+4
View File
@@ -1305,6 +1305,10 @@ For mods that run where the world is simulated: a dedicated server, a LAN host,
their level allows it, whether the world allows commands or not. The mod parses the line and sets `ran`. The
player's state is swapped in for the call, as for their other actions, so `native_player` reads them; a
registered command no mod takes answers as vanilla's unknown command.
- **`native_player_swap_hands(player, name, cancel)`**: a player pressed the swap-hands key (not a spectator), heard
before the main and off hand items swap. Set `cancel` and they stay where they are; the player's client, which swapped
them at once, gets its slots back. The player's state is swapped in, so `native_acting_player`, `native_tell` and the item
helpers reach them. Not heard: the same swap from an open inventory (hovering a slot and pressing the key) and the host of a LAN world.
- Helpers: `native_server_players()` (uuid, name, level, dimension, position and game mode of each player on);
`native_tell(uuid, text)`, a system line to one player, also the acting player inside their hook;
`native_broadcast(text)`, to every player and the server's
@@ -0,0 +1,16 @@
{
"hooks": [
{
"name": "player_swap_hands",
"target": "horizon_client::game::native_hooks::Game<'_>::native_player_swap_hands",
"neoforge": "LivingSwapItemsEvent.Hands",
"side": "server",
"mod_api": {
"contract": "cancel",
"out": "cancel"
}
}
],
"rust_summary": "a player swapping their hands, cancellable (`native_player_swap_hands`).",
"doc_summary": "a player swapping their main and off hand items: `native_player_swap_hands(player, name, cancel)` is heard before the swap (Bukkit's `PlayerSwapHandItemsEvent`; a swap from an open inventory is not heard), for a player of a server who is not a spectator, with their state swapped in. A mod sets `cancel` to keep the items where they are; the player's client gets its slots back. Bundles that use it need `\"hooks\": @API:native_player_swap_hands@`."
}