-
-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Valence tick #377
Closed
Closed
Valence tick #377
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
1d3df84
valence_tick
96fe5b0
touch of cleanup
4564540
Some cleanup and added example
0fc3742
expose valence_tick as tick
5b4f86b
valencify crate
b43fad1
Merge branch 'main' into valence_tick
0c855bd
Merge branch 'main' into valence_tick
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#![allow(clippy::type_complexity)] | ||
|
||
use valence::prelude::*; | ||
use valence_client::message::SendMessage; | ||
|
||
const SPAWN_Y: i32 = 64; | ||
|
||
pub fn main() { | ||
tracing_subscriber::fmt().init(); | ||
|
||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.add_plugin(TickSystem) | ||
.add_startup_system(setup) | ||
.add_system(init_clients) | ||
.add_system(despawn_disconnected_clients) | ||
.add_system(run_every_20_ticks.in_schedule(CoreSchedule::FixedUpdate)) | ||
.add_system(manual_tick_interval) | ||
.insert_resource(FixedTick::new(20)) | ||
.run(); | ||
} | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
server: Res<Server>, | ||
dimensions: Res<DimensionTypeRegistry>, | ||
biomes: Res<BiomeRegistry>, | ||
) { | ||
let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server); | ||
|
||
for z in -5..5 { | ||
for x in -5..5 { | ||
instance.insert_chunk([x, z], Chunk::default()); | ||
} | ||
} | ||
|
||
for z in -25..25 { | ||
for x in -25..25 { | ||
instance.set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK); | ||
} | ||
} | ||
|
||
commands.spawn(instance); | ||
} | ||
|
||
fn init_clients( | ||
mut clients: Query<(&mut Position, &mut Location, &mut GameMode), Added<Client>>, | ||
instances: Query<Entity, With<Instance>>, | ||
) { | ||
for (mut pos, mut loc, mut game_mode) in &mut clients { | ||
pos.0 = [0.0, SPAWN_Y as f64 + 1.0, 0.0].into(); | ||
loc.0 = instances.single(); | ||
*game_mode = GameMode::Creative; | ||
} | ||
} | ||
|
||
fn run_every_20_ticks(mut clients: Query<&mut Client>, tick: Res<Tick>) { | ||
for mut client in &mut clients { | ||
client.send_chat_message(format!("20 ticks have passed, tick: {}", tick.elapsed())); | ||
} | ||
} | ||
|
||
fn manual_tick_interval( | ||
mut clients: Query<&mut Client>, | ||
mut last_time: Local<usize>, | ||
tick: Res<Tick>, | ||
) { | ||
if tick.elapsed() - *last_time >= 40 { | ||
*last_time = tick.elapsed(); | ||
for mut client in &mut clients { | ||
client.send_chat_message(format!("40 ticks have passed, tick: {}", tick.elapsed())); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "valence_tick" | ||
version.workspace = true | ||
edition.workspace = true | ||
|
||
[dependencies] | ||
anyhow.workspace = true | ||
bevy_app.workspace = true | ||
bevy_ecs.workspace = true | ||
bevy_reflect.workspace = true | ||
tokio.workspace = true | ||
valence_core.workspace = true | ||
thiserror.workspace = true | ||
flume.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# valence_tick | ||
|
||
Everything related to Minecraft ticks. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use bevy_app::CoreSchedule; | ||
use bevy_ecs::{system::Resource, world::World}; | ||
use thiserror::Error; | ||
|
||
/// The amount of tick that must pass before the fixed tickstep schedule is run again. | ||
#[derive(Resource, Debug)] | ||
pub struct FixedTick { | ||
accumulated: usize, | ||
period: usize, | ||
} | ||
|
||
impl FixedTick { | ||
/// Creates a new [`FixedTick`] struct | ||
pub fn new(period: usize) -> Self { | ||
FixedTick { | ||
accumulated: 0, | ||
period, | ||
} | ||
} | ||
|
||
/// Adds 1 to the accumulated tick so far. | ||
pub fn tick(&mut self) { | ||
self.accumulated += 1; | ||
} | ||
|
||
/// Returns the current amount of accumulated tick | ||
pub fn accumulated(&self) -> usize { | ||
self.accumulated | ||
} | ||
|
||
/// Expends one `period` of accumulated tick. | ||
/// | ||
/// [`Err(FixedUpdateError`)] will be returned | ||
pub fn expend(&mut self) -> Result<(), FixedUpdateError> { | ||
if let Some(new_value) = self.accumulated.checked_sub(self.period) { | ||
self.accumulated = new_value; | ||
Ok(()) | ||
} else { | ||
Err(FixedUpdateError::NotEnoughTick { | ||
accumulated: self.accumulated, | ||
period: self.period, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
impl Default for FixedTick { | ||
fn default() -> Self { | ||
FixedTick { | ||
accumulated: 0, | ||
period: 1, | ||
} | ||
} | ||
} | ||
|
||
/// An error returned when working with [`FixedTick`]. | ||
#[derive(Debug, Error)] | ||
pub enum FixedUpdateError { | ||
#[error("At least one period worth of ticks must be accumulated.")] | ||
NotEnoughTick { accumulated: usize, period: usize }, | ||
} | ||
|
||
/// Ticks the [`FixedTick`] resource then runs the [`CoreSchedule::FixedUpdate`]. | ||
pub fn run_fixed_update_schedule(world: &mut World) { | ||
// Tick the time | ||
let mut fixed_time = world.resource_mut::<FixedTick>(); | ||
fixed_time.tick(); | ||
|
||
// Run the schedule until we run out of accumulated time | ||
let mut check_again = true; | ||
while check_again { | ||
let mut fixed_time = world.resource_mut::<FixedTick>(); | ||
let fixed_time_run = fixed_time.expend().is_ok(); | ||
if fixed_time_run { | ||
world.run_schedule(CoreSchedule::FixedUpdate); | ||
} else { | ||
check_again = false; | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn fixed_time_starts_at_zero() { | ||
let new_time = FixedTick::new(42); | ||
assert_eq!(new_time.accumulated(), 0); | ||
|
||
let default_time = FixedTick::default(); | ||
assert_eq!(default_time.accumulated(), 0); | ||
} | ||
|
||
#[test] | ||
fn fixed_time_ticks_up() { | ||
let mut fixed_time = FixedTick::default(); | ||
fixed_time.tick(); | ||
assert_eq!(fixed_time.accumulated(), 1); | ||
} | ||
|
||
#[test] | ||
fn enough_accumulated_time_is_required() { | ||
let mut fixed_time = FixedTick::new(2); | ||
fixed_time.tick(); | ||
assert!(fixed_time.expend().is_err()); | ||
assert_eq!(fixed_time.accumulated(), 1); | ||
|
||
fixed_time.tick(); | ||
assert!(fixed_time.expend().is_ok()); | ||
assert_eq!(fixed_time.accumulated(), 0); | ||
} | ||
|
||
#[test] | ||
fn repeatedly_expending_time() { | ||
let mut fixed_time = FixedTick::new(1); | ||
fixed_time.tick(); | ||
assert!(fixed_time.expend().is_ok()); | ||
assert!(fixed_time.expend().is_err()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#![doc = include_str!("../README.md")] | ||
#![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 | ||
)] | ||
|
||
pub mod fixed_tickstep; | ||
#[allow(clippy::module_inception)] | ||
mod tick; | ||
|
||
use fixed_tickstep::{run_fixed_update_schedule, FixedTick}; | ||
pub use tick::*; | ||
|
||
use bevy_ecs::system::ResMut; | ||
|
||
use bevy_app::prelude::*; | ||
use bevy_ecs::prelude::*; | ||
|
||
/// Adds tick functionality to Apps. | ||
#[derive(Default)] | ||
pub struct TickPlugin; | ||
|
||
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)] | ||
/// Updates the elapsed ticks. Any system that interacts with [Tick] component should run after | ||
/// this. | ||
pub struct TickSystem; | ||
|
||
impl Plugin for TickSystem { | ||
fn build(&self, app: &mut App) { | ||
app.init_resource::<Tick>() | ||
.register_type::<Tick>() | ||
.init_resource::<FixedTick>() | ||
.configure_set(TickSystem.in_base_set(CoreSet::First)) | ||
.add_system(tick_system.in_set(TickSystem)) | ||
.add_system(run_fixed_update_schedule.in_base_set(CoreSet::FixedUpdate)); | ||
} | ||
} | ||
|
||
/// The system used to update the [`Tick`] used by app logic. | ||
fn tick_system(mut tick: ResMut<Tick>) { | ||
tick.update(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use bevy_ecs::{reflect::ReflectResource, system::Resource}; | ||
use bevy_reflect::{FromReflect, Reflect}; | ||
|
||
/// A counter that tracks how many ticks has advanced | ||
#[derive(Resource, Default, Reflect, FromReflect, Debug, Clone)] | ||
#[reflect(Resource)] | ||
pub struct Tick { | ||
elapsed: usize, | ||
} | ||
|
||
impl Tick { | ||
/// Constructs a new `Tick` instance | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
/// Updates the internal tick measurements. | ||
pub fn update(&mut self) { | ||
self.update_with_tick(1); | ||
} | ||
|
||
pub fn update_with_tick(&mut self, tick: usize) { | ||
self.elapsed += tick; | ||
} | ||
|
||
/// Returns how many tick have advanced since [`startup`](#method.startup), as [`usize`]. | ||
#[inline] | ||
pub fn elapsed(&self) -> usize { | ||
self.elapsed | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean this only allows for one timer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionally it works the same as bevy_timer, so yeah, i think so, which is why I added the other example, or want to run it on a seperate scheduler, or maybe even make it some form of dynamic scheduler if thats possible?