mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-09 02:29:50 -10:00
Fix the eight hook issues from Dyna's review of #6801
- A cancelled book edit resends the server's book to the client. - Snow filling a cauldron reports level 1, the level it places. - fluid_level_change also sees (and can cancel) a flow drying up to 0. - A cost changed by prepare_item_enchant shows that cost's clue. - raid_spawn_wave's doc says it fires after the wave, without mounts. - A cancelled item cooldown ends the guest's predicted cooldown. - Anvil, grindstone and smithing hooks run only when a click changed the inputs, the result or the cost. - A crafting result set by prepare_item_craft is sent to the client. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
3552e9d1a4
commit
2e63a668e2
10 files changed
+106
-38
No files matched your search
@@ -6116,6 +6116,9 @@ impl Game<'_> {
|
||||
}
|
||||
if !cancel {
|
||||
self.add_cooldown(group, component.ticks());
|
||||
} else if self.lan_acting.is_some() {
|
||||
// The guest's client started the cooldown itself; 0 ends it there.
|
||||
self.guest_cooldowns.push((group, 0));
|
||||
}
|
||||
}
|
||||
/// `ItemCooldowns.addCooldown` (a guest also gets the packet).
|
||||
|
||||
@@ -334,7 +334,12 @@ impl Game<'_> {
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.as_guest(host, conn, |g| {
|
||||
let menu_slot = if p.slot == super::shield::OFFHAND_SLOT as i32 {
|
||||
45
|
||||
} else {
|
||||
36 + p.slot as usize
|
||||
};
|
||||
let cancelled = self.as_guest(host, conn, |g| {
|
||||
let mut cancel = false;
|
||||
let writable = g
|
||||
.stack_in(p.slot as usize)
|
||||
@@ -351,10 +356,18 @@ impl Game<'_> {
|
||||
);
|
||||
}
|
||||
if cancel {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
g.write_book(p.slot as usize, p.pages, p.title)
|
||||
g.write_book(p.slot as usize, p.pages, p.title);
|
||||
false
|
||||
});
|
||||
// A cancelled edit leaves the client's own copy of the book: forget
|
||||
// what was sent so the next broadcast resends the server's stack.
|
||||
if cancelled == Some(true)
|
||||
&& let Some(g) = host.hub.guests.iter_mut().find(|g| g.conn == conn)
|
||||
{
|
||||
g.menu_sent[menu_slot] = None;
|
||||
}
|
||||
}
|
||||
pub(super) fn book_key(&mut self, event: &KeyEvent) {
|
||||
if event.state != ElementState::Pressed {
|
||||
|
||||
@@ -528,7 +528,7 @@ impl Game<'_> {
|
||||
&& !host.hub.guests[gi].spectator()
|
||||
&& self.lan_table_valid(&host.hub.guests[gi], pos)
|
||||
{
|
||||
let changed = self.guest_menu_op(host, gi, &mut menu, |menu, env| {
|
||||
let mut changed = self.guest_menu_op(host, gi, &mut menu, |menu, env| {
|
||||
let before: Vec<_> = (0..menu.slot_count())
|
||||
.map(|s| menu.slot(s).cloned())
|
||||
.collect();
|
||||
@@ -540,7 +540,11 @@ impl Game<'_> {
|
||||
let guest = &host.hub.guests[gi];
|
||||
let (uuid, name) = (guest.uuid, guest.name.clone());
|
||||
let mut result = menu.slot(0).cloned();
|
||||
let vanilla = result.clone();
|
||||
self.native_prepare_item_craft(uuid, &name, Some(pos), &mut result);
|
||||
if result != vanilla && !changed.contains(&0) {
|
||||
changed.push(0);
|
||||
}
|
||||
let env = self.menu_env();
|
||||
menu.set_slot(&env, 0, result);
|
||||
agreed = p.state == state
|
||||
|
||||
@@ -125,8 +125,26 @@ impl Game<'_> {
|
||||
if let Some(gi) = host.hub.guests.iter().position(|g| g.conn == conn)
|
||||
&& !host.hub.guests[gi].spectator()
|
||||
{
|
||||
// createResult runs when the inputs change (or a rename): a click
|
||||
// elsewhere must not hand the hooks their own result again.
|
||||
let result_slot = match gs.kind {
|
||||
StationKind::Anvil | StationKind::Grindstone => Some(2),
|
||||
StationKind::Smithing => Some(3),
|
||||
_ => None,
|
||||
};
|
||||
let before = result_slot.map(|r| {
|
||||
let slots: Vec<_> = (0..=r).map(|s| gs.menu.slot(s).cloned()).collect();
|
||||
(slots, gs.menu.data[0])
|
||||
});
|
||||
self.guest_menu_op(host, gi, &mut gs.menu, op);
|
||||
if gs.kind == StationKind::Anvil {
|
||||
let recomputed = before.is_some_and(|(slots, cost)| {
|
||||
cost != gs.menu.data[0]
|
||||
|| slots
|
||||
.iter()
|
||||
.enumerate()
|
||||
.any(|(s, b)| gs.menu.slot(s) != b.as_ref())
|
||||
});
|
||||
if recomputed && gs.kind == StationKind::Anvil {
|
||||
let guest = &host.hub.guests[gi];
|
||||
let (uuid, name) = (guest.uuid, guest.name.clone());
|
||||
let mut result = gs.menu.slot(2).cloned();
|
||||
@@ -134,7 +152,7 @@ impl Game<'_> {
|
||||
let env = self.menu_env();
|
||||
gs.menu.set_slot(&env, 2, result);
|
||||
}
|
||||
if gs.kind == StationKind::Grindstone {
|
||||
if recomputed && gs.kind == StationKind::Grindstone {
|
||||
let guest = &host.hub.guests[gi];
|
||||
let (uuid, name) = (guest.uuid, guest.name.clone());
|
||||
let mut result = gs.menu.slot(2).cloned();
|
||||
@@ -142,7 +160,7 @@ impl Game<'_> {
|
||||
let env = self.menu_env();
|
||||
gs.menu.set_slot(&env, 2, result);
|
||||
}
|
||||
if gs.kind == StationKind::Smithing {
|
||||
if recomputed && gs.kind == StationKind::Smithing {
|
||||
let guest = &host.hub.guests[gi];
|
||||
let (uuid, name) = (guest.uuid, guest.name.clone());
|
||||
let mut result = gs.menu.slot(3).cloned();
|
||||
@@ -320,9 +338,25 @@ impl Game<'_> {
|
||||
let guest = &host.hub.guests[gi];
|
||||
let mut costs = offers.costs;
|
||||
self.native_prepare_item_enchant(guest.uuid, &guest.name, gs.pos, &mut costs);
|
||||
let mut clues = offers.clues;
|
||||
for i in 0..3 {
|
||||
// A cost a mod changed shows the clue of the list it will give.
|
||||
if costs[i] != offers.costs[i]
|
||||
&& let Some(stack) = gs.menu.slot(0)
|
||||
{
|
||||
clues[i] = horizon_client::stations::enchant_clue(
|
||||
&self.stations().enchantments,
|
||||
self.items(),
|
||||
seed,
|
||||
i,
|
||||
costs[i],
|
||||
stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
for i in 0..3 {
|
||||
gs.menu.data[i] = costs[i];
|
||||
let clue = offers.clues[i].as_ref();
|
||||
let clue = clues[i].as_ref();
|
||||
gs.menu.data[4 + i] = clue
|
||||
.and_then(|(e, _)| host.hub.registry_id("minecraft:enchantment", e))
|
||||
.unwrap_or(-1);
|
||||
|
||||
@@ -4439,8 +4439,8 @@ impl Game<'_> {
|
||||
mod_api::dispatch_portal_create(self, mod_api::mods(), dimension, pos);
|
||||
}
|
||||
|
||||
/// A raid wave is about to be spawned at `pos`; `wave` is its one-based
|
||||
/// wave number and `raiders` is the count Horizon will spawn. @API:native_raid_spawn_wave@.
|
||||
/// A raid wave has been spawned at `pos`; `wave` is its one-based wave
|
||||
/// number and `raiders` counts the raiders, not the mobs they ride. @API:native_raid_spawn_wave@.
|
||||
pub(super) fn native_raid_spawn_wave(
|
||||
&mut self,
|
||||
raid: i32,
|
||||
|
||||
+2
-2
@@ -3915,8 +3915,8 @@ pub trait Hooks: Send + Sync {
|
||||
let _ = host;
|
||||
let _ = event;
|
||||
}
|
||||
/// A raid wave is about to be spawned at `pos`; `wave` is its one-based
|
||||
/// wave number and `raiders` is the count Horizon will spawn. @API:native_raid_spawn_wave@.
|
||||
/// A raid wave has been spawned at `pos`; `wave` is its one-based wave
|
||||
/// number and `raiders` counts the raiders, not the mobs they ride. @API:native_raid_spawn_wave@.
|
||||
fn raid_spawn_wave(&self, host: &mut dyn Host, event: &mut RaidSpawnWaveEvent) {
|
||||
let _ = host;
|
||||
let _ = event;
|
||||
|
||||
@@ -100,23 +100,37 @@ pub fn enchant_offers(
|
||||
}
|
||||
}
|
||||
for i in 0..3 {
|
||||
if offers.costs[i] > 0 {
|
||||
// getEnchantmentList reseeds the menu random; the clue draws continue from it.
|
||||
let mut list_random = LegacyRandom::new(i64::from(seed.wrapping_add(i as i32)));
|
||||
let pool = e.tag("minecraft:in_enchanting_table").to_vec();
|
||||
let mut list = e.select(items, &mut list_random, &stack.item, offers.costs[i], &pool);
|
||||
if &*stack.item == "minecraft:book" && list.len() > 1 {
|
||||
let r = list_random.next_int_bound(list.len() as i32).unwrap_or(0) as usize;
|
||||
list.remove(r);
|
||||
}
|
||||
if !list.is_empty() {
|
||||
let pick = list_random.next_int_bound(list.len() as i32).unwrap_or(0) as usize;
|
||||
offers.clues[i] = Some(list[pick].clone());
|
||||
}
|
||||
}
|
||||
offers.clues[i] = enchant_clue(e, items, seed, i, offers.costs[i], stack);
|
||||
}
|
||||
offers
|
||||
}
|
||||
/// `EnchantmentMenu.slotsChanged`'s clue for offer `slot` at `cost`: one
|
||||
/// enchantment of the list that cost would give, or None for no offer.
|
||||
pub fn enchant_clue(
|
||||
e: &Enchantments,
|
||||
items: &Items,
|
||||
seed: i32,
|
||||
slot: usize,
|
||||
cost: i32,
|
||||
stack: &ItemStack,
|
||||
) -> Option<(Arc<str>, u32)> {
|
||||
if cost <= 0 {
|
||||
return None;
|
||||
}
|
||||
// getEnchantmentList reseeds the menu random; the clue draws continue from it.
|
||||
let mut list_random = LegacyRandom::new(i64::from(seed.wrapping_add(slot as i32)));
|
||||
let pool = e.tag("minecraft:in_enchanting_table").to_vec();
|
||||
let mut list = e.select(items, &mut list_random, &stack.item, cost, &pool);
|
||||
if &*stack.item == "minecraft:book" && list.len() > 1 {
|
||||
let r = list_random.next_int_bound(list.len() as i32).unwrap_or(0) as usize;
|
||||
list.remove(r);
|
||||
}
|
||||
if list.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let pick = list_random.next_int_bound(list.len() as i32).unwrap_or(0) as usize;
|
||||
Some(list[pick].clone())
|
||||
}
|
||||
/// `EnchantmentMenu.clickMenuButton` result: the enchanted item, or None.
|
||||
pub fn apply_enchantment(
|
||||
e: &Enchantments,
|
||||
|
||||
@@ -617,11 +617,7 @@ impl<L: LevelAccess + ?Sized> Sim<'_, L> {
|
||||
if !fluid.is_source() {
|
||||
let new_fluid = self.new_liquid(kind, pos, state);
|
||||
let delay = self.spread_delay(kind, pos, fluid, new_fluid);
|
||||
if new_fluid.kind() == FluidKind::Empty {
|
||||
fluid = new_fluid;
|
||||
state = self.b.air();
|
||||
self.set_block_and_update(pos, state);
|
||||
} else if new_fluid != fluid {
|
||||
if new_fluid != fluid {
|
||||
let name = match kind {
|
||||
FluidKind::Water | FluidKind::FlowingWater => "water",
|
||||
FluidKind::Lava | FluidKind::FlowingLava => "lava",
|
||||
@@ -635,6 +631,12 @@ impl<L: LevelAccess + ?Sized> Sim<'_, L> {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if new_fluid.kind() == FluidKind::Empty {
|
||||
fluid = new_fluid;
|
||||
state = self.b.air();
|
||||
self.set_block_and_update(pos, state);
|
||||
} else if new_fluid != fluid {
|
||||
fluid = new_fluid;
|
||||
state = self.b.legacy_block(fluid);
|
||||
self.set_block_and_update(pos, state);
|
||||
|
||||
@@ -1488,11 +1488,9 @@ impl<L: LevelAccess + ?Sized> Sim<'_, L> {
|
||||
};
|
||||
if data.is(Class::Cauldron) {
|
||||
if self.random().next_float() < chance {
|
||||
let to = if info.kind == PrecipitationKind::Rain {
|
||||
1
|
||||
} else {
|
||||
3
|
||||
};
|
||||
// Both place the cauldron's default state, level 1
|
||||
// (CauldronBlock.handlePrecipitation).
|
||||
let to = 1;
|
||||
let reason = if info.kind == PrecipitationKind::Rain {
|
||||
"rain"
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"rust_summary": "A raid wave about to spawn (`native_raid_spawn_wave`).",
|
||||
"doc_summary": "A raid wave is about to spawn at its position, with its raid id, wave number and raider count. Bundles need \"hooks\": @API:raid_spawn_wave@."
|
||||
"rust_summary": "A raid wave that has spawned (`native_raid_spawn_wave`).",
|
||||
"doc_summary": "A raid wave has spawned at its position, with its raid id, wave number and raider count (riders' mounts not counted). Bundles need \"hooks\": @API:raid_spawn_wave@."
|
||||
}
|
||||
Reference in new issue
Block a user