azalea_client/plugins/
cookies.rs

1//! Arbitrary data sent by the server that gets temporarily stored on the
2//! client.
3
4use std::collections::HashMap;
5
6use azalea_protocol::packets::{
7    config,
8    game::{self},
9    login,
10};
11use azalea_registry::identifier::Identifier;
12use bevy_app::{App, Plugin};
13use bevy_ecs::{
14    component::Component,
15    entity::Entity,
16    event::EntityEvent,
17    observer::On,
18    system::{Commands, Query},
19};
20use tracing::warn;
21
22use crate::{
23    InConfigState, InGameState,
24    packet::{
25        config::SendConfigPacketEvent,
26        game::SendGamePacketEvent,
27        login::{InLoginState, SendLoginPacketEvent},
28    },
29};
30
31pub struct CookiesPlugin;
32impl Plugin for CookiesPlugin {
33    fn build(&self, app: &mut App) {
34        app.add_observer(handle_request_cookie)
35            .add_observer(handle_store_cookie);
36    }
37}
38
39/// A component that holds arbitrary data sent by the server, that our client
40/// temporarily stores and persists across transfers.
41#[derive(Component, Default)]
42pub struct ServerCookies {
43    pub map: HashMap<Identifier, Vec<u8>>,
44}
45
46#[derive(EntityEvent)]
47pub struct RequestCookieEvent {
48    pub entity: Entity,
49    pub key: Identifier,
50}
51#[derive(EntityEvent)]
52pub struct StoreCookieEvent {
53    pub entity: Entity,
54    pub key: Identifier,
55    pub payload: Vec<u8>,
56}
57
58#[allow(clippy::type_complexity)]
59pub fn handle_request_cookie(
60    request_cookie: On<RequestCookieEvent>,
61    mut commands: Commands,
62    query: Query<(
63        Option<&ServerCookies>,
64        Option<&InGameState>,
65        Option<&InConfigState>,
66        Option<&InLoginState>,
67    )>,
68) {
69    let Ok((server_cookies, in_game_state, in_config_state, in_login_state)) =
70        query.get(request_cookie.entity)
71    else {
72        return;
73    };
74
75    let key = request_cookie.key.clone();
76    let payload = server_cookies.and_then(|c| c.map.get(&key)).cloned();
77
78    if in_game_state.is_some() {
79        commands.trigger(SendGamePacketEvent::new(
80            request_cookie.entity,
81            game::ServerboundCookieResponse { key, payload },
82        ));
83    } else if in_config_state.is_some() {
84        commands.trigger(SendConfigPacketEvent::new(
85            request_cookie.entity,
86            config::ServerboundCookieResponse { key, payload },
87        ));
88    } else if in_login_state.is_some() {
89        commands.trigger(SendLoginPacketEvent::new(
90            request_cookie.entity,
91            login::ServerboundCookieResponse { key, payload },
92        ));
93    } else {
94        warn!("got RequestCookieEvent while in an unknown state")
95    }
96}
97pub fn handle_store_cookie(
98    store_cookie: On<StoreCookieEvent>,
99    mut query: Query<&mut ServerCookies>,
100) {
101    if let Ok(mut server_cookies) = query.get_mut(store_cookie.entity) {
102        server_cookies
103            .map
104            .insert(store_cookie.key.clone(), store_cookie.payload.clone());
105    } else {
106        warn!("got StoreCookieEvent for a client without ServerCookies")
107    }
108}