-
-
Notifications
You must be signed in to change notification settings - Fork 145
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
world time implementation #526
Open
tachibanayui
wants to merge
7
commits into
valence-rs:main
Choose a base branch
from
tachibanayui:time
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
79e8d6c
initial implementation of `valence_world_time`
tachibanayui 84c1881
Add tests for `valence_world_time`
tachibanayui a8ad555
add example for `valance_world_time`
tachibanayui d236a91
Fix CI
tachibanayui c0e7b99
typo
tachibanayui b7231f5
another typo
tachibanayui 1fc4f29
update depgraph again
tachibanayui 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,17 @@ | ||
[package] | ||
name = "valence_world_time" | ||
description = "World time support for Valence" | ||
readme = "README.md" | ||
version.workspace = true | ||
edition.workspace = true | ||
repository.workspace = true | ||
documentation.workspace = true | ||
license.workspace = true | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
bevy_app.workspace = true | ||
bevy_ecs.workspace = true | ||
valence_server.workspace = true | ||
derive_more.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,75 @@ | ||
# Controlling World Time | ||
|
||
This module contains Components and Systems needed to update, tick, | ||
broadcast information about the time of day and world age of a | ||
[`ChunkLayer`]. | ||
|
||
## Enable world time | ||
|
||
To control world time of an [`ChunkLayer`], simply insert the | ||
[`WorldTimeBundle`] bundle. We also need to broadcast world time updates to | ||
clients. The [`IntervalBroadcast::default()`] provides configuration to | ||
mimic vanilla behavior: | ||
|
||
```rust ignore | ||
fn enable(mut commands: Commands, instance: Entity) { | ||
commands.entity(instance).insert(WorldTimeBundle::default()); | ||
} | ||
``` | ||
|
||
## Set the time explicitly | ||
|
||
Mutating [`WorldTime`] will not automatically broadcast the | ||
change to clients. Mutating [`SetTimeQuery`] to modify time | ||
and broadcast the time changes immediately. | ||
|
||
```rust ignore | ||
fn into_the_night(mut instances: Query<(&mut WorldTime, SetTimeQuery), With<Instance>>) { | ||
for (mut t1, mut t2) in instances.iter_mut() { | ||
let time_to_set = DayPhase::Night.into(); | ||
|
||
// Using [`WorldTime`] - Change won't broadcast immediately | ||
t1.time_of_day = time_to_set; | ||
// Using [`SetTimeQuery`] - Change broadcast immediately | ||
t2.time_of_day = time_to_set; | ||
} | ||
} | ||
``` | ||
|
||
## Advacing the world time | ||
|
||
Time of day and world age can be ticked individually using | ||
[`LinearTimeTicking`] and [`LinearWorldAging`] respectively. | ||
If these components don't meet your requirements | ||
(eg: you need time increment follow a sine wave ~~for some reason~~), | ||
you can tick the time yourself by modifying the respective | ||
fields on [`WorldTime`]. | ||
|
||
## Prevent client from automatically update WorldTime | ||
|
||
_(mimics `/gamerule doDaylightCycle false`)_ | ||
|
||
By default, client will continue to update world time if the server | ||
doesn't send packet to sync time between client and server. | ||
This can be toggled by using [`WorldTime::set_client_time_ticking()`] | ||
of [`WorldTime`] to true. | ||
|
||
Here is an example of mimicking `/gamerule doDaylightCycle <value>`: | ||
|
||
```rust ignore | ||
#[derive(Component)] | ||
pub struct DaylightCycle(pub bool); | ||
|
||
fn handle_game_rule_daylight_cycle( | ||
mut instances: Query< | ||
(&mut WorldTime, &mut LinearTimeTicking, &DaylightCycle), | ||
Changed<DaylightCycle>, | ||
>, | ||
) { | ||
for (mut time, mut ticking, doCycle) in instances.iter_mut() { | ||
// Stop client from update | ||
time.set_client_time_ticking(!doCycle.0); | ||
ticking.speed = if doCycle.0 { 1 } else { 0 }; | ||
} | ||
} | ||
``` |
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,115 @@ | ||
use crate::WorldTime; | ||
|
||
pub const DAY_LENGTH: u64 = 24000; | ||
|
||
/// Notable events of a 24-hour Minecraft day | ||
pub enum DayPhase { | ||
Day = 0, | ||
Noon = 6000, | ||
Sunset = 12000, | ||
Night = 13000, | ||
Midnight = 18000, | ||
Sunrise = 23000, | ||
} | ||
|
||
impl From<DayPhase> for u64 { | ||
fn from(value: DayPhase) -> Self { | ||
value as Self | ||
} | ||
} | ||
|
||
/// Reference: <https://minecraft.fandom.com/wiki/Daylight_cycle#Moon_phases> | ||
pub enum MoonPhase { | ||
FullMoon = 0, | ||
WaningGibbous = 1, | ||
ThirdQuarter = 2, | ||
WaningCrescent = 3, | ||
NewMoon = 4, | ||
WaxingCrescent = 5, | ||
FirstQuarter = 6, | ||
WaxingGibbous = 7, | ||
} | ||
|
||
impl From<MoonPhase> for u64 { | ||
fn from(value: MoonPhase) -> Self { | ||
value as Self | ||
} | ||
} | ||
|
||
impl WorldTime { | ||
/// This function ensure that adding time will not resulting in | ||
/// time_of_day flipping sign. | ||
pub fn add_time(&mut self, amount: impl Into<i64>) { | ||
let client_ticking = self.client_time_ticking(); | ||
self.time_of_day = self.time_of_day.abs().wrapping_add(amount.into()); | ||
if self.time_of_day < 0 { | ||
self.time_of_day = self.time_of_day + i64::MAX + 1; | ||
} | ||
|
||
self.set_client_time_ticking(client_ticking); | ||
} | ||
|
||
/// If the client advances world time locally without server updates. | ||
pub fn client_time_ticking(&self) -> bool { | ||
self.time_of_day >= 0 | ||
} | ||
|
||
/// Sets if the client advances world time locally without server updates. | ||
/// Note: If the resulting calculation set time_of_day to 0. This function | ||
/// will set time -1 if time_of_day is 0 and is time ticking = false to | ||
/// workaround protocol limitations | ||
pub fn set_client_time_ticking(&mut self, val: bool) { | ||
self.time_of_day = if val { | ||
self.time_of_day.abs() | ||
} else { | ||
-self.time_of_day.abs() | ||
}; | ||
} | ||
|
||
/// Get the time part of `time_of_day` | ||
pub fn current_day_time(&self) -> u64 { | ||
self.time_of_day as u64 % DAY_LENGTH | ||
} | ||
|
||
/// Set the time part of `time_of_day` | ||
/// Use the [`DayPhase`] enum to easily handle common time | ||
/// of day events without the need to look up information in the wiki. | ||
pub fn set_current_day_time(&mut self, time: impl Into<u64>) { | ||
let client_ticking = self.client_time_ticking(); | ||
self.time_of_day = (self.day() * DAY_LENGTH + time.into() % DAY_LENGTH) as i64; | ||
self.set_client_time_ticking(client_ticking); | ||
} | ||
|
||
/// Get the current day part of `time_of_day` | ||
pub fn day(&self) -> u64 { | ||
self.time_of_day as u64 / DAY_LENGTH | ||
} | ||
|
||
/// Set the current day `time_of_day` | ||
pub fn set_day(&mut self, day: u64) { | ||
let client_ticking = self.client_time_ticking(); | ||
self.time_of_day = (day * DAY_LENGTH + self.current_day_time()) as i64; | ||
self.set_client_time_ticking(client_ticking); | ||
} | ||
|
||
/// Set the time_of_day to the next specified [`DayPhase`] | ||
pub fn warp_to_next_day_phase(&mut self, phase: DayPhase) { | ||
let phase_num: u64 = phase.into(); | ||
if self.current_day_time() >= phase_num { | ||
self.set_day(self.day() + 1); | ||
} | ||
|
||
self.set_current_day_time(phase_num); | ||
} | ||
|
||
/// Set the time_of_day to the next specified [`MoonPhase`] | ||
pub fn wrap_to_next_moon_phase(&mut self, phase: MoonPhase) { | ||
let phase_no: u64 = phase.into(); | ||
if self.day() % 8 >= phase_no { | ||
self.set_day(self.day() + 8 - (self.day() % 8)) | ||
} | ||
|
||
self.set_day(self.day() + phase_no - self.day() % 8); | ||
self.set_current_day_time(DayPhase::Night); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
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.