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
#![doc = include_str!("../README.md")]
#![allow(clippy::type_complexity)]
#![deny(
    rustdoc::broken_intra_doc_links,
    rustdoc::private_intra_doc_links,
    rustdoc::missing_crate_level_docs,
    rustdoc::invalid_codeblock_attributes,
    rustdoc::invalid_rust_codeblocks,
    rustdoc::bare_urls,
    rustdoc::invalid_html_tags
)]
#![warn(
    trivial_casts,
    trivial_numeric_casts,
    unused_lifetimes,
    unused_import_braces,
    unreachable_pub,
    clippy::dbg_macro
)]

use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut};
use valence_server::client::{Client, FlushPacketsSet, UpdateClientsSet, VisibleChunkLayer};
use valence_server::protocol::packets::play::game_state_change_s2c::GameEventKind;
use valence_server::protocol::packets::play::GameStateChangeS2c;
use valence_server::protocol::WritePacket;
use valence_server::ChunkLayer;

pub struct WeatherPlugin;

impl Plugin for WeatherPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            PostUpdate,
            (
                init_weather_on_layer_join,
                change_client_rain_level,
                change_client_thunder_level,
            )
                .before(FlushPacketsSet),
        )
        .add_systems(
            PostUpdate,
            (change_layer_rain_level, change_layer_thunder_level).before(UpdateClientsSet),
        );
    }
}

/// Bundle containing rain and thunder components. `valence_weather` allows this
/// to be added to clients and chunk layer entities.
#[derive(Bundle, Default, PartialEq, PartialOrd)]
pub struct WeatherBundle {
    pub rain: Rain,
    pub thunder: Thunder,
}

/// Component containing the rain level. Valid values are in \[0, 1] with 0
/// being no rain and 1 being full rain.
#[derive(Component, Default, PartialEq, PartialOrd, Deref, DerefMut)]
pub struct Rain(pub f32);

/// Component containing the thunder level. Valid values are in \[0, 1] with 0
/// being no rain and 1 being full rain.
#[derive(Component, Default, PartialEq, PartialOrd, Deref, DerefMut)]
pub struct Thunder(pub f32);

fn init_weather_on_layer_join(
    mut clients: Query<(&mut Client, &VisibleChunkLayer), Changed<VisibleChunkLayer>>,
    layers: Query<(Option<&Rain>, Option<&Thunder>), With<ChunkLayer>>,
) {
    for (mut client, visible_chunk_layer) in &mut clients {
        if let Ok((rain, thunder)) = layers.get(visible_chunk_layer.0) {
            if let Some(rain) = rain {
                if rain.0 != 0.0 {
                    client.write_packet(&GameStateChangeS2c {
                        kind: GameEventKind::RainLevelChange,
                        value: rain.0,
                    });
                }
            }

            if let Some(thunder) = thunder {
                if thunder.0 != 0.0 {
                    client.write_packet(&GameStateChangeS2c {
                        kind: GameEventKind::ThunderLevelChange,
                        value: thunder.0,
                    });
                }
            }
        }
    }
}

fn change_layer_rain_level(
    mut layers: Query<(&mut ChunkLayer, &Rain), (Changed<Rain>, Without<Client>)>,
) {
    for (mut layer, rain) in &mut layers {
        layer.write_packet(&GameStateChangeS2c {
            kind: GameEventKind::RainLevelChange,
            value: rain.0,
        });
    }
}

fn change_layer_thunder_level(
    mut layers: Query<(&mut ChunkLayer, &Thunder), (Changed<Thunder>, Without<Client>)>,
) {
    for (mut layer, thunder) in &mut layers {
        layer.write_packet(&GameStateChangeS2c {
            kind: GameEventKind::ThunderLevelChange,
            value: thunder.0,
        });
    }
}

fn change_client_rain_level(mut clients: Query<(&mut Client, &Rain), Changed<Rain>>) {
    for (mut client, rain) in &mut clients {
        client.write_packet(&GameStateChangeS2c {
            kind: GameEventKind::RainLevelChange,
            value: rain.0,
        });
    }
}

fn change_client_thunder_level(mut clients: Query<(&mut Client, &Thunder), Changed<Thunder>>) {
    for (mut client, thunder) in &mut clients {
        client.write_packet(&GameStateChangeS2c {
            kind: GameEventKind::RainLevelChange,
            value: thunder.0,
        });
    }
}