use anyhow::Context;
use bevy::prelude::{
not, App, EventReader, EventWriter, FixedUpdate, IntoSystemConfigs, IntoSystemSetConfigs,
Plugin, PostUpdate, Res, ResMut, SystemSet,
};
use tracing::{error, trace};
use crate::channel::builder::InputChannel;
use crate::client::events::InputEvent;
use crate::client::prediction::plugin::is_in_rollback;
use crate::client::prediction::{Rollback, RollbackState};
use crate::client::resource::Client;
use crate::client::sync::client_is_synced;
use crate::inputs::UserInput;
use crate::protocol::Protocol;
use crate::shared::sets::{FixedUpdateSet, MainSet};
#[derive(Debug, Clone)]
pub struct InputConfig {
packet_redundancy: u16,
}
impl Default for InputConfig {
fn default() -> Self {
InputConfig {
packet_redundancy: 10,
}
}
}
pub struct InputPlugin<P: Protocol> {
config: InputConfig,
_marker: std::marker::PhantomData<P>,
}
impl<P: Protocol> InputPlugin<P> {
fn new(config: InputConfig) -> Self {
Self {
config,
_marker: std::marker::PhantomData,
}
}
}
impl<P: Protocol> Default for InputPlugin<P> {
fn default() -> Self {
Self {
config: InputConfig::default(),
_marker: std::marker::PhantomData,
}
}
}
pub struct CurrentInput<T: UserInput> {
input: T,
}
impl<P: Protocol> Plugin for InputPlugin<P> {
fn build(&self, app: &mut App) {
app.add_event::<InputEvent<P::Input>>();
app.configure_sets(
FixedUpdate,
((
FixedUpdateSet::TickUpdate,
InputSystemSet::BufferInputs.run_if(not(is_in_rollback)),
InputSystemSet::WriteInputEvent,
FixedUpdateSet::Main,
InputSystemSet::ClearInputEvent,
)
.chain(),),
);
app.configure_sets(
PostUpdate,
(
InputSystemSet::SendInputMessage.in_set(MainSet::Send),
MainSet::SendPackets,
)
.chain(),
);
app.add_systems(
FixedUpdate,
write_input_event::<P>.in_set(InputSystemSet::WriteInputEvent),
);
app.add_systems(
FixedUpdate,
clear_input_events::<P>.in_set(InputSystemSet::ClearInputEvent),
);
app.add_systems(
PostUpdate,
prepare_input_message::<P>
.in_set(InputSystemSet::SendInputMessage)
.run_if(client_is_synced::<P>),
);
}
}
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum InputSystemSet {
BufferInputs,
WriteInputEvent,
ClearInputEvent,
SendInputMessage,
}
fn clear_input_events<P: Protocol>(mut input_events: EventReader<InputEvent<P::Input>>) {
input_events.clear();
}
fn write_input_event<P: Protocol>(
mut client: ResMut<Client<P>>,
mut input_events: EventWriter<InputEvent<P::Input>>,
rollback: Option<Res<Rollback>>,
) {
let tick = rollback.map_or(client.tick(), |rollback| match rollback.state {
RollbackState::Default => client.tick(),
RollbackState::ShouldRollback {
current_tick: rollback_tick,
} => rollback_tick,
});
input_events.send(InputEvent::new(client.get_input(tick).clone(), ()));
}
fn prepare_input_message<P: Protocol>(mut client: ResMut<Client<P>>) {
let current_tick = client.tick();
trace!(tick = ?current_tick, "prepare_input_message");
let num_tick = ((client.config().shared.client_send_interval.as_micros()
/ client.config().shared.tick.tick_duration.as_micros())
+ 1) as u16;
let redundancy = client.config().input.packet_redundancy;
let message_len = redundancy * num_tick;
let message = client
.get_input_buffer()
.create_message(client.tick(), message_len);
if !message.is_empty() {
client
.buffer_send::<InputChannel, _>(message)
.unwrap_or_else(|err| {
error!("Error while sending input message: {:?}", err);
})
}
client
.get_mut_input_buffer()
.pop(current_tick - (message_len + 1));
}