#![allow(clippy::type_complexity)] use valence::interact_block::InteractBlockEvent; use valence::inventory::HeldItem; use valence::prelude::*; const SPAWN_Y: i32 = 64; pub fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) .add_systems( Update, ( init_clients, despawn_disconnected_clients, toggle_gamemode_on_sneak, digging, place_blocks, ), ) .run(); } fn setup( mut commands: Commands, server: Res, dimensions: Res, biomes: Res, ) { 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 -50..50 { for x in -50..50 { layer .chunk .set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK); } } commands.spawn(layer); } fn init_clients( mut clients: Query< ( &mut Client, &mut EntityLayerId, &mut VisibleChunkLayer, &mut VisibleEntityLayers, &mut Position, &mut GameMode, ), Added, >, layers: Query, With)>, ) { for ( mut client, 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; client.send_chat_message("Welcome to Valence! Build something cool.".italic()); } } fn toggle_gamemode_on_sneak( mut clients: Query<&mut GameMode>, mut events: EventReader, ) { for event in events.iter() { let Ok(mut mode) = clients.get_mut(event.client) else { continue; }; if event.state == SneakState::Start { *mode = match *mode { GameMode::Survival => GameMode::Creative, GameMode::Creative => GameMode::Survival, _ => GameMode::Creative, }; } } } fn digging( clients: Query<&GameMode>, mut layers: Query<&mut ChunkLayer>, mut events: EventReader, ) { let mut layer = layers.single_mut(); for event in events.iter() { let Ok(game_mode) = clients.get(event.client) else { continue; }; if (*game_mode == GameMode::Creative && event.state == DiggingState::Start) || (*game_mode == GameMode::Survival && event.state == DiggingState::Stop) { layer.set_block(event.position, BlockState::AIR); } } } fn place_blocks( mut clients: Query<(&mut Inventory, &GameMode, &HeldItem)>, mut layers: Query<&mut ChunkLayer>, mut events: EventReader, ) { let mut layer = layers.single_mut(); for event in events.iter() { let Ok((mut inventory, game_mode, held)) = clients.get_mut(event.client) else { continue; }; if event.hand != Hand::Main { continue; } // get the held item let slot_id = held.slot(); let Some(stack) = inventory.slot(slot_id) else { // no item in the slot continue; }; let Some(block_kind) = BlockKind::from_item_kind(stack.item) else { // can't place this item as a block continue; }; if *game_mode == GameMode::Survival { // check if the player has the item in their inventory and remove // it. if stack.count() > 1 { let count = stack.count(); inventory.set_slot_amount(slot_id, count - 1); } else { inventory.set_slot(slot_id, None); } } let real_pos = event.position.get_in_direction(event.face); layer.set_block(real_pos, block_kind.to_state()); } }