1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
// login packets aren't actually handled here because compression/encryption
// would make packet handling a lot messier
mod events;
use azalea_protocol::packets::{
ConnectionProtocol,
login::{
ClientboundCookieRequest, ClientboundCustomQuery, ClientboundHello,
ClientboundLoginCompression, ClientboundLoginDisconnect, ClientboundLoginFinished,
ClientboundLoginPacket, ServerboundLoginAcknowledged,
},
};
use bevy_ecs::prelude::*;
pub use events::*;
use tracing::{debug, error};
use super::as_system;
use crate::{
InConfigState, account::Account, connection::RawConnection, cookies::RequestCookieEvent,
disconnect::DisconnectEvent, packet::declare_packet_handlers, player::GameProfileComponent,
};
pub fn process_packet(ecs: &mut World, player: Entity, packet: &ClientboundLoginPacket) {
let mut handler = LoginPacketHandler { player, ecs };
declare_packet_handlers!(
ClientboundLoginPacket,
packet,
handler,
[
hello,
login_disconnect,
login_finished,
login_compression,
custom_query,
cookie_request
]
);
}
/// A marker component for local players that are currently in the
/// `login` state.
#[derive(Clone, Component, Debug)]
pub struct InLoginState;
pub struct LoginPacketHandler<'a> {
pub ecs: &'a mut World,
pub player: Entity,
}
impl LoginPacketHandler<'_> {
pub fn hello(&mut self, p: &ClientboundHello) {
debug!("Got encryption request {p:?}");
as_system::<(Commands, Query<&Account>)>(self.ecs, |(mut commands, query)| {
let Ok(account) = query.get(self.player) else {
error!(
"Expected Account component to be present on player when receiving hello packet."
);
return;
};
commands.trigger(ReceiveHelloEvent {
entity: self.player,
account: account.clone(),
packet: p.clone(),
});
});
}
pub fn login_disconnect(&mut self, p: &ClientboundLoginDisconnect) {
debug!("Got disconnect {:?}", p);
as_system::<MessageWriter<_>>(self.ecs, |mut events| {
events.write(DisconnectEvent {
entity: self.player,
reason: Some(p.reason.clone()),
});
});
}
pub fn login_finished(&mut self, p: &ClientboundLoginFinished) {
debug!(
"Got profile {:?}. login is finished and we're now switching to the config state",
p.game_profile
);
as_system::<(Commands, Query<&mut RawConnection>)>(
self.ecs,
|(mut commands, mut query)| {
commands.trigger(SendLoginPacketEvent::new(
self.player,
ServerboundLoginAcknowledged,
));
commands
.entity(self.player)
.remove::<InLoginState>()
.insert(InConfigState)
.insert(GameProfileComponent(p.game_profile.clone()));
let mut conn = query
.get_mut(self.player)
.expect("RawConnection component should be present when receiving packets");
conn.state = ConnectionProtocol::Configuration;
},
);
}
pub fn login_compression(&mut self, p: &ClientboundLoginCompression) {
debug!("Got compression request {p:?}");
as_system::<Query<&mut RawConnection>>(self.ecs, |mut query| {
let mut conn = query
.get_mut(self.player)
.expect("RawConnection component should be present when receiving packets");
if let Some(net_conn) = &mut conn.net_conn() {
net_conn.set_compression_threshold(Some(p.compression_threshold as u32));
}
})
}
pub fn custom_query(&mut self, p: &ClientboundCustomQuery) {
debug!("Got custom query {p:?}");
as_system::<MessageWriter<ReceiveCustomQueryEvent>>(self.ecs, |mut events| {
events.write(ReceiveCustomQueryEvent {
entity: self.player,
packet: p.clone(),
disabled: false,
});
});
}
pub fn cookie_request(&mut self, p: &ClientboundCookieRequest) {
debug!("Got cookie request packet {p:?}");
as_system::<Commands>(self.ecs, |mut commands| {
commands.trigger(RequestCookieEvent {
entity: self.player,
key: p.key.clone(),
});
});
}
}
|