valence/crates/valence/examples/particles.rs
Ryan Johnson 2597e92b8f
Reorganize Packages (#180)
Moves all the packages to the `crates/` directory.
2022-12-29 16:51:05 -08:00

317 lines
9.6 KiB
Rust

use std::fmt;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicUsize, Ordering};
use valence::prelude::*;
pub fn main() -> ShutdownResult {
tracing_subscriber::fmt().init();
valence::start_server(
Game {
player_count: AtomicUsize::new(0),
},
ServerState {
player_list: None,
particle_list: create_particle_vec(),
particle_idx: 0,
},
)
}
struct Game {
player_count: AtomicUsize,
}
struct ServerState {
player_list: Option<PlayerListId>,
particle_list: Vec<Particle>,
particle_idx: usize,
}
const MAX_PLAYERS: usize = 10;
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, 0);
#[async_trait]
impl Config for Game {
type ServerState = ServerState;
type ClientState = EntityId;
type EntityState = ();
type WorldState = ();
type ChunkState = ();
type PlayerListState = ();
type InventoryState = ();
async fn server_list_ping(
&self,
_server: &SharedServer<Self>,
_remote_addr: SocketAddr,
_protocol_version: i32,
) -> ServerListPing {
ServerListPing::Respond {
online_players: self.player_count.load(Ordering::SeqCst) as i32,
max_players: MAX_PLAYERS as i32,
player_sample: Default::default(),
description: "Hello Valence!".color(Color::AQUA),
favicon_png: Some(
include_bytes!("../../../assets/logo-64x64.png")
.as_slice()
.into(),
),
}
}
fn init(&self, server: &mut Server<Self>) {
let (_, world) = server.worlds.insert(DimensionId::default(), ());
server.state.player_list = Some(server.player_lists.insert(()).0);
let size = 5;
for z in -size..size {
for x in -size..size {
world.chunks.insert([x, z], UnloadedChunk::default(), ());
}
}
world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK);
}
fn update(&self, server: &mut Server<Self>) {
let (world_id, _) = server.worlds.iter_mut().next().expect("missing world");
server.clients.retain(|_, client| {
if client.created_this_tick() {
if self
.player_count
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
(count < MAX_PLAYERS).then_some(count + 1)
})
.is_err()
{
client.disconnect("The server is full!".color(Color::RED));
return false;
}
match server
.entities
.insert_with_uuid(EntityKind::Player, client.uuid(), ())
{
Some((id, _)) => client.state = id,
None => {
client.disconnect("Conflicting UUID");
return false;
}
}
client.respawn(world_id);
client.set_flat(true);
client.set_game_mode(GameMode::Creative);
client.teleport(
[
SPAWN_POS.x as f64 + 0.5,
SPAWN_POS.y as f64 + 1.0,
SPAWN_POS.z as f64 + 0.5,
],
0.0,
0.0,
);
client.set_player_list(server.state.player_list.clone());
client.send_message("Sneak to speed up the cycling of particles");
if let Some(id) = &server.state.player_list {
server.player_lists.get_mut(id).insert(
client.uuid(),
client.username(),
client.textures().cloned(),
client.game_mode(),
0,
None,
true,
);
}
}
if client.is_disconnected() {
self.player_count.fetch_sub(1, Ordering::SeqCst);
if let Some(id) = &server.state.player_list {
server.player_lists[id].remove(client.uuid());
}
server.entities[client.state].set_deleted(true);
return false;
}
let entity = server
.entities
.get_mut(client.state)
.expect("missing player entity");
while let Some(event) = client.next_event() {
event.handle_default(client, entity);
}
true
});
let players_are_sneaking = server.clients.iter().any(|(_, client)| -> bool {
let player = &server.entities[client.state];
if let TrackedData::Player(data) = player.data() {
return data.get_pose() == Pose::Sneaking;
}
false
});
let cycle_time = if players_are_sneaking { 5 } else { 30 };
if !server.clients.is_empty() && server.current_tick() % cycle_time == 0 {
if server.state.particle_idx == server.state.particle_list.len() {
server.state.particle_idx = 0;
}
let pos = [
SPAWN_POS.x as f64 + 0.5,
SPAWN_POS.y as f64 + 2.0,
SPAWN_POS.z as f64 + 5.5,
];
let offset = [0.5, 0.5, 0.5];
let particle = &server.state.particle_list[server.state.particle_idx];
server.clients.iter_mut().for_each(|(_, client)| {
client.set_title(
"",
dbg_name(particle).bold(),
SetTitleAnimationTimes {
fade_in: 0,
stay: 100,
fade_out: 2,
},
);
client.play_particle(particle, true, pos, offset, 0.1, 100);
});
server.state.particle_idx += 1;
}
}
}
fn dbg_name(dbg: &impl fmt::Debug) -> String {
let string = format!("{dbg:?}");
string
.split_once(|ch: char| !ch.is_ascii_alphabetic())
.map(|(fst, _)| fst.to_owned())
.unwrap_or(string)
}
fn create_particle_vec() -> Vec<Particle> {
vec![
Particle::AmbientEntityEffect,
Particle::AngryVillager,
Particle::Block(BlockState::OAK_PLANKS),
Particle::BlockMarker(BlockState::GOLD_BLOCK),
Particle::Bubble,
Particle::Cloud,
Particle::Crit,
Particle::DamageIndicator,
Particle::DragonBreath,
Particle::DrippingLava,
Particle::FallingLava,
Particle::LandingLava,
Particle::DrippingWater,
Particle::FallingWater,
Particle::Dust {
rgb: [1.0, 1.0, 0.0],
scale: 2.0,
},
Particle::DustColorTransition {
from_rgb: [1.0, 0.0, 0.0],
scale: 2.0,
to_rgb: [0.0, 1.0, 0.0],
},
Particle::Effect,
Particle::ElderGuardian,
Particle::EnchantedHit,
Particle::Enchant,
Particle::EndRod,
Particle::EntityEffect,
Particle::ExplosionEmitter,
Particle::Explosion,
Particle::SonicBoom,
Particle::FallingDust(BlockState::RED_SAND),
Particle::Firework,
Particle::Fishing,
Particle::Flame,
Particle::SculkSoul,
Particle::SculkCharge { roll: 1.0 },
Particle::SculkChargePop,
Particle::SoulFireFlame,
Particle::Soul,
Particle::Flash,
Particle::HappyVillager,
Particle::Composter,
Particle::Heart,
Particle::InstantEffect,
Particle::Item(None),
Particle::Item(Some(ItemStack::new(ItemKind::IronPickaxe, 1, None))),
Particle::VibrationBlock {
block_pos: SPAWN_POS,
ticks: 50,
},
Particle::VibrationEntity {
entity_id: 0,
entity_eye_height: 1.0,
ticks: 50,
},
Particle::ItemSlime,
Particle::ItemSnowball,
Particle::LargeSmoke,
Particle::Lava,
Particle::Mycelium,
Particle::Note,
Particle::Poof,
Particle::Portal,
Particle::Rain,
Particle::Smoke,
Particle::Sneeze,
Particle::Spit,
Particle::SquidInk,
Particle::SweepAttack,
Particle::TotemOfUndying,
Particle::Underwater,
Particle::Splash,
Particle::Witch,
Particle::BubblePop,
Particle::CurrentDown,
Particle::BubbleColumnUp,
Particle::Nautilus,
Particle::Dolphin,
Particle::CampfireCosySmoke,
Particle::CampfireSignalSmoke,
Particle::DrippingHoney,
Particle::FallingHoney,
Particle::LandingHoney,
Particle::FallingNectar,
Particle::FallingSporeBlossom,
Particle::Ash,
Particle::CrimsonSpore,
Particle::WarpedSpore,
Particle::SporeBlossomAir,
Particle::DrippingObsidianTear,
Particle::FallingObsidianTear,
Particle::LandingObsidianTear,
Particle::ReversePortal,
Particle::WhiteAsh,
Particle::SmallFlame,
Particle::Snowflake,
Particle::DrippingDripstoneLava,
Particle::FallingDripstoneLava,
Particle::DrippingDripstoneWater,
Particle::FallingDripstoneWater,
Particle::GlowSquidInk,
Particle::Glow,
Particle::WaxOn,
Particle::WaxOff,
Particle::ElectricSpark,
Particle::Scrape,
]
}