mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
run Update schedule 60 times per second and delete code related to run_schedule_sender
This commit is contained in:
parent
5557747a97
commit
7a3246dc1a
5 changed files with 40 additions and 71 deletions
|
@ -46,13 +46,10 @@ use parking_lot::{Mutex, RwLock};
|
|||
use simdnbt::owned::NbtCompound;
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
sync::{
|
||||
broadcast,
|
||||
mpsc::{self, error::TrySendError},
|
||||
},
|
||||
sync::{broadcast, mpsc},
|
||||
time,
|
||||
};
|
||||
use tracing::{debug, error, info};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
|
@ -95,9 +92,6 @@ pub struct Client {
|
|||
/// directly. Note that if you're using a shared world (i.e. a swarm), this
|
||||
/// will contain all entities in all worlds.
|
||||
pub ecs: Arc<Mutex<World>>,
|
||||
|
||||
/// Use this to force the client to run the schedule outside of a tick.
|
||||
pub run_schedule_sender: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
/// An error that happened while joining the server.
|
||||
|
@ -129,7 +123,6 @@ pub struct StartClientOpts<'a> {
|
|||
pub address: &'a ServerAddress,
|
||||
pub resolved_address: &'a SocketAddr,
|
||||
pub proxy: Option<Proxy>,
|
||||
pub run_schedule_sender: mpsc::Sender<()>,
|
||||
pub event_sender: Option<mpsc::UnboundedSender<Event>>,
|
||||
}
|
||||
|
||||
|
@ -140,13 +133,10 @@ impl<'a> StartClientOpts<'a> {
|
|||
resolved_address: &'a SocketAddr,
|
||||
event_sender: Option<mpsc::UnboundedSender<Event>>,
|
||||
) -> StartClientOpts<'a> {
|
||||
// An event that causes the schedule to run. This is only used internally.
|
||||
let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
|
||||
|
||||
let mut app = App::new();
|
||||
app.add_plugins(DefaultPlugins);
|
||||
|
||||
let ecs_lock = start_ecs_runner(app, run_schedule_receiver, run_schedule_sender.clone());
|
||||
let ecs_lock = start_ecs_runner(app);
|
||||
|
||||
Self {
|
||||
ecs_lock,
|
||||
|
@ -154,7 +144,6 @@ impl<'a> StartClientOpts<'a> {
|
|||
address,
|
||||
resolved_address,
|
||||
proxy: None,
|
||||
run_schedule_sender,
|
||||
event_sender,
|
||||
}
|
||||
}
|
||||
|
@ -170,18 +159,12 @@ impl Client {
|
|||
/// World, and schedule runner function.
|
||||
/// You should only use this if you want to change these fields from the
|
||||
/// defaults, otherwise use [`Client::join`].
|
||||
pub fn new(
|
||||
entity: Entity,
|
||||
ecs: Arc<Mutex<World>>,
|
||||
run_schedule_sender: mpsc::Sender<()>,
|
||||
) -> Self {
|
||||
pub fn new(entity: Entity, ecs: Arc<Mutex<World>>) -> Self {
|
||||
Self {
|
||||
// default our id to 0, it'll be set later
|
||||
entity,
|
||||
|
||||
ecs,
|
||||
|
||||
run_schedule_sender,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +231,6 @@ impl Client {
|
|||
address,
|
||||
resolved_address,
|
||||
proxy,
|
||||
run_schedule_sender,
|
||||
event_sender,
|
||||
}: StartClientOpts<'_>,
|
||||
) -> Result<Self, JoinError> {
|
||||
|
@ -352,7 +334,7 @@ impl Client {
|
|||
))
|
||||
});
|
||||
|
||||
let client = Client::new(entity, ecs_lock.clone(), run_schedule_sender.clone());
|
||||
let client = Client::new(entity, ecs_lock.clone());
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
|
@ -769,11 +751,7 @@ impl Plugin for AzaleaPlugin {
|
|||
/// You can create your app with `App::new()`, but don't forget to add
|
||||
/// [`DefaultPlugins`].
|
||||
#[doc(hidden)]
|
||||
pub fn start_ecs_runner(
|
||||
mut app: App,
|
||||
run_schedule_receiver: mpsc::Receiver<()>,
|
||||
run_schedule_sender: mpsc::Sender<()>,
|
||||
) -> Arc<Mutex<World>> {
|
||||
pub fn start_ecs_runner(mut app: App) -> Arc<Mutex<World>> {
|
||||
// this block is based on Bevy's default runner:
|
||||
// https://github.com/bevyengine/bevy/blob/390877cdae7a17095a75c8f9f1b4241fe5047e83/crates/bevy_app/src/schedule_runner.rs#L77-L85
|
||||
if app.plugins_state() != PluginsState::Cleaned {
|
||||
|
@ -796,35 +774,54 @@ pub fn start_ecs_runner(
|
|||
tokio::spawn(run_schedule_loop(
|
||||
ecs.clone(),
|
||||
*app.main().update_schedule.as_ref().unwrap(),
|
||||
run_schedule_receiver,
|
||||
));
|
||||
tokio::spawn(tick_run_schedule_loop(run_schedule_sender));
|
||||
|
||||
ecs
|
||||
}
|
||||
|
||||
async fn run_schedule_loop(
|
||||
ecs: Arc<Mutex<World>>,
|
||||
outer_schedule_label: InternedScheduleLabel,
|
||||
mut run_schedule_receiver: mpsc::Receiver<()>,
|
||||
) {
|
||||
async fn run_schedule_loop(ecs: Arc<Mutex<World>>, outer_schedule_label: InternedScheduleLabel) {
|
||||
let mut last_update: Option<Instant> = None;
|
||||
let mut last_tick: Option<Instant> = None;
|
||||
|
||||
// azalea runs the Update schedule at most 60 times per second to simulate
|
||||
// framerate. unlike vanilla though, we also only handle packets during Updates
|
||||
// due to everything running in ecs systems.
|
||||
const UPDATE_DURATION_TARGET: Duration = Duration::from_micros(1_000_000 / 60);
|
||||
// minecraft runs at 20 tps
|
||||
const GAME_TICK_DURATION_TARGET: Duration = Duration::from_micros(1_000_000 / 20);
|
||||
|
||||
loop {
|
||||
// whenever we get an event from run_schedule_receiver, run the schedule
|
||||
run_schedule_receiver.recv().await;
|
||||
// sleep until the next update if necessary
|
||||
let now = Instant::now();
|
||||
if let Some(last_update) = last_update {
|
||||
let elapsed = now.duration_since(last_update);
|
||||
if elapsed < UPDATE_DURATION_TARGET {
|
||||
time::sleep(UPDATE_DURATION_TARGET - elapsed).await;
|
||||
}
|
||||
}
|
||||
last_update = Some(now);
|
||||
|
||||
let mut ecs = ecs.lock();
|
||||
|
||||
// if last tick is None or more than 50ms ago, run the GameTick schedule
|
||||
ecs.run_schedule(outer_schedule_label);
|
||||
if last_tick
|
||||
.map(|last_tick| last_tick.elapsed() > Duration::from_millis(50))
|
||||
.map(|last_tick| last_tick.elapsed() > GAME_TICK_DURATION_TARGET)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
if let Some(last_tick) = &mut last_tick {
|
||||
*last_tick += Duration::from_millis(50);
|
||||
*last_tick += GAME_TICK_DURATION_TARGET;
|
||||
|
||||
// if we're more than 10 ticks behind, set last_tick to now.
|
||||
// vanilla doesn't do it in exactly the same way but it shouldn't really matter
|
||||
if (now - *last_tick) > GAME_TICK_DURATION_TARGET * 10 {
|
||||
warn!(
|
||||
"GameTick is more than 10 ticks behind, skipping ticks so we don't have to burst too much"
|
||||
);
|
||||
*last_tick = now;
|
||||
}
|
||||
} else {
|
||||
last_tick = Some(Instant::now());
|
||||
last_tick = Some(now);
|
||||
}
|
||||
ecs.run_schedule(GameTick);
|
||||
}
|
||||
|
@ -833,23 +830,6 @@ async fn run_schedule_loop(
|
|||
}
|
||||
}
|
||||
|
||||
/// Send an event to run the schedule every 50 milliseconds. It will stop when
|
||||
/// the receiver is dropped.
|
||||
pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::Sender<()>) {
|
||||
let mut game_tick_interval = time::interval(Duration::from_millis(50));
|
||||
// TODO: Minecraft bursts up to 10 ticks and then skips, we should too
|
||||
game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
|
||||
|
||||
loop {
|
||||
game_tick_interval.tick().await;
|
||||
if let Err(TrySendError::Closed(())) = run_schedule_sender.try_send(()) {
|
||||
error!("tick_run_schedule_loop failed because run_schedule_sender was closed");
|
||||
// the sender is closed so end the task
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resource that contains a [`broadcast::Sender`] that will be sent every
|
||||
/// Minecraft tick.
|
||||
///
|
||||
|
|
|
@ -152,7 +152,6 @@ impl Client {
|
|||
content: message.to_string(),
|
||||
kind: ChatKind::Message,
|
||||
});
|
||||
let _ = self.run_schedule_sender.try_send(());
|
||||
}
|
||||
|
||||
/// Send a command packet to the server. The `command` argument should not
|
||||
|
@ -166,7 +165,6 @@ impl Client {
|
|||
content: command.to_string(),
|
||||
kind: ChatKind::Command,
|
||||
});
|
||||
let _ = self.run_schedule_sender.try_send(());
|
||||
}
|
||||
|
||||
/// Send a message in chat.
|
||||
|
@ -183,7 +181,6 @@ impl Client {
|
|||
entity: self.entity,
|
||||
content: content.to_string(),
|
||||
});
|
||||
let _ = self.run_schedule_sender.try_send(());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ pub fn process_raw_packet(
|
|||
player: Entity,
|
||||
raw_packet: &[u8],
|
||||
) -> Result<(), Box<ReadPacketError>> {
|
||||
let packet = deserialize_packet(&mut Cursor::new(&raw_packet))?;
|
||||
let packet = deserialize_packet(&mut Cursor::new(raw_packet))?;
|
||||
process_packet(ecs, player, &packet);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ struct State {
|
|||
|
||||
async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
||||
if let Event::Chat(m) = event {
|
||||
if m.sender() == Some(bot.profile.name.clone()) {
|
||||
if m.sender() == Some(bot.profile().name.clone()) {
|
||||
return Ok(());
|
||||
};
|
||||
if m.content() != "go" {
|
||||
|
|
|
@ -55,8 +55,6 @@ pub struct Swarm {
|
|||
|
||||
bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>,
|
||||
swarm_tx: mpsc::UnboundedSender<SwarmEvent>,
|
||||
|
||||
run_schedule_sender: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
/// Create a new [`Swarm`].
|
||||
|
@ -396,12 +394,9 @@ where
|
|||
|
||||
swarm_tx.send(SwarmEvent::Init).unwrap();
|
||||
|
||||
let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
|
||||
|
||||
let main_schedule_label = self.app.main().update_schedule.unwrap();
|
||||
|
||||
let ecs_lock =
|
||||
start_ecs_runner(self.app, run_schedule_receiver, run_schedule_sender.clone());
|
||||
let ecs_lock = start_ecs_runner(self.app);
|
||||
|
||||
let swarm = Swarm {
|
||||
ecs_lock: ecs_lock.clone(),
|
||||
|
@ -414,8 +409,6 @@ where
|
|||
bots_tx,
|
||||
|
||||
swarm_tx: swarm_tx.clone(),
|
||||
|
||||
run_schedule_sender,
|
||||
};
|
||||
|
||||
// run the main schedule so the startup systems run
|
||||
|
@ -667,7 +660,6 @@ impl Swarm {
|
|||
address: &address,
|
||||
resolved_address: &resolved_address,
|
||||
proxy: join_opts.proxy.clone(),
|
||||
run_schedule_sender: self.run_schedule_sender.clone(),
|
||||
event_sender: Some(tx),
|
||||
})
|
||||
.await?;
|
||||
|
|
Loading…
Add table
Reference in a new issue