Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Objective - Adds support for Scoreboards via a new crate, `valence_scoreboard` - closes #435 # TODO - [x] When the scores update, only send the diff to clients - [x] handle layer switching - [x] docs - [x] update the ctf example to show scoreboards - [x] unit tests <details> <summary>Playground used for testing</summary> ```rust use valence::client::despawn_disconnected_clients; use valence::entity::pig::PigEntityBundle; use valence::log::LogPlugin; use valence::network::ConnectionMode; use valence::prelude::*; use valence::scoreboard::*; #[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.build().disable::<LogPlugin>()) .add_systems(Startup, setup) .add_systems(EventLoopUpdate, toggle_gamemode_on_sneak) .add_systems(Update, (init_clients, despawn_disconnected_clients)) .add_systems(Update, update_visible_layers) .run(); } fn setup( mut commands: Commands, server: Res<Server>, biomes: Res<BiomeRegistry>, dimensions: Res<DimensionTypeRegistry>, ) { let mut layer = LayerBundle::new(ident!("overworld"), &dimensions, &biomes, &server); for z in -5..5 { for x in -5..5 { layer.chunk.insert_chunk([x, z], UnloadedChunk::new()); } } for z in -25..25 { for x in -25..25 { layer .chunk .set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK); } } let main_layer = commands.spawn(layer).id(); let foo_layer = commands.spawn(EntityLayer::new(&server)).id(); let bar_layer = commands.spawn(EntityLayer::new(&server)).id(); let sample_ent = PigEntityBundle { position: Position([0.0, SPAWN_Y as f64 + 1.0, 0.0].into()), layer: EntityLayerId(main_layer), ..Default::default() }; let foo_objective = ObjectiveBundle { name: Objective::new("foo"), display: ObjectiveDisplay("Foo".bold().color(Color::RED)), render_type: Default::default(), scores: ObjectiveScores::with_map([("foo".to_owned(), 3)]), position: ScoreboardPosition::Sidebar, layer: EntityLayerId(foo_layer), }; let bar_objective = ObjectiveBundle { name: Objective::new("bar"), display: ObjectiveDisplay("Bar".bold().color(Color::BLUE)), render_type: Default::default(), scores: ObjectiveScores::with_map([("bar".to_owned(), 7)]), position: ScoreboardPosition::Sidebar, layer: EntityLayerId(bar_layer), }; commands.spawn(sample_ent); commands.spawn(foo_objective); commands.spawn(bar_objective); let objectives = Objectives { main_layer, objectives: vec![foo_layer, bar_layer], current: 0, }; commands.insert_resource(objectives); } fn init_clients( mut clients: Query< ( &mut EntityLayerId, &mut VisibleChunkLayer, &mut VisibleEntityLayers, &mut Position, &mut GameMode, ), Added<Client>, >, layers: Query<Entity, (With<ChunkLayer>, With<EntityLayer>)>, ) { for ( mut layer_id, mut visible_chunk_layer, mut visible_entity_layers, mut pos, mut game_mode, ) in &mut clients { let layer = layers.single(); layer_id.0 = layer; visible_chunk_layer.0 = layer; visible_entity_layers.0.insert(layer); pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]); *game_mode = GameMode::Creative; } } #[derive(Resource)] struct Objectives { main_layer: Entity, objectives: Vec<Entity>, current: usize, } fn update_visible_layers( mut clients: Query<&mut VisibleEntityLayers, With<Client>>, mut objectives: ResMut<Objectives>, server: Res<Server>, ) { let cycle_duration: usize = 20; if server.current_tick() % (cycle_duration as i64) != 0 { return; } objectives.current = (objectives.current + 1) % objectives.objectives.len(); for mut visible_entity_layers in &mut clients { visible_entity_layers.0.clear(); visible_entity_layers.0.insert(objectives.main_layer); visible_entity_layers .0 .insert(objectives.objectives[objectives.current]); } } ``` </details>
- Loading branch information