classics/src/server.rs

195 lines
4.6 KiB
Rust

pub mod config;
mod network;
use std::sync::Arc;
use tokio::{net::TcpListener, sync::RwLock};
use crate::{
level::{
block::{BlockType, BLOCK_INFO},
BlockUpdate, Level,
},
player::Player,
util::neighbors_minus_up,
CONFIG_FILE,
};
use self::config::ServerConfig;
const TICK_DURATION: std::time::Duration = std::time::Duration::from_millis(50);
/// the server
#[derive(Debug)]
pub struct Server {
/// shared server data
pub data: Arc<RwLock<ServerData>>,
/// the server's listener
pub listener: TcpListener,
}
/// shared server data
#[derive(Debug)]
pub struct ServerData {
/// the level
pub level: Level,
/// list of players connected to the server
pub players: Vec<Player>,
/// list of player ids which have been freed up
pub free_player_ids: Vec<i8>,
/// the server's config
pub config: ServerConfig,
/// whether the server config needs to be resaved or not
pub config_needs_saving: bool,
}
impl Server {
/// creates a new server with a generated level
pub async fn new(config: ServerConfig) -> std::io::Result<Self> {
println!("generating level");
let mut rng = rand::thread_rng();
let mut level = Level::new(
config.level_size.x,
config.level_size.y,
config.level_size.z,
);
config.generation.generate(&mut level, &mut rng);
println!("done!");
Self::new_with_level(config, level).await
}
/// creates a new server with the given level
pub async fn new_with_level(config: ServerConfig, level: Level) -> std::io::Result<Self> {
let listener = TcpListener::bind("0.0.0.0:25565").await?;
Ok(Self {
data: Arc::new(RwLock::new(ServerData {
level,
players: Default::default(),
free_player_ids: Vec::new(),
config,
config_needs_saving: true,
})),
listener,
})
}
/// starts the server
pub async fn run(&mut self) -> std::io::Result<()> {
let data = self.data.clone();
tokio::spawn(async move {
handle_ticks(data).await;
});
loop {
let (stream, addr) = self.listener.accept().await?;
println!("connection from {addr}");
let data = self.data.clone();
tokio::spawn(async move {
network::handle_stream(stream, addr, data)
.await
.expect("failed to handle client stream");
});
}
}
}
/// function to tick the server
async fn handle_ticks(data: Arc<RwLock<ServerData>>) {
let mut current_tick = 0;
loop {
{
let mut data = data.write().await;
tick(&mut data, current_tick);
if data.config_needs_saving {
std::fs::write(
CONFIG_FILE,
serde_json::to_string_pretty(&data.config)
.expect("failed to serialize default config"),
)
.expect("failed to save config file");
data.config_needs_saving = false;
}
}
current_tick = current_tick.wrapping_add(1);
tokio::time::sleep(TICK_DURATION).await;
}
}
/// function which ticks the server once
fn tick(data: &mut ServerData, tick: usize) {
let level = &mut data.level;
let mut packets = level.apply_updates();
let awaiting_update = std::mem::take(&mut level.awaiting_update);
for index in awaiting_update {
let (x, y, z) = level.coordinates(index);
let block_id = level.get_block(x, y, z);
let block = BLOCK_INFO.get(&block_id).expect("should never fail");
match &block.block_type {
BlockType::FluidFlowing {
stationary,
ticks_to_spread,
} => {
if tick % ticks_to_spread == 0 {
let update = BlockUpdate {
index,
block: *stationary,
};
level.updates.push(update);
for (nx, ny, nz) in neighbors_minus_up(level, x, y, z) {
let block_at = BLOCK_INFO
.get(&level.get_block(nx, ny, nz))
.expect("missing block");
let update = if matches!(block_at.block_type, BlockType::NonSolid) {
level.awaiting_update.insert(level.index(nx, ny, nz));
BlockUpdate {
index: level.index(nx, ny, nz),
block: block_id,
}
} else {
continue;
};
level.updates.push(update);
}
} else {
level.awaiting_update.insert(index);
}
}
BlockType::FluidStationary { moving } => {
let mut needs_update = false;
for (nx, ny, nz) in neighbors_minus_up(level, x, y, z) {
if matches!(
BLOCK_INFO
.get(&level.get_block(nx, ny, nz))
.expect("missing block")
.block_type,
BlockType::NonSolid
) {
needs_update = true;
break;
}
}
if needs_update {
let index = level.index(x, y, z);
level.updates.push(BlockUpdate {
index,
block: *moving,
});
level.awaiting_update.insert(index);
}
}
_ => {}
}
}
packets.extend(level.apply_updates());
for packet in packets {
for player in &mut data.players {
player.packets_to_send.push(packet.clone());
}
}
}