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
use std::borrow::Cow;

use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use valence_protocol::encode::{PacketWriter, WritePacket};
pub use valence_protocol::packets::play::synchronize_tags_s2c::RegistryMap;
use valence_protocol::packets::play::SynchronizeTagsS2c;
use valence_server_common::Server;

use crate::RegistrySet;

#[derive(Debug, Resource, Default)]
pub struct TagsRegistry {
    pub registries: RegistryMap,
    cached_packet: Vec<u8>,
}

pub(super) fn build(app: &mut App) {
    app.init_resource::<TagsRegistry>()
        .add_systems(PreStartup, init_tags_registry)
        .add_systems(PostUpdate, cache_tags_packet.in_set(RegistrySet));
}

impl TagsRegistry {
    fn build_synchronize_tags(&self) -> SynchronizeTagsS2c {
        SynchronizeTagsS2c {
            groups: Cow::Borrowed(&self.registries),
        }
    }

    /// Returns bytes of the cached [`SynchronizeTagsS2c`] packet.
    pub fn sync_tags_packet(&self) -> &[u8] {
        &self.cached_packet
    }
}

fn init_tags_registry(mut tags: ResMut<TagsRegistry>) {
    let registries = serde_json::from_str::<RegistryMap>(include_str!("../extracted/tags.json"))
        .expect("tags.json must have expected structure");

    tags.registries = registries;
}

pub(crate) fn cache_tags_packet(server: Res<Server>, tags: ResMut<TagsRegistry>) {
    if tags.is_changed() {
        let tags = tags.into_inner();
        let packet = tags.build_synchronize_tags();
        let mut bytes = vec![];
        let mut writer = PacketWriter::new(&mut bytes, server.compression_threshold());

        writer.write_packet(&packet);
        tags.cached_packet = bytes;
    }
}

#[cfg(test)]
mod tests {
    /* TODO: move this to src/tests/
    #[test]
    fn smoke_test() {
        let mut app = bevy_app::App::new();
        app.add_plugins(RegistryPlugin);
        // app.insert_resource(Server::default());
        app.update();

        let tags_registry = app.world.get_resource::<TagsRegistry>().unwrap();
        let packet = tags_registry.build_synchronize_tags();
        assert!(!packet.registries.is_empty());
        assert!(!tags_registry.cached_packet.is_empty());
    }
    */
}