Skip to content
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 #370

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ valence_network.path = "crates/valence_network"
valence_player_list.path = "crates/valence_player_list"
valence_registry.path = "crates/valence_registry"
valence_world_border.path = "crates/valence_world_border"
valence_world_time.path = "crates/valence_world_time"
valence.path = "crates/valence"

zip = "0.6.3"
Expand Down
1 change: 1 addition & 0 deletions crates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ graph TD
instance --> biome
instance --> dimension
instance --> entity
instance --> world_time
rj00a marked this conversation as resolved.
Show resolved Hide resolved
player_list --> client
inventory --> client
anvil --> client
Expand Down
5 changes: 3 additions & 2 deletions crates/valence/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ keywords = ["minecraft", "gamedev", "server", "ecs"]
categories = ["game-engines"]

[features]
default = ["network", "player_list", "inventory", "anvil", "advancement", "world_border"]
default = ["network", "player_list", "inventory", "anvil", "advancement", "world_border", "world_time"]
network = ["dep:valence_network"]
player_list = ["dep:valence_player_list"]
inventory = ["dep:valence_inventory"]
anvil = ["dep:valence_anvil"]
advancement = ["dep:valence_advancement"]
world_border = ["dep:valence_world_border"]
world_time = ["dep:valence_world_time"]

[dependencies]
bevy_app.workspace = true
Expand All @@ -39,7 +40,7 @@ valence_inventory = { workspace = true, optional = true }
valence_anvil = { workspace = true, optional = true }
valence_advancement = { workspace = true, optional = true }
valence_world_border = { workspace = true, optional = true }

valence_world_time = { workspace = true, optional = true }

[dev-dependencies]
anyhow.workspace = true
Expand Down
106 changes: 106 additions & 0 deletions crates/valence/examples/time_travel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use valence::client::despawn_disconnected_clients;
use valence::client::hand_swing::HandSwingEvent;
use valence::client::message::SendMessage;
use valence::inventory::HeldItem;
use valence::prelude::*;
use valence::world_time::{ChangeTrackingTimeBroadcast, LinearTimeTicking, WorldTime};

const SPAWN_Y: i32 = 64;

pub fn main() {
tracing_subscriber::fmt().init();

App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(init_clients)
.add_system(despawn_disconnected_clients)
.add_system(handle_adjust_speed)
.add_system(handle_display_time)
.run();
}

fn setup(
mut commands: Commands,
server: Res<Server>,
biomes: Res<BiomeRegistry>,
dimensions: Res<DimensionTypeRegistry>,
) {
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).insert((
WorldTime::default(),
ChangeTrackingTimeBroadcast,
LinearTimeTicking { speed: 1 },
));
}

fn init_clients(
mut clients: Query<(&mut Client, &mut Location, &mut Position, &mut Inventory), Added<Client>>,
instances: Query<Entity, With<Instance>>,
) {
for (mut client, mut loc, mut pos, mut inv) in &mut clients {
loc.0 = instances.single();
pos.set([0.5, SPAWN_Y as f64 + 1.0, 0.5]);

client.send_chat_message("Let's control time!");
client.send_chat_message("- Left click the clock to make time run faster");
client.send_chat_message("- Left click the trident to make time run slower");

inv.set_slot(36, Some(ItemStack::new(ItemKind::Trident, 1, None)));
inv.set_slot(37, Some(ItemStack::new(ItemKind::Clock, 1, None)));
}
}

fn handle_adjust_speed(
mut instances: Query<&mut LinearTimeTicking, With<Instance>>,
clients: Query<(&Inventory, &HeldItem)>,
mut events: EventReader<HandSwingEvent>,
) {
for e in events.iter() {
let Ok((inv, inv_state)) = clients.get(e.client) else {
continue;
};

let mut ltt = instances.single_mut();

let slot = inv_state.slot();
let Some(is) = inv.slot(slot) else {
continue;
};

match is.item {
ItemKind::Clock => ltt.speed += 1,
ItemKind::Trident => ltt.speed -= 1,
_ => (),
}
}
}

fn handle_display_time(
mut clients: Query<&mut Client>,
instances: Query<(&WorldTime, &LinearTimeTicking), With<Instance>>,
) {
for mut client in clients.iter_mut() {
let (time, ltt) = instances.single();

client.send_action_bar_message(format!(
"Time: {} / {} per tick",
time.time_of_day, ltt.speed
));
}
}
rj00a marked this conversation as resolved.
Show resolved Hide resolved

