mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-26 15:51:29 +11:00
2597e92b8f
Moves all the packages to the `crates/` directory.
317 lines
9.6 KiB
Rust
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,
|
|
]
|
|
}
|