use crate::{ClientState, full_sync, lib_priv::SyncTrackerRes, proto::Message};
use bevy_app::{App, Plugin, FixedUpdate};
use bevy_connect::{events::MessageReceivedEvent, prelude::Channel};
use bevy_ecs::schedule::IntoScheduleConfigs;
use bevy_ecs::schedule::SystemCondition;
use bevy_ecs::schedule::common_conditions::on_message;
use bevy_ecs::{
resource::Resource,
schedule::common_conditions::{
resource_added, resource_changed, resource_exists, resource_removed,
},
system::{Commands, Res, ResMut},
world::World,
};
use bevy_state::{condition::in_state, state::NextState};
use tracing::info;
use tracing::warn;
mod receiver;
#[derive(Resource, Default)]
struct ClientPresendInitialSync {
messages: Vec<Message>,
}
pub(crate) struct ClientSyncPlugin;
impl Plugin for ClientSyncPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ClientPresendInitialSync>();
app.add_systems(
FixedUpdate,
client_connected.run_if(
resource_added::<Channel<Message>>.and_then(in_state(ClientState::Disconnected)),
),
);
app.add_systems(
FixedUpdate,
set_client_to_disconnected
.run_if(resource_removed::<Channel<Message>>.and_then(in_state(ClientState::Connected))),
);
app.add_systems(
FixedUpdate,
client_update_state.run_if(
resource_exists::<Channel<Message>>.and_then(resource_changed::<Channel<Message>>),
),
);
app.add_systems(
FixedUpdate,
receiver::process_messages.chain().run_if(
resource_exists::<Channel<Message>>
.and_then(in_state(ClientState::Connected))
.and_then(on_message::<MessageReceivedEvent<Message>>),
),
);
}
}
fn set_client_to_disconnected(mut client_state: ResMut<NextState<ClientState>>) {
info!("Disconnected from server.");
client_state.set(ClientState::Disconnected);
}
#[allow(clippy::needless_pass_by_value)]
fn client_update_state(channel: Res<Channel<Message>>, mut state: ResMut<NextState<ClientState>>) {
if channel.is_host() {
state.set(ClientState::Disconnected);
return;
}
state.set(ClientState::Connected);
}
#[allow(clippy::needless_pass_by_value)]
fn client_connected(
mut cmd: Commands,
channel: Res<Channel<Message>>,
mut client_state: ResMut<NextState<ClientState>>,
mut tracker: ResMut<SyncTrackerRes>,
) {
if channel.is_host() {
client_state.set(ClientState::Disconnected);
return;
}
info!("Connected to server.");
client_state.set(ClientState::Connected);
if tracker.host_promotion_in_progress {
tracker.host_promotion_in_progress = false;
info!("Promotion: Reconnected after host promotion, not requesting initial sync.");
} else {
cmd.queue(|world: &mut World| {
info!("Starting new client session and requesting initial sync.");
world.resource_mut::<ClientPresendInitialSync>().messages =
full_sync::build_full_sync(world).unwrap_or_default();
let mut channel = world.resource_mut::<Channel<Message>>();
let Some(host_uuid) = channel.host_uuid() else {
warn!("No host uuid known");
return;
};
let self_uuid = channel.uuid();
channel.send_to(
host_uuid,
Message::RequestInitialSync {
from_uuid: self_uuid.0,
},
);
});
}
}