// Add more systems here!
rj00a marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions crates/valence/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub use valence_network as network;
pub use valence_player_list as player_list;
#[cfg(feature = "world_border")]
pub use valence_world_border as world_border;
#[cfg(feature = "world_time")]
pub use valence_world_time as world_time;
pub use {
bevy_app as app, bevy_ecs as ecs, glam, valence_biome as biome, valence_block as block,
valence_client as client, valence_dimension as dimension, valence_entity as entity,
Expand Down Expand Up @@ -169,6 +171,11 @@ impl PluginGroup for DefaultPlugins {
group = group.add(valence_world_border::WorldBorderPlugin);
}

#[cfg(feature = "world_time")]
{
group = group.add(valence_world_time::WorldTimePlugin);
}

group
}
}
1 change: 1 addition & 0 deletions crates/valence/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,4 @@ mod example;
mod inventory;
mod weather;
mod world_border;
mod world_time;
102 changes: 102 additions & 0 deletions crates/valence/src/tests/world_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use bevy_app::App;
use valence_entity::Location;
use valence_world_time::packet::WorldTimeUpdateS2c;
use valence_world_time::{
ChangeTrackingTimeBroadcast, DayPhase, IntervalTimeBroadcast, LinearTimeTicking,
LinearWorldAging, MoonPhase, WorldTime, DAY_LENGTH,
};

use super::scenario_single_client;

#[test]
fn test_world_time_add() {
let mut time = WorldTime::default();
time.add_time(10);

assert_eq!(10, time.time_of_day);
assert!(time.client_time_ticking());

time.set_client_time_ticking(false);
assert_eq!(-10, time.time_of_day);

time.add_time(-11);
assert_eq!(-i64::MAX, time.time_of_day);
}

#[test]
fn test_world_time_modifications() {
let mut time = WorldTime::default();

time.set_day(3);
time.set_current_day_time(12000);
assert_eq!(3 * DAY_LENGTH + 12000, time.time_of_day);

time.warp_to_next_day_phase(DayPhase::Day);
assert_eq!(4 * DAY_LENGTH, time.time_of_day);

time.set_day(0);
time.wrap_to_next_moon_phase(MoonPhase::NewMoon);
assert_eq!(4 * DAY_LENGTH + DayPhase::Night as i64, time.time_of_day)
}

#[test]
fn test_interval_time_broadcast() {
let mut app = App::new();
let (client, mut client_helper) = scenario_single_client(&mut app);
let loc: &Location = app.world.entity(client).get().unwrap();

app.world
.entity_mut(loc.0)
.insert((WorldTime::default(), IntervalTimeBroadcast::new(20)));

for _ in 0..20 {
app.update()
}

client_helper
.collect_sent()
.assert_count::<WorldTimeUpdateS2c>(2);
}

#[test]
fn test_change_tracking_broadcast() {
let mut app = App::new();
let (client, mut client_helper) = scenario_single_client(&mut app);
let loc: &Location = app.world.entity(client).get().unwrap();
let ins_ent = loc.0;

app.world
.entity_mut(ins_ent)
.insert((WorldTime::default(), ChangeTrackingTimeBroadcast));

app.world
.entity_mut(ins_ent)
.get_mut::<WorldTime>()
.unwrap()
.add_time(1);

app.update();
client_helper
.collect_sent()
.assert_count::<WorldTimeUpdateS2c>(1);
}

#[test]
fn test_time_ticking() {
let mut app = App::new();
let (client, _) = scenario_single_client(&mut app);
let loc: &Location = app.world.entity(client).get().unwrap();
let ins_ent = loc.0;

app.world.entity_mut(ins_ent).insert((
WorldTime::default(),
LinearTimeTicking { speed: 1 },
LinearWorldAging { speed: 1 },
));

app.update();

let time: &WorldTime = app.world.entity(ins_ent).get().unwrap();
assert_eq!(1, time.world_age);
assert_eq!(1, time.time_of_day);
}
11 changes: 0 additions & 11 deletions crates/valence_instance/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@ pub struct WorldEventS2c {
pub disable_relative_volume: bool,
}

#[derive(Copy, Clone, Debug, Encode, Decode, Packet)]
#[packet(id = packet_id::WORLD_TIME_UPDATE_S2C)]
pub struct WorldTimeUpdateS2c {
/// The age of the world in 1/20ths of a second.
pub world_age: i64,
/// The current time of day in 1/20ths of a second.
/// The value should be in the range \[0, 24000].
/// 6000 is noon, 12000 is sunset, and 18000 is midnight.
pub time_of_day: i64,
}

#[derive(Clone, Debug, Encode, Decode, Packet)]
#[packet(id = packet_id::CHUNK_BIOME_DATA_S2C)]
pub struct ChunkBiomeDataS2c<'a> {
Expand Down
14 changes: 14 additions & 0 deletions crates/valence_world_time/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "valence_world_time"
version.workspace = true
edition.workspace = true

[dependencies]
bevy_app.workspace = true
bevy_ecs.workspace = true
glam.workspace = true
valence_client.workspace = true
valence_core.workspace = true
valence_entity.workspace = true
valence_instance.workspace = true
valence_registry.workspace = true
Loading