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

Conversation

tachibanayui
Copy link
Contributor

Description

Implementation of world time for instance. Support sets the time explicitly, advances the time and adjusts the frequency that the server sends WorldTimeUpdateS2c packet to clients.

Playground:
use valence::client::despawn_disconnected_clients;
use valence::client::hand_swing::HandSwingEvent;
use valence::client::message::SendMessage;
use valence::inventory::HeldItem;
use valence::network::ConnectionMode;
use valence::prelude::*;
use valence::world_time::{ChangeTrackingTimeBroadcast, LinearTimeTicking, WorldTime};

#[allow(unused_imports)]
use crate::extras::*;

const SPAWN_Y: i32 = 64;

pub fn build_app(app: &mut App) {
    app.insert_resource(NetworkSettings {
        connection_mode: ConnectionMode::Offline,
        ..Default::default()
    })
    .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)
    .add_system(toggle_gamemode_on_sneak.in_schedule(EventLoopSchedule));
}

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
        ));
    }
}

// Add more systems here!

example: cargo run --package valence --example time_travel
tests: cargo test --package valence --lib -- tests::world_time

Related
part of #210

@tachibanayui tachibanayui marked this pull request as ready for review June 17, 2023 07:24
@tachibanayui tachibanayui changed the title initial world time implementation world time implementation Jun 17, 2023
@rj00a rj00a linked an issue Jun 17, 2023 that may be closed by this pull request
3 tasks
Copy link
Collaborator

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just a few nitpicks.

crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence/examples/time_travel.rs Outdated Show resolved Hide resolved
crates/valence/examples/time_travel.rs Outdated Show resolved Hide resolved
@tachibanayui tachibanayui requested review from dyc3 and rj00a June 18, 2023 04:01
crates/README.md Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
crates/valence_world_time/src/lib.rs Outdated Show resolved Hide resolved
WorldTime day and time getter will no longer produce negative values
@tachibanayui tachibanayui requested a review from rj00a June 18, 2023 10:39
Copy link
Member

@rj00a rj00a left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One problem I see here is that the LinearTimeTicking and LinearWorldAging components do not mix well with the ChangeTrackingTimeBroadcast component, since it constantly triggers change detection and spams the client with world time packets. This is problematic for WorldTimeBundle.

@tachibanayui
Copy link
Contributor Author

One problem I see here is that the LinearTimeTicking and LinearWorldAging components do not mix well with the ChangeTrackingTimeBroadcast component, since it constantly triggers change detection and spams the client with world time packets. This is problematic for WorldTimeBundle.

I might have to redesign the API a bit. I will create a Broadcaster kind of a mix of the 2 current broadcasters.

struct ScheduledBroadcaster {
    pub next_tick: i64;
    pub interval: u64;
}

And a prebuilt query to provide some API sugarily. I want to separate WorldTime and ScheduledBroadcaster because the user can choose the level of abstraction they want

struct TimeControllingQuery {
    pub time: &mut 'static WorldTime,
    pub schedule: &mut 'static ScheduledBroadcaster 
}

impl TimeControllingQuery {
    pub set_time(&mut self, time: i64) { ... }
    pub set_time_defered(&mut self, time: i64) { ... }
}

to change the time we can do:

let mut query: TimeControllingQuery = ...;
// Update immediately
query.set_time(10);

// Scheduled
for i in 11..20 {
    query.set_time_defered(i);
}

What do you think about this API?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optional component for Instance used to control time, weather, world border, etc.
3 participants