Skip to content

Commit 5400609

Browse files
committed
refactor(tag): use OnSet observer for inventory updates
Replace PreStore system with OnSet observer for handling inventory updates on level-up: - Remove manual XP delta checking since OnSet handles this automatically - Simplify system signature to only required components - Add explicit entity modification tracking for XP changes - Clean up related component naming (`Rank` → `Class`) for consistency This change provides more efficient and idiomatic handling of inventory updates by leveraging the ECS observer pattern instead of manual diff checking.
1 parent 0bf0af7 commit 5400609

File tree

8 files changed

+210
-223
lines changed

8 files changed

+210
-223
lines changed

crates/hyperion-rank-tree/src/inventory.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use hyperion_inventory::PlayerInventory;
33
use hyperion_item::builder::{AttackDamage, BookBuilder, Color, ItemBuilder};
44
use valence_protocol::ItemKind;
55

6-
use crate::{Handles, Rank, Team};
6+
use crate::{Class, Handles, Team};
77

88
impl Team {
99
pub const fn build_item(self) -> ItemBuilder {
@@ -25,7 +25,7 @@ pub const UPGRADE_START_SLOT: u16 = 3;
2525
pub const GUI_SLOT: u16 = 7;
2626
pub const HELP_SLOT: u16 = 8;
2727

28-
impl Rank {
28+
impl Class {
2929
pub fn apply_inventory(
3030
self,
3131
team: Team,

crates/hyperion-rank-tree/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub mod skin;
1414

1515
#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, Component, Default)]
1616
#[repr(C)]
17-
pub enum Rank {
17+
pub enum Class {
1818
/// ![Widget Example](https://i.imgur.com/pW7v0Xn.png)
1919
///
2020
/// The stick is the starting rank.
@@ -55,7 +55,7 @@ impl Module for RankTree {
5555
fn module(world: &World) {
5656
world.import::<hyperion_item::ItemModule>();
5757
world.component::<Team>();
58-
world.component::<Rank>();
58+
world.component::<Class>();
5959
world.component::<Handles>();
6060

6161
world
@@ -64,7 +64,7 @@ impl Module for RankTree {
6464

6565
world
6666
.component::<Player>()
67-
.add_trait::<(flecs::With, Rank)>();
67+
.add_trait::<(flecs::With, Class)>();
6868

6969
let handler: EventFn<ClickEvent> = |query, _| {
7070
let cursor = query.inventory.get_cursor();

crates/hyperion-rank-tree/src/skin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::LazyLock;
22

33
use hyperion::simulation::skin::PlayerSkin;
44

5-
use crate::Rank;
5+
use crate::Class;
66

77
macro_rules! define_skin {
88
($name:ident, $path:literal) => {
@@ -22,7 +22,7 @@ define_skin!(BUILDER_SKIN, "skin/builder.toml");
2222
define_skin!(MINER_SKIN, "skin/miner.toml");
2323
define_skin!(EXCAVATOR_SKIN, "skin/excavator.toml");
2424

25-
impl Rank {
25+
impl Class {
2626
#[must_use]
2727
pub fn skin(&self) -> &'static PlayerSkin {
2828
match self {

events/tag/src/command.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use flecs_ecs::core::World;
22
use hyperion_clap::{MinecraftCommand, hyperion_command::CommandRegistry};
33

44
use crate::command::{
5-
dirt::DirtCommand, fly::FlyCommand, rank::ClassCommand, replace::ReplaceCommand,
5+
class::ClassCommand, dirt::DirtCommand, fly::FlyCommand, replace::ReplaceCommand,
66
speed::SpeedCommand, xp::XpCommand,
77
};
88

9+
mod class;
910
mod dirt;
1011
mod fly;
11-
mod rank;
1212
mod replace;
1313
mod speed;
1414
mod xp;

events/tag/src/command/class.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use std::borrow::Cow;
2+
3+
use clap::Parser;
4+
use flecs_ecs::core::{Entity, EntityViewGet, World, WorldGet};
5+
use hyperion::{
6+
egress::{
7+
metadata::show_all,
8+
player_join::{PlayerListActions, PlayerListEntry, PlayerListS2c},
9+
},
10+
net::{Compose, DataBundle, NetworkStreamRef, agnostic},
11+
simulation::{Pitch, Position, Yaw},
12+
system_registry::SystemId,
13+
valence_ident::ident,
14+
valence_protocol::{
15+
BlockPos, GameMode, VarInt,
16+
game_mode::OptGameMode,
17+
packets::{
18+
play,
19+
play::{PlayerRespawnS2c, player_position_look_s2c::PlayerPositionLookFlags},
20+
},
21+
profile::Property,
22+
},
23+
};
24+
use hyperion_clap::{CommandPermission, MinecraftCommand};
25+
use hyperion_rank_tree::{Class, Team};
26+
use hyperion_utils::EntityExt;
27+
28+
#[derive(Parser, CommandPermission, Debug)]
29+
#[command(name = "class")]
30+
#[command_permission(group = "Normal")]
31+
pub struct ClassCommand {
32+
class: Class,
33+
team: Team,
34+
}
35+
impl MinecraftCommand for ClassCommand {
36+
fn execute(self, world: &World, caller: Entity) {
37+
let class_param = self.class;
38+
let team_param = self.team;
39+
40+
world.get::<&Compose>(|compose| {
41+
let caller = caller.entity_view(world);
42+
caller.get::<(
43+
&NetworkStreamRef,
44+
&hyperion::simulation::Uuid,
45+
&Position,
46+
&Yaw,
47+
&Pitch,
48+
&mut Team,
49+
&mut Class,
50+
)>(|(stream, uuid, position, yaw, pitch, team, class)| {
51+
if *team != team_param {
52+
*team = team_param;
53+
caller.modified::<Team>();
54+
}
55+
56+
if *class != class_param {
57+
*class = class_param;
58+
caller.modified::<Class>();
59+
}
60+
61+
let minecraft_id = caller.minecraft_id();
62+
let mut bundle = DataBundle::new(compose);
63+
64+
let mut position_block = position.floor().as_ivec3();
65+
position_block.y -= 1;
66+
67+
// Add bundle splitter so these are all received at once
68+
bundle.add_packet(&play::BundleSplitterS2c, world).unwrap();
69+
70+
// Set respawn position to player's position
71+
bundle
72+
.add_packet(
73+
&play::PlayerSpawnPositionS2c {
74+
position: BlockPos::new(
75+
position_block.x,
76+
position_block.y,
77+
position_block.z,
78+
),
79+
// todo: seems to not do anything; perhaps angle is different than yaw?
80+
// regardless doesn't matter as we teleport to the correct position
81+
// later anyway
82+
angle: **yaw,
83+
},
84+
world,
85+
)
86+
.unwrap();
87+
88+
// Remove player info
89+
bundle
90+
.add_packet(
91+
&play::PlayerRemoveS2c {
92+
uuids: Cow::Borrowed(&[uuid.0]),
93+
},
94+
world,
95+
)
96+
.unwrap();
97+
98+
// Destroy player entity
99+
bundle
100+
.add_packet(
101+
&play::EntitiesDestroyS2c {
102+
entity_ids: Cow::Borrowed(&[VarInt(minecraft_id)]),
103+
},
104+
world,
105+
)
106+
.unwrap();
107+
108+
let skin = class.skin();
109+
let property = Property {
110+
name: "textures".to_string(),
111+
value: skin.textures.clone(),
112+
signature: Some(skin.signature.clone()),
113+
};
114+
115+
let property = &[property];
116+
117+
// Add player back with new skin
118+
bundle
119+
.add_packet(
120+
&PlayerListS2c {
121+
actions: PlayerListActions::default().with_add_player(true),
122+
entries: Cow::Borrowed(&[PlayerListEntry {
123+
player_uuid: uuid.0,
124+
username: Cow::Borrowed("Player"),
125+
properties: Cow::Borrowed(property),
126+
chat_data: None,
127+
listed: true,
128+
ping: 20,
129+
game_mode: GameMode::Survival,
130+
display_name: None,
131+
}]),
132+
},
133+
world,
134+
)
135+
.unwrap();
136+
137+
// Respawn player
138+
bundle
139+
.add_packet(
140+
&PlayerRespawnS2c {
141+
dimension_type_name: ident!("minecraft:overworld").into(),
142+
dimension_name: ident!("minecraft:overworld").into(),
143+
hashed_seed: 0,
144+
game_mode: GameMode::Survival,
145+
previous_game_mode: OptGameMode::default(),
146+
is_debug: false,
147+
is_flat: false,
148+
copy_metadata: false,
149+
last_death_location: None,
150+
portal_cooldown: VarInt::default(),
151+
},
152+
world,
153+
)
154+
.unwrap();
155+
156+
// look and teleport to more accurate position than full-block respawn position
157+
bundle
158+
.add_packet(
159+
&play::PlayerPositionLookS2c {
160+
position: position.as_dvec3(),
161+
yaw: **yaw,
162+
pitch: **pitch,
163+
flags: PlayerPositionLookFlags::default(),
164+
teleport_id: VarInt(fastrand::i32(..)),
165+
},
166+
world,
167+
)
168+
.unwrap();
169+
170+
let msg = format!("Setting rank to {class:?} with yaw {yaw:?}");
171+
let chat = agnostic::chat(msg);
172+
bundle.add_packet(&chat, world).unwrap();
173+
174+
let show_all = show_all(minecraft_id);
175+
bundle.add_packet(show_all.borrow_packet(), world).unwrap();
176+
177+
bundle.add_packet(&play::BundleSplitterS2c, world).unwrap();
178+
179+
bundle.send(world, *stream, SystemId(0)).unwrap();
180+
});
181+
});
182+
}
183+
}

0 commit comments

Comments
 (0)