mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
fix(mods): count the host's own player and golems as container viewers
The count now runs in tick_world, so it also runs in-frame and without LAN. Fixes #6077 Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
fcbfd000ee
commit
74a8a866f8
4 files changed
+70
-52
No files matched your search
@@ -7317,6 +7317,7 @@ impl Game<'_> {
|
||||
if running {
|
||||
horizon_client::spans::time("tick_mobs", || self.tick_mobs())?;
|
||||
horizon_client::spans::time("recheck_golem_openers", || self.recheck_golem_openers());
|
||||
self.tick_mod_container_viewers();
|
||||
let before = self.orb_ids();
|
||||
horizon_client::spans::time("tick_experience", || self.tick_experience());
|
||||
// Merged, despawned or picked up.
|
||||
|
||||
@@ -32,37 +32,8 @@ use horizon_protocol::{
|
||||
text::literal,
|
||||
};
|
||||
use horizon_world::entity_move::EntityMoveWorld;
|
||||
use std::collections::HashMap;
|
||||
use winit::keyboard::KeyCode;
|
||||
|
||||
/// Sets the viewer counts `known` holds for `dim` to `now` and returns the
|
||||
/// changes as `(pos, previous, viewers)`, in position order. A container that
|
||||
/// is no longer counted goes to 0 and leaves `known`.
|
||||
pub(super) fn viewer_changes(
|
||||
known: &mut HashMap<(&'static str, (i32, i32, i32)), u32>,
|
||||
dim: &'static str,
|
||||
mut now: HashMap<(i32, i32, i32), u32>,
|
||||
) -> Vec<((i32, i32, i32), u32, u32)> {
|
||||
let mut changes = Vec::new();
|
||||
known.retain(|&(d, pos), previous| {
|
||||
if d != dim {
|
||||
return true;
|
||||
}
|
||||
let viewers = now.remove(&pos).unwrap_or(0);
|
||||
if viewers != *previous {
|
||||
changes.push((pos, *previous, viewers));
|
||||
*previous = viewers;
|
||||
}
|
||||
viewers > 0
|
||||
});
|
||||
for (pos, viewers) in now {
|
||||
known.insert((dim, pos), viewers);
|
||||
changes.push((pos, 0, viewers));
|
||||
}
|
||||
changes.sort_unstable();
|
||||
changes
|
||||
}
|
||||
|
||||
/// Per-guest state of the gameplay code that lives outside `Guest`.
|
||||
#[derive(Default)]
|
||||
pub(super) struct GuestPlay {
|
||||
@@ -1395,27 +1366,6 @@ impl Game<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `ContainerOpenersCounter` for the mod containers of the level in place:
|
||||
/// the players with each one open, recounted from their menus, so a
|
||||
/// change is heard whichever way a player left (`native_mod_container_viewers`).
|
||||
fn tick_mod_container_viewers(&mut self, host: &LanHost) {
|
||||
let mut now: HashMap<(i32, i32, i32), u32> = HashMap::new();
|
||||
for g in host.hub.guests.iter().filter(|g| host.hub.here(g)) {
|
||||
// `BarrelBlockEntity.startOpen`: a spectator does not open it.
|
||||
if !g.spectator()
|
||||
&& let Some(m) = &g.menu
|
||||
&& let Slots::Block(pos) = m.slots
|
||||
&& self.mod_menu_at(pos).is_some()
|
||||
{
|
||||
*now.entry(pos).or_default() += 1;
|
||||
}
|
||||
}
|
||||
let dim = self.dim.id();
|
||||
for (pos, previous, viewers) in viewer_changes(&mut self.mod_container_viewers, dim, now) {
|
||||
self.native_mod_container_viewers(dim, [pos.0, pos.1, pos.2], viewers, previous);
|
||||
}
|
||||
}
|
||||
|
||||
/// `stillValid` of the guest's open container: its blocks are still there
|
||||
/// and in reach, or its vehicle exists and is in reach. True with none open.
|
||||
fn container_menu_valid(&self, g: &horizon_client::lan::guest::Guest) -> bool {
|
||||
@@ -3305,7 +3255,6 @@ impl Game<'_> {
|
||||
self.lan_stations_tick(host);
|
||||
self.lan_tables_tick(host);
|
||||
self.lan_containers_tick(host);
|
||||
self.tick_mod_container_viewers(host);
|
||||
self.tick_lan_mount_menus(host);
|
||||
self.announce_guest_advancements(host);
|
||||
let conns: Vec<u32> = host
|
||||
|
||||
@@ -296,6 +296,7 @@ use horizon_client::{
|
||||
hud::{HudBatch, rgba},
|
||||
items::ItemStack,
|
||||
keybinds,
|
||||
lan::menu::Slots,
|
||||
mobs::{Damage, Who},
|
||||
mod_list::{self, Installed, NativePage},
|
||||
music::{Music, MusicLayer},
|
||||
@@ -315,6 +316,7 @@ use horizon_world::{
|
||||
registry::{BlockRegistry, BlockStateId},
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
@@ -2905,6 +2907,34 @@ fn sound_id(id: &str) -> bool {
|
||||
&& path.chars().all(|c| allowed(c, true))
|
||||
}
|
||||
|
||||
/// Sets the viewer counts `known` holds for `dim` to `now` and returns the
|
||||
/// changes as `(pos, previous, viewers)`, in position order. A container that
|
||||
/// is no longer counted goes to 0 and leaves `known`.
|
||||
pub(super) fn viewer_changes(
|
||||
known: &mut HashMap<(&'static str, (i32, i32, i32)), u32>,
|
||||
dim: &'static str,
|
||||
mut now: HashMap<(i32, i32, i32), u32>,
|
||||
) -> Vec<((i32, i32, i32), u32, u32)> {
|
||||
let mut changes = Vec::new();
|
||||
known.retain(|&(d, pos), previous| {
|
||||
if d != dim {
|
||||
return true;
|
||||
}
|
||||
let viewers = now.remove(&pos).unwrap_or(0);
|
||||
if viewers != *previous {
|
||||
changes.push((pos, *previous, viewers));
|
||||
*previous = viewers;
|
||||
}
|
||||
viewers > 0
|
||||
});
|
||||
for (pos, viewers) in now {
|
||||
known.insert((dim, pos), viewers);
|
||||
changes.push((pos, 0, viewers));
|
||||
}
|
||||
changes.sort_unstable();
|
||||
changes
|
||||
}
|
||||
|
||||
fn dimension_id(dimension: LevelDimension) -> &'static str {
|
||||
match dimension {
|
||||
LevelDimension::Overworld => "minecraft:overworld",
|
||||
@@ -4311,6 +4341,44 @@ impl Game<'_> {
|
||||
}
|
||||
true
|
||||
}
|
||||
/// `ContainerOpenersCounter` for the mod containers of the level in place:
|
||||
/// everyone with one open, recounted every tick, so a change is heard
|
||||
/// whichever way an opener left (`native_mod_container_viewers`). The
|
||||
/// openers are the guests (but not a spectator, `BarrelBlockEntity.startOpen`),
|
||||
/// the host's own player, and the copper golems that opened it
|
||||
/// (`chest_openers`).
|
||||
pub(super) fn tick_mod_container_viewers(&mut self) {
|
||||
let mut now: HashMap<(i32, i32, i32), u32> = HashMap::new();
|
||||
let mut open = |game: &Self, pos: (i32, i32, i32), n: u32| {
|
||||
if n > 0 && game.mod_menu_at(pos).is_some() {
|
||||
*now.entry(pos).or_default() += n;
|
||||
}
|
||||
};
|
||||
if let Some(host) = &self.lan {
|
||||
for g in host
|
||||
.hub
|
||||
.guests
|
||||
.iter()
|
||||
.filter(|g| host.hub.here(g) && !g.spectator())
|
||||
{
|
||||
if let Some(m) = &g.menu
|
||||
&& let Slots::Block(pos) = m.slots
|
||||
{
|
||||
open(self, pos, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(pos) = self.container.filter(|_| !self.spectator()) {
|
||||
open(self, pos, 1);
|
||||
}
|
||||
for (&pos, &(golems, _)) in &self.world.chest_openers {
|
||||
open(self, pos, golems.max(0) as u32);
|
||||
}
|
||||
let dim = self.dim.id();
|
||||
for (pos, previous, viewers) in viewer_changes(&mut self.mod_container_viewers, dim, now) {
|
||||
self.native_mod_container_viewers(dim, [pos.0, pos.1, pos.2], viewers, previous);
|
||||
}
|
||||
}
|
||||
/// The menu type of the mod container at `key`, if it has one.
|
||||
pub(super) fn mod_menu_at(&self, key: Pos) -> Option<&'static str> {
|
||||
let state = self.world.block(key.0, key.1, key.2).0;
|
||||
|
||||
@@ -11,7 +11,7 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
class ModContainerViewersTests(unittest.TestCase):
|
||||
@unittest.skipUnless(shutil.which("rustc"), "requires Rust")
|
||||
def test_opens_second_viewer_close_and_leaving_without_closing(self):
|
||||
source = (ROOT / "crates/horizon-client/src/game/lan_actions.rs").read_text(encoding="utf-8")
|
||||
source = (ROOT / "crates/horizon-client/src/game/native_hooks.rs").read_text(encoding="utf-8")
|
||||
start = source.index("pub(super) fn viewer_changes(")
|
||||
end = source.index("\n}\n", start) + 3
|
||||
function = source[start:end].replace("pub(super) fn", "fn")
|
||||
|
||||
Reference in new issue
Block a user