mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Add custom data
This commit is contained in:
parent
0ef05bb0d0
commit
865ab76699
|
@ -2470,11 +2470,11 @@ pub fn build() -> anyhow::Result<()> {
|
||||||
#(#entity_structs)*
|
#(#entity_structs)*
|
||||||
|
|
||||||
/// An enum encoding the type of an entity along with any data specific to that entity type.
|
/// An enum encoding the type of an entity along with any data specific to that entity type.
|
||||||
pub enum EntityData {
|
pub enum EntityState {
|
||||||
#(#entity_kind_variants(#entity_kind_variants),)*
|
#(#entity_kind_variants(#entity_kind_variants),)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EntityData {
|
impl EntityState {
|
||||||
pub(super) fn new(kind: EntityKind) -> Self {
|
pub(super) fn new(kind: EntityKind) -> Self {
|
||||||
match kind {
|
match kind {
|
||||||
#(EntityKind::#entity_kind_variants => Self::#entity_kind_variants(#entity_kind_variants::new()),)*
|
#(EntityKind::#entity_kind_variants => Self::#entity_kind_variants(#entity_kind_variants::new()),)*
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use valence::biome::Biome;
|
use valence::biome::Biome;
|
||||||
use valence::block::BlockState;
|
use valence::block::BlockState;
|
||||||
use valence::client::{ClientId, Event, Hand};
|
use valence::client::{Event, Hand};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::{Dimension, DimensionId};
|
use valence::dimension::{Dimension, DimensionId};
|
||||||
use valence::entity::data::Pose;
|
use valence::entity::state::Pose;
|
||||||
use valence::entity::{EntityData, EntityId, EntityKind};
|
use valence::entity::{EntityId, EntityKind, EntityState};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::{async_trait, ident};
|
use valence::{async_trait, ident};
|
||||||
|
@ -24,27 +22,32 @@ pub fn main() -> ShutdownResult {
|
||||||
.parse_default_env()
|
.parse_default_env()
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
valence::start_server(Game {
|
valence::start_server(
|
||||||
player_count: AtomicUsize::new(0),
|
Game {
|
||||||
state: Mutex::new(State {
|
player_count: AtomicUsize::new(0),
|
||||||
player_entities: HashMap::new(),
|
},
|
||||||
|
ServerData {
|
||||||
board: vec![false; SIZE_X * SIZE_Z].into_boxed_slice(),
|
board: vec![false; SIZE_X * SIZE_Z].into_boxed_slice(),
|
||||||
board_buf: vec![false; SIZE_X * SIZE_Z].into_boxed_slice(),
|
board_buf: vec![false; SIZE_X * SIZE_Z].into_boxed_slice(),
|
||||||
}),
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Game {
|
struct Game {
|
||||||
player_count: AtomicUsize,
|
player_count: AtomicUsize,
|
||||||
state: Mutex<State>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct ServerData {
|
||||||
player_entities: HashMap<ClientId, EntityId>,
|
|
||||||
board: Box<[bool]>,
|
board: Box<[bool]>,
|
||||||
board_buf: Box<[bool]>,
|
board_buf: Box<[bool]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ClientData {
|
||||||
|
/// The client's player entity.
|
||||||
|
player: EntityId,
|
||||||
|
}
|
||||||
|
|
||||||
const MAX_PLAYERS: usize = 10;
|
const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
const SIZE_X: usize = 100;
|
const SIZE_X: usize = 100;
|
||||||
|
@ -53,6 +56,12 @@ const BOARD_Y: i32 = 50;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
|
type ChunkData = ();
|
||||||
|
type ClientData = ClientData;
|
||||||
|
type EntityData = ();
|
||||||
|
type ServerData = ServerData;
|
||||||
|
type WorldData = ();
|
||||||
|
|
||||||
fn max_connections(&self) -> usize {
|
fn max_connections(&self) -> usize {
|
||||||
// We want status pings to be successful even if the server is full.
|
// We want status pings to be successful even if the server is full.
|
||||||
MAX_PLAYERS + 64
|
MAX_PLAYERS + 64
|
||||||
|
@ -80,7 +89,7 @@ impl Config for Game {
|
||||||
|
|
||||||
async fn server_list_ping(
|
async fn server_list_ping(
|
||||||
&self,
|
&self,
|
||||||
_server: &SharedServer,
|
_server: &SharedServer<Self>,
|
||||||
_remote_addr: SocketAddr,
|
_remote_addr: SocketAddr,
|
||||||
) -> ServerListPing {
|
) -> ServerListPing {
|
||||||
ServerListPing::Respond {
|
ServerListPing::Respond {
|
||||||
|
@ -91,18 +100,18 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, server: &mut Server) {
|
fn init(&self, server: &mut Server<Self>) {
|
||||||
let world = server.worlds.create(DimensionId::default()).1;
|
let world = server.worlds.create(DimensionId::default(), ()).1;
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
|
|
||||||
for chunk_z in -2..Integer::div_ceil(&(SIZE_X as i32), &16) + 2 {
|
for chunk_z in -2..Integer::div_ceil(&(SIZE_X as i32), &16) + 2 {
|
||||||
for chunk_x in -2..Integer::div_ceil(&(SIZE_Z as i32), &16) + 2 {
|
for chunk_x in -2..Integer::div_ceil(&(SIZE_Z as i32), &16) + 2 {
|
||||||
world.chunks.create((chunk_x as i32, chunk_z as i32));
|
world.chunks.create((chunk_x as i32, chunk_z as i32), ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &mut Server) {
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
let spawn_pos = [
|
let spawn_pos = [
|
||||||
|
@ -111,13 +120,7 @@ impl Config for Game {
|
||||||
SIZE_Z as f64 / 2.0,
|
SIZE_Z as f64 / 2.0,
|
||||||
];
|
];
|
||||||
|
|
||||||
let State {
|
server.clients.retain(|_, client| {
|
||||||
player_entities,
|
|
||||||
board,
|
|
||||||
board_buf,
|
|
||||||
} = &mut *self.state.lock().unwrap();
|
|
||||||
|
|
||||||
server.clients.retain(|client_id, client| {
|
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_tick() == server.shared.current_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
|
@ -142,14 +145,13 @@ impl Config for Game {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
player_entities.insert(
|
let player_id = server
|
||||||
client_id,
|
.entities
|
||||||
server
|
.create_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||||
.entities
|
.unwrap()
|
||||||
.create_with_uuid(EntityKind::Player, client.uuid())
|
.0;
|
||||||
.unwrap()
|
|
||||||
.0,
|
client.data = ClientData { player: player_id };
|
||||||
);
|
|
||||||
|
|
||||||
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
||||||
client.send_message("Hold the left mouse button to bring blocks to life.".italic());
|
client.send_message("Hold the left mouse button to bring blocks to life.".italic());
|
||||||
|
@ -157,8 +159,7 @@ impl Config for Game {
|
||||||
|
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
let id = player_entities.remove(&client_id).unwrap();
|
server.entities.delete(client.data.player);
|
||||||
server.entities.delete(id);
|
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -166,11 +167,8 @@ impl Config for Game {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
for (client_id, client) in server.clients.iter_mut() {
|
for (_, client) in server.clients.iter_mut() {
|
||||||
let player = server
|
let player = server.entities.get_mut(client.data.player).unwrap();
|
||||||
.entities
|
|
||||||
.get_mut(player_entities[&client_id])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
while let Some(event) = client.pop_event() {
|
while let Some(event) = client.pop_event() {
|
||||||
match event {
|
match event {
|
||||||
|
@ -179,7 +177,8 @@ impl Config for Game {
|
||||||
&& (0..SIZE_Z as i32).contains(&position.z)
|
&& (0..SIZE_Z as i32).contains(&position.z)
|
||||||
&& position.y == BOARD_Y
|
&& position.y == BOARD_Y
|
||||||
{
|
{
|
||||||
board[position.x as usize + position.z as usize * SIZE_X] = true;
|
server.data.board[position.x as usize + position.z as usize * SIZE_X] =
|
||||||
|
true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Movement { .. } => {
|
Event::Movement { .. } => {
|
||||||
|
@ -195,29 +194,29 @@ impl Config for Game {
|
||||||
player.set_on_ground(client.on_ground());
|
player.set_on_ground(client.on_ground());
|
||||||
}
|
}
|
||||||
Event::StartSneaking => {
|
Event::StartSneaking => {
|
||||||
if let EntityData::Player(e) = player.data_mut() {
|
if let EntityState::Player(e) = &mut player.state {
|
||||||
e.set_crouching(true);
|
e.set_crouching(true);
|
||||||
e.set_pose(Pose::Sneaking);
|
e.set_pose(Pose::Sneaking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::StopSneaking => {
|
Event::StopSneaking => {
|
||||||
if let EntityData::Player(e) = player.data_mut() {
|
if let EntityState::Player(e) = &mut player.state {
|
||||||
e.set_pose(Pose::Standing);
|
e.set_pose(Pose::Standing);
|
||||||
e.set_crouching(false);
|
e.set_crouching(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::StartSprinting => {
|
Event::StartSprinting => {
|
||||||
if let EntityData::Player(e) = player.data_mut() {
|
if let EntityState::Player(e) = &mut player.state {
|
||||||
e.set_sprinting(true);
|
e.set_sprinting(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::StopSprinting => {
|
Event::StopSprinting => {
|
||||||
if let EntityData::Player(e) = player.data_mut() {
|
if let EntityState::Player(e) = &mut player.state {
|
||||||
e.set_sprinting(false);
|
e.set_sprinting(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::ArmSwing(hand) => {
|
Event::ArmSwing(hand) => {
|
||||||
if let EntityData::Player(e) = player.data_mut() {
|
if let EntityState::Player(e) = &mut player.state {
|
||||||
match hand {
|
match hand {
|
||||||
Hand::Main => e.trigger_swing_main_arm(),
|
Hand::Main => e.trigger_swing_main_arm(),
|
||||||
Hand::Off => e.trigger_swing_offhand(),
|
Hand::Off => e.trigger_swing_offhand(),
|
||||||
|
@ -233,31 +232,36 @@ impl Config for Game {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
board_buf.par_iter_mut().enumerate().for_each(|(i, cell)| {
|
server
|
||||||
let cx = (i % SIZE_X) as i32;
|
.data
|
||||||
let cz = (i / SIZE_Z) as i32;
|
.board_buf
|
||||||
|
.par_iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, cell)| {
|
||||||
|
let cx = (i % SIZE_X) as i32;
|
||||||
|
let cz = (i / SIZE_Z) as i32;
|
||||||
|
|
||||||
let mut live_count = 0;
|
let mut live_count = 0;
|
||||||
for z in cz - 1..=cz + 1 {
|
for z in cz - 1..=cz + 1 {
|
||||||
for x in cx - 1..=cx + 1 {
|
for x in cx - 1..=cx + 1 {
|
||||||
if !(x == cx && z == cz) {
|
if !(x == cx && z == cz) {
|
||||||
let i = x.rem_euclid(SIZE_X as i32) as usize
|
let i = x.rem_euclid(SIZE_X as i32) as usize
|
||||||
+ z.rem_euclid(SIZE_Z as i32) as usize * SIZE_X;
|
+ z.rem_euclid(SIZE_Z as i32) as usize * SIZE_X;
|
||||||
if board[i] {
|
if server.data.board[i] {
|
||||||
live_count += 1;
|
live_count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if board[cx as usize + cz as usize * SIZE_X] {
|
if server.data.board[cx as usize + cz as usize * SIZE_X] {
|
||||||
*cell = (2..=3).contains(&live_count);
|
*cell = (2..=3).contains(&live_count);
|
||||||
} else {
|
} else {
|
||||||
*cell = live_count == 3;
|
*cell = live_count == 3;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mem::swap(board, board_buf);
|
mem::swap(&mut server.data.board, &mut server.data.board_buf);
|
||||||
|
|
||||||
let min_y = server.shared.dimensions().next().unwrap().1.min_y;
|
let min_y = server.shared.dimensions().next().unwrap().1.min_y;
|
||||||
|
|
||||||
|
@ -273,7 +277,7 @@ impl Config for Game {
|
||||||
let cell_z = chunk_z * 16 + z;
|
let cell_z = chunk_z * 16 + z;
|
||||||
|
|
||||||
if cell_x < SIZE_X && cell_z < SIZE_Z {
|
if cell_x < SIZE_X && cell_z < SIZE_Z {
|
||||||
let b = if board[cell_x + cell_z * SIZE_X] {
|
let b = if server.data.board[cell_x + cell_z * SIZE_X] {
|
||||||
BlockState::GRASS_BLOCK
|
BlockState::GRASS_BLOCK
|
||||||
} else {
|
} else {
|
||||||
BlockState::DIRT
|
BlockState::DIRT
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::f64::consts::TAU;
|
use std::f64::consts::TAU;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use valence::async_trait;
|
use valence::async_trait;
|
||||||
|
@ -21,23 +20,34 @@ pub fn main() -> ShutdownResult {
|
||||||
.parse_default_env()
|
.parse_default_env()
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
valence::start_server(Game {
|
valence::start_server(
|
||||||
player_count: AtomicUsize::new(0),
|
Game {
|
||||||
cows: Mutex::new(Vec::new()),
|
player_count: AtomicUsize::new(0),
|
||||||
})
|
},
|
||||||
|
ServerData { cows: Vec::new() },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Game {
|
struct Game {
|
||||||
player_count: AtomicUsize,
|
player_count: AtomicUsize,
|
||||||
cows: Mutex<Vec<EntityId>>,
|
}
|
||||||
|
|
||||||
|
struct ServerData {
|
||||||
|
cows: Vec<EntityId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_PLAYERS: usize = 10;
|
const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -35);
|
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -25);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
|
type ChunkData = ();
|
||||||
|
type ClientData = ();
|
||||||
|
type EntityData = ();
|
||||||
|
type ServerData = ServerData;
|
||||||
|
type WorldData = ();
|
||||||
|
|
||||||
fn max_connections(&self) -> usize {
|
fn max_connections(&self) -> usize {
|
||||||
// We want status pings to be successful even if the server is full.
|
// We want status pings to be successful even if the server is full.
|
||||||
MAX_PLAYERS + 64
|
MAX_PLAYERS + 64
|
||||||
|
@ -50,7 +60,7 @@ impl Config for Game {
|
||||||
|
|
||||||
async fn server_list_ping(
|
async fn server_list_ping(
|
||||||
&self,
|
&self,
|
||||||
_server: &SharedServer,
|
_server: &SharedServer<Self>,
|
||||||
_remote_addr: SocketAddr,
|
_remote_addr: SocketAddr,
|
||||||
) -> ServerListPing {
|
) -> ServerListPing {
|
||||||
ServerListPing::Respond {
|
ServerListPing::Respond {
|
||||||
|
@ -61,27 +71,27 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, server: &mut Server) {
|
fn init(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.create(DimensionId::default());
|
let (world_id, world) = server.worlds.create(DimensionId::default(), ());
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
|
|
||||||
let size = 5;
|
let size = 5;
|
||||||
for z in -size..size {
|
for z in -size..size {
|
||||||
for x in -size..size {
|
for x in -size..size {
|
||||||
world.chunks.create([x, z]);
|
world.chunks.create([x, z], ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK);
|
world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK);
|
||||||
|
|
||||||
self.cows.lock().unwrap().extend((0..200).map(|_| {
|
server.data.cows.extend((0..200).map(|_| {
|
||||||
let (id, e) = server.entities.create(EntityKind::Cow);
|
let (id, e) = server.entities.create(EntityKind::Cow, ());
|
||||||
e.set_world(world_id);
|
e.set_world(world_id);
|
||||||
id
|
id
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &mut Server) {
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
|
@ -122,10 +132,10 @@ impl Config for Game {
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
false
|
return false;
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
let time = server.shared.current_tick() as f64 / server.shared.tick_rate() as f64;
|
let time = server.shared.current_tick() as f64 / server.shared.tick_rate() as f64;
|
||||||
|
@ -134,9 +144,6 @@ impl Config for Game {
|
||||||
.rotated_y(time * TAU * 0.2)
|
.rotated_y(time * TAU * 0.2)
|
||||||
.rotated_z(time * TAU * 0.3);
|
.rotated_z(time * TAU * 0.3);
|
||||||
|
|
||||||
let cows = self.cows.lock().unwrap();
|
|
||||||
let cow_count = cows.len();
|
|
||||||
|
|
||||||
let radius = 6.0 + ((time * TAU / 2.5).sin() + 1.0) / 2.0 * 10.0;
|
let radius = 6.0 + ((time * TAU / 2.5).sin() + 1.0) / 2.0 * 10.0;
|
||||||
|
|
||||||
let player_pos = server
|
let player_pos = server
|
||||||
|
@ -149,11 +156,16 @@ impl Config for Game {
|
||||||
// TODO: hardcoded eye pos.
|
// TODO: hardcoded eye pos.
|
||||||
let eye_pos = Vec3::new(player_pos.x, player_pos.y + 1.6, player_pos.z);
|
let eye_pos = Vec3::new(player_pos.x, player_pos.y + 1.6, player_pos.z);
|
||||||
|
|
||||||
#[allow(clippy::significant_drop_in_scrutinee)]
|
for (cow_id, p) in server
|
||||||
for (cow_id, p) in cows.iter().cloned().zip(fibonacci_spiral(cow_count)) {
|
.data
|
||||||
|
.cows
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.zip(fibonacci_spiral(server.data.cows.len()))
|
||||||
|
{
|
||||||
let cow = server.entities.get_mut(cow_id).expect("missing cow");
|
let cow = server.entities.get_mut(cow_id).expect("missing cow");
|
||||||
let rotated = p * rot;
|
let rotated = p * rot;
|
||||||
let transformed = rotated * radius + [0.5, SPAWN_POS.y as f64 + 1.0, 0.5];
|
let transformed = rotated * radius + [0.5, SPAWN_POS.y as f64 + 2.0, 0.5];
|
||||||
|
|
||||||
let yaw = f32::atan2(rotated.z as f32, rotated.x as f32).to_degrees() - 90.0;
|
let yaw = f32::atan2(rotated.z as f32, rotated.x as f32).to_degrees() - 90.0;
|
||||||
let (looking_yaw, looking_pitch) =
|
let (looking_yaw, looking_pitch) =
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
@ -8,8 +7,9 @@ use valence::block::{BlockPos, BlockState};
|
||||||
use valence::client::GameMode;
|
use valence::client::GameMode;
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::DimensionId;
|
use valence::dimension::DimensionId;
|
||||||
use valence::entity::{EntityData, EntityKind};
|
use valence::entity::{EntityKind, EntityState};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
|
use valence::spatial_index::RaycastHit;
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::util::from_yaw_and_pitch;
|
use valence::util::from_yaw_and_pitch;
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
|
@ -20,9 +20,12 @@ pub fn main() -> ShutdownResult {
|
||||||
.parse_default_env()
|
.parse_default_env()
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
valence::start_server(Game {
|
valence::start_server(
|
||||||
player_count: AtomicUsize::new(0),
|
Game {
|
||||||
})
|
player_count: AtomicUsize::new(0),
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Game {
|
struct Game {
|
||||||
|
@ -31,12 +34,19 @@ struct Game {
|
||||||
|
|
||||||
const MAX_PLAYERS: usize = 10;
|
const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -8);
|
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -5);
|
||||||
|
|
||||||
const PLAYER_EYE_HEIGHT: f64 = 1.6;
|
const PLAYER_EYE_HEIGHT: f64 = 1.6;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
|
type ChunkData = ();
|
||||||
|
type ClientData = ();
|
||||||
|
/// `true` for entities that have been intersected with.
|
||||||
|
type EntityData = bool;
|
||||||
|
type ServerData = ();
|
||||||
|
type WorldData = ();
|
||||||
|
|
||||||
fn max_connections(&self) -> usize {
|
fn max_connections(&self) -> usize {
|
||||||
// We want status pings to be successful even if the server is full.
|
// We want status pings to be successful even if the server is full.
|
||||||
MAX_PLAYERS + 64
|
MAX_PLAYERS + 64
|
||||||
|
@ -49,7 +59,7 @@ impl Config for Game {
|
||||||
|
|
||||||
async fn server_list_ping(
|
async fn server_list_ping(
|
||||||
&self,
|
&self,
|
||||||
_server: &SharedServer,
|
_server: &SharedServer<Self>,
|
||||||
_remote_addr: SocketAddr,
|
_remote_addr: SocketAddr,
|
||||||
) -> ServerListPing {
|
) -> ServerListPing {
|
||||||
ServerListPing::Respond {
|
ServerListPing::Respond {
|
||||||
|
@ -60,14 +70,14 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, server: &mut Server) {
|
fn init(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.create(DimensionId::default());
|
let (world_id, world) = server.worlds.create(DimensionId::default(), ());
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
|
|
||||||
let size = 5;
|
let size = 5;
|
||||||
for z in -size..size {
|
for z in -size..size {
|
||||||
for x in -size..size {
|
for x in -size..size {
|
||||||
world.chunks.create([x, z]);
|
world.chunks.create([x, z], ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,24 +87,17 @@ impl Config for Game {
|
||||||
for i in 0..SHEEP_COUNT {
|
for i in 0..SHEEP_COUNT {
|
||||||
let offset = (i as f64 - (SHEEP_COUNT - 1) as f64 / 2.0) * 1.25;
|
let offset = (i as f64 - (SHEEP_COUNT - 1) as f64 / 2.0) * 1.25;
|
||||||
|
|
||||||
let (_, sheep) = server.entities.create(EntityKind::Sheep);
|
let (_, sheep) = server.entities.create(EntityKind::Sheep, false);
|
||||||
sheep.set_world(world_id);
|
sheep.set_world(world_id);
|
||||||
sheep.set_position([offset + 0.5, SPAWN_POS.y as f64 + 1.0, 0.0]);
|
sheep.set_position([offset + 0.5, SPAWN_POS.y as f64 + 1.0, 0.0]);
|
||||||
sheep.set_yaw(180.0);
|
sheep.set_yaw(180.0);
|
||||||
sheep.set_head_yaw(180.0);
|
sheep.set_head_yaw(180.0);
|
||||||
|
|
||||||
if let EntityData::Sheep(sheep) = sheep.data_mut() {
|
|
||||||
// Shear the sheep.
|
|
||||||
sheep.set_sheep_state(0b1_0000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &mut Server) {
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
let mut hit_entities = HashSet::new();
|
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_tick() == server.shared.current_tick() {
|
||||||
if self
|
if self
|
||||||
|
@ -133,7 +136,7 @@ impl Config for Game {
|
||||||
"Look at a sheep to change its ".italic()
|
"Look at a sheep to change its ".italic()
|
||||||
+ "color".italic().color(Color::GREEN)
|
+ "color".italic().color(Color::GREEN)
|
||||||
+ ".",
|
+ ".",
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
|
@ -146,27 +149,31 @@ impl Config for Game {
|
||||||
|
|
||||||
let origin = Vec3::new(client_pos.x, client_pos.y + PLAYER_EYE_HEIGHT, client_pos.z);
|
let origin = Vec3::new(client_pos.x, client_pos.y + PLAYER_EYE_HEIGHT, client_pos.z);
|
||||||
let direction = from_yaw_and_pitch(client.yaw() as f64, client.pitch() as f64);
|
let direction = from_yaw_and_pitch(client.yaw() as f64, client.pitch() as f64);
|
||||||
|
let only_sheep = |hit: &RaycastHit| {
|
||||||
if let Some(hit) = world.spatial_index.raycast(origin, direction, |hit| {
|
|
||||||
server
|
server
|
||||||
.entities
|
.entities
|
||||||
.get(hit.entity)
|
.get(hit.entity)
|
||||||
.map_or(false, |e| e.kind() == EntityKind::Sheep)
|
.map_or(false, |e| e.kind() == EntityKind::Sheep)
|
||||||
}) {
|
};
|
||||||
hit_entities.insert(hit.entity);
|
|
||||||
|
if let Some(hit) = world.spatial_index.raycast(origin, direction, only_sheep) {
|
||||||
|
if let Some(e) = server.entities.get_mut(hit.entity) {
|
||||||
|
e.data = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
for (id, e) in server.entities.iter_mut() {
|
for (_, e) in server.entities.iter_mut() {
|
||||||
if let EntityData::Sheep(sheep) = e.data_mut() {
|
if let EntityState::Sheep(sheep) = &mut e.state {
|
||||||
if hit_entities.contains(&id) {
|
if e.data {
|
||||||
sheep.set_sheep_state(5);
|
sheep.set_sheep_state(5);
|
||||||
} else {
|
} else {
|
||||||
sheep.set_sheep_state(0);
|
sheep.set_sheep_state(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.data = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,17 @@ pub fn main() -> ShutdownResult {
|
||||||
|
|
||||||
let seed = rand::random();
|
let seed = rand::random();
|
||||||
|
|
||||||
valence::start_server(Game {
|
valence::start_server(
|
||||||
player_count: AtomicUsize::new(0),
|
Game {
|
||||||
density_noise: SuperSimplex::new().set_seed(seed),
|
player_count: AtomicUsize::new(0),
|
||||||
hilly_noise: SuperSimplex::new().set_seed(seed.wrapping_add(1)),
|
density_noise: SuperSimplex::new().set_seed(seed),
|
||||||
stone_noise: SuperSimplex::new().set_seed(seed.wrapping_add(2)),
|
hilly_noise: SuperSimplex::new().set_seed(seed.wrapping_add(1)),
|
||||||
gravel_noise: SuperSimplex::new().set_seed(seed.wrapping_add(3)),
|
stone_noise: SuperSimplex::new().set_seed(seed.wrapping_add(2)),
|
||||||
grass_noise: SuperSimplex::new().set_seed(seed.wrapping_add(4)),
|
gravel_noise: SuperSimplex::new().set_seed(seed.wrapping_add(3)),
|
||||||
})
|
grass_noise: SuperSimplex::new().set_seed(seed.wrapping_add(4)),
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Game {
|
struct Game {
|
||||||
|
@ -47,6 +50,12 @@ const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
|
type ChunkData = ();
|
||||||
|
type ClientData = ();
|
||||||
|
type EntityData = ();
|
||||||
|
type ServerData = ();
|
||||||
|
type WorldData = ();
|
||||||
|
|
||||||
fn max_connections(&self) -> usize {
|
fn max_connections(&self) -> usize {
|
||||||
// We want status pings to be successful even if the server is full.
|
// We want status pings to be successful even if the server is full.
|
||||||
MAX_PLAYERS + 64
|
MAX_PLAYERS + 64
|
||||||
|
@ -59,7 +68,7 @@ impl Config for Game {
|
||||||
|
|
||||||
async fn server_list_ping(
|
async fn server_list_ping(
|
||||||
&self,
|
&self,
|
||||||
_server: &SharedServer,
|
_server: &SharedServer<Self>,
|
||||||
_remote_addr: SocketAddr,
|
_remote_addr: SocketAddr,
|
||||||
) -> ServerListPing {
|
) -> ServerListPing {
|
||||||
ServerListPing::Respond {
|
ServerListPing::Respond {
|
||||||
|
@ -70,12 +79,12 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, server: &mut Server) {
|
fn init(&self, server: &mut Server<Self>) {
|
||||||
let (_, world) = server.worlds.create(DimensionId::default());
|
let (_, world) = server.worlds.create(DimensionId::default(), ());
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &mut Server) {
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
let mut chunks_to_unload = HashSet::<_>::from_iter(world.chunks.iter().map(|t| t.0));
|
let mut chunks_to_unload = HashSet::<_>::from_iter(world.chunks.iter().map(|t| t.0));
|
||||||
|
@ -126,7 +135,7 @@ impl Config for Game {
|
||||||
for pos in chunks_in_view_distance(ChunkPos::at(p.x, p.z), dist) {
|
for pos in chunks_in_view_distance(ChunkPos::at(p.x, p.z), dist) {
|
||||||
chunks_to_unload.remove(&pos);
|
chunks_to_unload.remove(&pos);
|
||||||
if world.chunks.get(pos).is_none() {
|
if world.chunks.get(pos).is_none() {
|
||||||
world.chunks.create(pos);
|
world.chunks.create(pos, ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
68
src/chunk.rs
68
src/chunk.rs
|
@ -15,6 +15,7 @@ use crate::biome::BiomeId;
|
||||||
use crate::block::BlockState;
|
use crate::block::BlockState;
|
||||||
use crate::block_pos::BlockPos;
|
use crate::block_pos::BlockPos;
|
||||||
pub use crate::chunk_pos::ChunkPos;
|
pub use crate::chunk_pos::ChunkPos;
|
||||||
|
use crate::config::Config;
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::protocol_inner::packets::play::s2c::{
|
use crate::protocol_inner::packets::play::s2c::{
|
||||||
BlockUpdate, LevelChunkHeightmaps, LevelChunkWithLight, S2cPlayPacket, SectionBlocksUpdate,
|
BlockUpdate, LevelChunkHeightmaps, LevelChunkWithLight, S2cPlayPacket, SectionBlocksUpdate,
|
||||||
|
@ -24,14 +25,14 @@ use crate::server::SharedServer;
|
||||||
use crate::Ticks;
|
use crate::Ticks;
|
||||||
|
|
||||||
/// A container for all [`Chunks`]s in a [`World`](crate::world::World).
|
/// A container for all [`Chunks`]s in a [`World`](crate::world::World).
|
||||||
pub struct Chunks {
|
pub struct Chunks<C: Config> {
|
||||||
chunks: HashMap<ChunkPos, Chunk>,
|
chunks: HashMap<ChunkPos, Chunk<C>>,
|
||||||
server: SharedServer,
|
server: SharedServer<C>,
|
||||||
dimension: DimensionId,
|
dimension: DimensionId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunks {
|
impl<C: Config> Chunks<C> {
|
||||||
pub(crate) fn new(server: SharedServer, dimension: DimensionId) -> Self {
|
pub(crate) fn new(server: SharedServer<C>, dimension: DimensionId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
chunks: HashMap::new(),
|
chunks: HashMap::new(),
|
||||||
server,
|
server,
|
||||||
|
@ -49,9 +50,9 @@ impl Chunks {
|
||||||
/// adjacent to it must also be loaded. It is also important that clients
|
/// adjacent to it must also be loaded. It is also important that clients
|
||||||
/// are not spawned within unloaded chunks via
|
/// are not spawned within unloaded chunks via
|
||||||
/// [`spawn`](crate::client::Client::spawn).
|
/// [`spawn`](crate::client::Client::spawn).
|
||||||
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> &mut Chunk {
|
pub fn create(&mut self, pos: impl Into<ChunkPos>, data: C::ChunkData) -> &mut Chunk<C> {
|
||||||
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
||||||
let chunk = Chunk::new(section_count, self.server.current_tick());
|
let chunk = Chunk::new(section_count, self.server.current_tick(), data);
|
||||||
|
|
||||||
match self.chunks.entry(pos.into()) {
|
match self.chunks.entry(pos.into()) {
|
||||||
Entry::Occupied(mut oe) => {
|
Entry::Occupied(mut oe) => {
|
||||||
|
@ -78,14 +79,14 @@ impl Chunks {
|
||||||
/// Gets a shared reference to the chunk at the provided position.
|
/// Gets a shared reference to the chunk at the provided position.
|
||||||
///
|
///
|
||||||
/// If there is no chunk at the position, then `None` is returned.
|
/// If there is no chunk at the position, then `None` is returned.
|
||||||
pub fn get(&self, pos: impl Into<ChunkPos>) -> Option<&Chunk> {
|
pub fn get(&self, pos: impl Into<ChunkPos>) -> Option<&Chunk<C>> {
|
||||||
self.chunks.get(&pos.into())
|
self.chunks.get(&pos.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an exclusive reference to the chunk at the provided position.
|
/// Gets an exclusive reference to the chunk at the provided position.
|
||||||
///
|
///
|
||||||
/// If there is no chunk at the position, then `None` is returned.
|
/// If there is no chunk at the position, then `None` is returned.
|
||||||
pub fn get_mut(&mut self, pos: impl Into<ChunkPos>) -> Option<&mut Chunk> {
|
pub fn get_mut(&mut self, pos: impl Into<ChunkPos>) -> Option<&mut Chunk<C>> {
|
||||||
self.chunks.get_mut(&pos.into())
|
self.chunks.get_mut(&pos.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,25 +97,25 @@ impl Chunks {
|
||||||
|
|
||||||
/// Returns an immutable iterator over all chunks in the world in an
|
/// Returns an immutable iterator over all chunks in the world in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (ChunkPos, &Chunk)> + Clone + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (ChunkPos, &Chunk<C>)> + Clone + '_ {
|
||||||
self.chunks.iter().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.iter().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable iterator over all chunks in the world in an
|
/// Returns a mutable iterator over all chunks in the world in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ChunkPos, &mut Chunk)> + '_ {
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ChunkPos, &mut Chunk<C>)> + '_ {
|
||||||
self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel immutable iterator over all chunks in the world in an
|
/// Returns a parallel immutable iterator over all chunks in the world in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk<C>)> + Clone + '_ {
|
||||||
self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel mutable iterator over all chunks in the world in an
|
/// Returns a parallel mutable iterator over all chunks in the world in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ChunkPos, &mut Chunk)> + '_ {
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ChunkPos, &mut Chunk<C>)> + '_ {
|
||||||
self.chunks.par_iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.par_iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +184,9 @@ impl Chunks {
|
||||||
///
|
///
|
||||||
/// In addition to blocks, chunks also contain [biomes](crate::biome::Biome).
|
/// In addition to blocks, chunks also contain [biomes](crate::biome::Biome).
|
||||||
/// Every 4x4x4 segment of blocks in a chunk corresponds to a biome.
|
/// Every 4x4x4 segment of blocks in a chunk corresponds to a biome.
|
||||||
pub struct Chunk {
|
pub struct Chunk<C: Config> {
|
||||||
|
/// Custom data.
|
||||||
|
pub data: C::ChunkData,
|
||||||
sections: Box<[ChunkSection]>,
|
sections: Box<[ChunkSection]>,
|
||||||
// TODO block_entities: HashMap<u32, BlockEntity>,
|
// TODO block_entities: HashMap<u32, BlockEntity>,
|
||||||
/// The MOTION_BLOCKING heightmap
|
/// The MOTION_BLOCKING heightmap
|
||||||
|
@ -191,8 +194,8 @@ pub struct Chunk {
|
||||||
created_tick: Ticks,
|
created_tick: Ticks,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chunk {
|
impl<C: Config> Chunk<C> {
|
||||||
pub(crate) fn new(section_count: u32, current_tick: Ticks) -> Self {
|
pub(crate) fn new(section_count: u32, current_tick: Ticks, data: C::ChunkData) -> Self {
|
||||||
let sect = ChunkSection {
|
let sect = ChunkSection {
|
||||||
blocks: [BlockState::AIR.to_raw(); 4096],
|
blocks: [BlockState::AIR.to_raw(); 4096],
|
||||||
modified_count: 1, // Must be >0 so the chunk is initialized.
|
modified_count: 1, // Must be >0 so the chunk is initialized.
|
||||||
|
@ -201,6 +204,7 @@ impl Chunk {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut chunk = Self {
|
let mut chunk = Self {
|
||||||
|
data,
|
||||||
sections: vec![sect; section_count as usize].into(),
|
sections: vec![sect; section_count as usize].into(),
|
||||||
heightmap: Vec::new(),
|
heightmap: Vec::new(),
|
||||||
created_tick: current_tick,
|
created_tick: current_tick,
|
||||||
|
@ -547,35 +551,3 @@ fn encode_paletted_container(
|
||||||
fn log2_ceil(n: usize) -> usize {
|
fn log2_ceil(n: usize) -> usize {
|
||||||
n.next_power_of_two().trailing_zeros() as usize
|
n.next_power_of_two().trailing_zeros() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_get() {
|
|
||||||
let mut chunk = Chunk::new(16, 0);
|
|
||||||
|
|
||||||
chunk.set_block_state(1, 2, 3, BlockState::CAKE);
|
|
||||||
assert_eq!(chunk.get_block_state(1, 2, 3), BlockState::CAKE);
|
|
||||||
|
|
||||||
chunk.set_biome(1, 2, 3, BiomeId(7));
|
|
||||||
assert_eq!(chunk.get_biome(1, 2, 3), BiomeId(7));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn block_state_oob() {
|
|
||||||
let mut chunk = Chunk::new(16, 0);
|
|
||||||
|
|
||||||
chunk.set_block_state(16, 0, 0, BlockState::CAKE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn biome_oob() {
|
|
||||||
let mut chunk = Chunk::new(16, 0);
|
|
||||||
|
|
||||||
chunk.set_biome(4, 0, 0, BiomeId(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ use vek::Vec3;
|
||||||
use crate::biome::Biome;
|
use crate::biome::Biome;
|
||||||
use crate::block_pos::BlockPos;
|
use crate::block_pos::BlockPos;
|
||||||
use crate::chunk_pos::ChunkPos;
|
use crate::chunk_pos::ChunkPos;
|
||||||
|
use crate::config::Config;
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::entity::types::Player;
|
use crate::entity::types::Player;
|
||||||
use crate::entity::{velocity_to_packet_units, Entities, Entity, EntityId, EntityKind};
|
use crate::entity::{velocity_to_packet_units, Entities, Entity, EntityId, EntityKind};
|
||||||
|
@ -47,16 +48,16 @@ use crate::{ident, Ticks, LIBRARY_NAMESPACE};
|
||||||
/// New clients are automatically inserted into this container but
|
/// New clients are automatically inserted into this container but
|
||||||
/// are not automatically deleted. It is your responsibility to delete them once
|
/// are not automatically deleted. It is your responsibility to delete them once
|
||||||
/// they disconnect. This can be checked with [`Client::is_disconnected`].
|
/// they disconnect. This can be checked with [`Client::is_disconnected`].
|
||||||
pub struct Clients {
|
pub struct Clients<C: Config> {
|
||||||
sm: SlotMap<Client>,
|
sm: SlotMap<Client<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clients {
|
impl<C: Config> Clients<C> {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { sm: SlotMap::new() }
|
Self { sm: SlotMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, client: Client) -> (ClientId, &mut Client) {
|
pub(crate) fn insert(&mut self, client: Client<C>) -> (ClientId, &mut Client<C>) {
|
||||||
let (id, client) = self.sm.insert(client);
|
let (id, client) = self.sm.insert(client);
|
||||||
(ClientId(id), client)
|
(ClientId(id), client)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ impl Clients {
|
||||||
/// which `f` returns `true`.
|
/// which `f` returns `true`.
|
||||||
///
|
///
|
||||||
/// All clients are visited in an unspecified order.
|
/// All clients are visited in an unspecified order.
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client) -> bool) {
|
pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client<C>) -> bool) {
|
||||||
self.sm.retain(|k, v| f(ClientId(k), v))
|
self.sm.retain(|k, v| f(ClientId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,37 +86,39 @@ impl Clients {
|
||||||
|
|
||||||
/// Returns a shared reference to the client with the given ID. If
|
/// Returns a shared reference to the client with the given ID. If
|
||||||
/// the ID is invalid, then `None` is returned.
|
/// the ID is invalid, then `None` is returned.
|
||||||
pub fn get(&self, client: ClientId) -> Option<&Client> {
|
pub fn get(&self, client: ClientId) -> Option<&Client<C>> {
|
||||||
self.sm.get(client.0)
|
self.sm.get(client.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an exclusive reference to the client with the given ID. If the
|
/// Returns an exclusive reference to the client with the given ID. If the
|
||||||
/// ID is invalid, then `None` is returned.
|
/// ID is invalid, then `None` is returned.
|
||||||
pub fn get_mut(&mut self, client: ClientId) -> Option<&mut Client> {
|
pub fn get_mut(&mut self, client: ClientId) -> Option<&mut Client<C>> {
|
||||||
self.sm.get_mut(client.0)
|
self.sm.get_mut(client.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an immutable iterator over all clients on the server in an
|
/// Returns an immutable iterator over all clients on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (ClientId, &Client<C>)> + Clone + '_ {
|
||||||
self.sm.iter().map(|(k, v)| (ClientId(k), v))
|
self.sm.iter().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable iterator over all clients on the server in an
|
/// Returns a mutable iterator over all clients on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ClientId, &mut Client)> + '_ {
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ClientId, &mut Client<C>)> + '_ {
|
||||||
self.sm.iter_mut().map(|(k, v)| (ClientId(k), v))
|
self.sm.iter_mut().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel immutable iterator over all clients on the server in
|
/// Returns a parallel immutable iterator over all clients on the server in
|
||||||
/// an unspecified order.
|
/// an unspecified order.
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client<C>)> + Clone + '_ {
|
||||||
self.sm.par_iter().map(|(k, v)| (ClientId(k), v))
|
self.sm.par_iter().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel mutable iterator over all clients on the server in an
|
/// Returns a parallel mutable iterator over all clients on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ClientId, &mut Client)> + '_ {
|
pub fn par_iter_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> impl ParallelIterator<Item = (ClientId, &mut Client<C>)> + '_ {
|
||||||
self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v))
|
self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +161,9 @@ impl ClientId {
|
||||||
///
|
///
|
||||||
/// In Valence however, clients and players have been decoupled. This separation
|
/// In Valence however, clients and players have been decoupled. This separation
|
||||||
/// was done primarily to enable multithreaded client updates.
|
/// was done primarily to enable multithreaded client updates.
|
||||||
pub struct Client {
|
pub struct Client<C: Config> {
|
||||||
|
/// Custom data.
|
||||||
|
pub data: C::ClientData,
|
||||||
/// Setting this to `None` disconnects the client.
|
/// Setting this to `None` disconnects the client.
|
||||||
send: SendOpt,
|
send: SendOpt,
|
||||||
recv: Receiver<C2sPlayPacket>,
|
recv: Receiver<C2sPlayPacket>,
|
||||||
|
@ -227,15 +232,17 @@ pub(crate) struct ClientFlags {
|
||||||
_pad: u8,
|
_pad: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl<C: Config> Client<C> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
packet_channels: C2sPacketChannels,
|
packet_channels: C2sPacketChannels,
|
||||||
server: &SharedServer,
|
server: &SharedServer<C>,
|
||||||
ncd: NewClientData,
|
ncd: NewClientData,
|
||||||
|
data: C::ClientData,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (send, recv) = packet_channels;
|
let (send, recv) = packet_channels;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
data,
|
||||||
send: Some(send),
|
send: Some(send),
|
||||||
recv,
|
recv,
|
||||||
created_tick: server.current_tick(),
|
created_tick: server.current_tick(),
|
||||||
|
@ -584,16 +591,16 @@ impl Client {
|
||||||
send_packet(&mut self.send, packet);
|
send_packet(&mut self.send, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) {
|
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities<C>) {
|
||||||
self.events.clear();
|
self.events.clear();
|
||||||
for _ in 0..self.recv.len() {
|
for _ in 0..self.recv.len() {
|
||||||
self.handle_serverbound_packet(entities, self.recv.try_recv().unwrap());
|
self.handle_serverbound_packet(entities, self.recv.try_recv().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_serverbound_packet(&mut self, entities: &Entities, pkt: C2sPlayPacket) {
|
fn handle_serverbound_packet(&mut self, entities: &Entities<C>, pkt: C2sPlayPacket) {
|
||||||
fn handle_movement_packet(
|
fn handle_movement_packet<C: Config>(
|
||||||
client: &mut Client,
|
client: &mut Client<C>,
|
||||||
_vehicle: bool,
|
_vehicle: bool,
|
||||||
new_position: Vec3<f64>,
|
new_position: Vec3<f64>,
|
||||||
new_yaw: f32,
|
new_yaw: f32,
|
||||||
|
@ -835,7 +842,12 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&mut self, shared: &SharedServer, entities: &Entities, worlds: &Worlds) {
|
pub(crate) fn update(
|
||||||
|
&mut self,
|
||||||
|
shared: &SharedServer<C>,
|
||||||
|
entities: &Entities<C>,
|
||||||
|
worlds: &Worlds<C>,
|
||||||
|
) {
|
||||||
// Mark the client as disconnected when appropriate.
|
// Mark the client as disconnected when appropriate.
|
||||||
if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) {
|
if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) {
|
||||||
self.send = None;
|
self.send = None;
|
||||||
|
@ -1298,8 +1310,8 @@ fn send_packet(send_opt: &mut SendOpt, pkt: impl Into<S2cPlayPacket>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_entity_events(send_opt: &mut SendOpt, id: EntityId, entity: &Entity) {
|
fn send_entity_events<C: Config>(send_opt: &mut SendOpt, id: EntityId, entity: &Entity<C>) {
|
||||||
for &code in entity.data().event_codes() {
|
for &code in entity.state.event_codes() {
|
||||||
if code <= ENTITY_EVENT_MAX_BOUND as u8 {
|
if code <= ENTITY_EVENT_MAX_BOUND as u8 {
|
||||||
send_packet(
|
send_packet(
|
||||||
send_opt,
|
send_opt,
|
||||||
|
@ -1320,7 +1332,7 @@ fn send_entity_events(send_opt: &mut SendOpt, id: EntityId, entity: &Entity) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_registry_codec(shared: &SharedServer) -> RegistryCodec {
|
fn make_registry_codec<C: Config>(shared: &SharedServer<C>) -> RegistryCodec {
|
||||||
let mut dims = Vec::new();
|
let mut dims = Vec::new();
|
||||||
for (id, dim) in shared.dimensions() {
|
for (id, dim) in shared.dimensions() {
|
||||||
let id = id.0 as i32;
|
let id = id.0 as i32;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Configuration for the server.
|
//! Configuration for the server.
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
|
||||||
|
@ -13,14 +12,7 @@ use crate::server::{NewClientData, Server, SharedServer};
|
||||||
use crate::text::Text;
|
use crate::text::Text;
|
||||||
use crate::Ticks;
|
use crate::Ticks;
|
||||||
|
|
||||||
/// A trait containing callbacks which are invoked by the running Minecraft
|
/// A trait for the configuration of a server.
|
||||||
/// server.
|
|
||||||
///
|
|
||||||
/// The config is used from multiple threads and must therefore implement
|
|
||||||
/// [`Send`] and [`Sync`]. From within a single thread, methods are never
|
|
||||||
/// invoked recursively by the library. In other words, a mutex can be aquired
|
|
||||||
/// at the beginning of a method and released at the end without risk of
|
|
||||||
/// deadlocking.
|
|
||||||
///
|
///
|
||||||
/// This trait uses the [async_trait] attribute macro. It is exported at the
|
/// This trait uses the [async_trait] attribute macro. It is exported at the
|
||||||
/// root of this crate.
|
/// root of this crate.
|
||||||
|
@ -28,10 +20,21 @@ use crate::Ticks;
|
||||||
/// [async_trait]: https://docs.rs/async-trait/latest/async_trait/
|
/// [async_trait]: https://docs.rs/async-trait/latest/async_trait/
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
pub trait Config: 'static + Sized + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// Called once at startup to get the maximum number of connections allowed
|
/// Custom data to store with the [`Server`].
|
||||||
/// to the server. Note that this includes all connections, not just those
|
type ServerData: Send + Sync;
|
||||||
/// past the login stage.
|
/// Custom data to store with every [`Client`](crate::client::Client).
|
||||||
|
type ClientData: Default + Send + Sync;
|
||||||
|
/// Custom data to store with every [`Entity`](crate::entity::Entity).
|
||||||
|
type EntityData: Send + Sync;
|
||||||
|
/// Custom data to store with every [`World`](crate::world::World).
|
||||||
|
type WorldData: Send + Sync;
|
||||||
|
/// Custom data to store with every [`Chunk`](crate::chunk::Chunk).
|
||||||
|
type ChunkData: Send + Sync;
|
||||||
|
|
||||||
|
/// Called once at startup to get the maximum number of simultaneous
|
||||||
|
/// connections allowed to the server. This includes all
|
||||||
|
/// connections, not just those past the login stage.
|
||||||
///
|
///
|
||||||
/// You will want this value to be somewhere above the maximum number of
|
/// You will want this value to be somewhere above the maximum number of
|
||||||
/// players, since status pings should still succeed even when the server is
|
/// players, since status pings should still succeed even when the server is
|
||||||
|
@ -169,7 +172,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// The query is ignored.
|
/// The query is ignored.
|
||||||
async fn server_list_ping(
|
async fn server_list_ping(
|
||||||
&self,
|
&self,
|
||||||
shared: &SharedServer,
|
shared: &SharedServer<Self>,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
) -> ServerListPing {
|
) -> ServerListPing {
|
||||||
ServerListPing::Ignore
|
ServerListPing::Ignore
|
||||||
|
@ -192,7 +195,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// The client is allowed to join unconditionally.
|
/// The client is allowed to join unconditionally.
|
||||||
///
|
///
|
||||||
/// [`Clients`]: crate::client::Clients
|
/// [`Clients`]: crate::client::Clients
|
||||||
async fn login(&self, shared: &SharedServer, ncd: &NewClientData) -> Result<(), Text> {
|
async fn login(&self, shared: &SharedServer<Self>, ncd: &NewClientData) -> Result<(), Text> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +206,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// no connections to the server will be made until this function returns.
|
/// no connections to the server will be made until this function returns.
|
||||||
///
|
///
|
||||||
/// This method is called from within a tokio runtime.
|
/// This method is called from within a tokio runtime.
|
||||||
fn init(&self, server: &mut Server) {}
|
fn init(&self, server: &mut Server<Self>) {}
|
||||||
|
|
||||||
/// Called once at the beginning of every server update (also known as
|
/// Called once at the beginning of every server update (also known as
|
||||||
/// "tick"). This is likely where the majority of your code will be.
|
/// "tick"). This is likely where the majority of your code will be.
|
||||||
|
@ -215,7 +218,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// # Default Implementation
|
/// # Default Implementation
|
||||||
///
|
///
|
||||||
/// The default implementation does nothing.
|
/// The default implementation does nothing.
|
||||||
fn update(&self, server: &mut Server);
|
fn update(&self, server: &mut Server<Self>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of the [`server_list_ping`](Config::server_list_ping) callback.
|
/// The result of the [`server_list_ping`](Config::server_list_ping) callback.
|
||||||
|
|
311
src/entity.rs
311
src/entity.rs
|
@ -1,6 +1,6 @@
|
||||||
//! Dynamic actors in a world.
|
//! Dynamic actors in a world.
|
||||||
|
|
||||||
pub mod data;
|
pub mod state;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
@ -10,10 +10,11 @@ use std::num::NonZeroU32;
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
pub use types::{EntityData, EntityKind};
|
pub use types::{EntityKind, EntityState};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
use crate::protocol_inner::packets::play::s2c::{
|
use crate::protocol_inner::packets::play::s2c::{
|
||||||
AddEntity, AddExperienceOrb, AddPlayer, S2cPlayPacket, SetEntityMetadata,
|
AddEntity, AddExperienceOrb, AddPlayer, S2cPlayPacket, SetEntityMetadata,
|
||||||
};
|
};
|
||||||
|
@ -32,13 +33,13 @@ use crate::world::WorldId;
|
||||||
///
|
///
|
||||||
/// [`Player`]: crate::entity::types::Player
|
/// [`Player`]: crate::entity::types::Player
|
||||||
/// [`PlayerList`]: crate::player_list::PlayerList
|
/// [`PlayerList`]: crate::player_list::PlayerList
|
||||||
pub struct Entities {
|
pub struct Entities<C: Config> {
|
||||||
sm: SlotMap<Entity>,
|
sm: SlotMap<Entity<C>>,
|
||||||
uuid_to_entity: HashMap<Uuid, EntityId>,
|
uuid_to_entity: HashMap<Uuid, EntityId>,
|
||||||
network_id_to_entity: HashMap<NonZeroU32, u32>,
|
network_id_to_entity: HashMap<NonZeroU32, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entities {
|
impl<C: Config> Entities<C> {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
sm: SlotMap::new(),
|
sm: SlotMap::new(),
|
||||||
|
@ -49,8 +50,8 @@ impl Entities {
|
||||||
|
|
||||||
/// Spawns a new entity with a random UUID. A reference to the entity along
|
/// Spawns a new entity with a random UUID. A reference to the entity along
|
||||||
/// with its ID is returned.
|
/// with its ID is returned.
|
||||||
pub fn create(&mut self, kind: EntityKind) -> (EntityId, &mut Entity) {
|
pub fn create(&mut self, kind: EntityKind, data: C::EntityData) -> (EntityId, &mut Entity<C>) {
|
||||||
self.create_with_uuid(kind, Uuid::from_bytes(rand::random()))
|
self.create_with_uuid(kind, Uuid::from_bytes(rand::random()), data)
|
||||||
.expect("UUID collision")
|
.expect("UUID collision")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +64,15 @@ impl Entities {
|
||||||
&mut self,
|
&mut self,
|
||||||
kind: EntityKind,
|
kind: EntityKind,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
) -> Option<(EntityId, &mut Entity)> {
|
data: C::EntityData,
|
||||||
|
) -> Option<(EntityId, &mut Entity<C>)> {
|
||||||
match self.uuid_to_entity.entry(uuid) {
|
match self.uuid_to_entity.entry(uuid) {
|
||||||
Entry::Occupied(_) => None,
|
Entry::Occupied(_) => None,
|
||||||
Entry::Vacant(ve) => {
|
Entry::Vacant(ve) => {
|
||||||
let (k, e) = self.sm.insert(Entity {
|
let (k, e) = self.sm.insert(Entity {
|
||||||
|
data,
|
||||||
|
state: EntityState::new(kind),
|
||||||
flags: EntityFlags(0),
|
flags: EntityFlags(0),
|
||||||
data: EntityData::new(kind),
|
|
||||||
world: WorldId::NULL,
|
world: WorldId::NULL,
|
||||||
new_position: Vec3::default(),
|
new_position: Vec3::default(),
|
||||||
old_position: Vec3::default(),
|
old_position: Vec3::default(),
|
||||||
|
@ -113,7 +116,7 @@ impl Entities {
|
||||||
/// Removes all entities from the server for which `f` returns `true`.
|
/// Removes all entities from the server for which `f` returns `true`.
|
||||||
///
|
///
|
||||||
/// All entities are visited in an unspecified order.
|
/// All entities are visited in an unspecified order.
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(EntityId, &mut Entity) -> bool) {
|
pub fn retain(&mut self, mut f: impl FnMut(EntityId, &mut Entity<C>) -> bool) {
|
||||||
self.sm.retain(|k, v| {
|
self.sm.retain(|k, v| {
|
||||||
if f(EntityId(k), v) {
|
if f(EntityId(k), v) {
|
||||||
true
|
true
|
||||||
|
@ -147,14 +150,14 @@ impl Entities {
|
||||||
/// Gets a shared reference to the entity with the given [`EntityId`].
|
/// Gets a shared reference to the entity with the given [`EntityId`].
|
||||||
///
|
///
|
||||||
/// If the ID is invalid, `None` is returned.
|
/// If the ID is invalid, `None` is returned.
|
||||||
pub fn get(&self, entity: EntityId) -> Option<&Entity> {
|
pub fn get(&self, entity: EntityId) -> Option<&Entity<C>> {
|
||||||
self.sm.get(entity.0)
|
self.sm.get(entity.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an exclusive reference to the entity with the given [`EntityId`].
|
/// Gets an exclusive reference to the entity with the given [`EntityId`].
|
||||||
///
|
///
|
||||||
/// If the ID is invalid, `None` is returned.
|
/// If the ID is invalid, `None` is returned.
|
||||||
pub fn get_mut(&mut self, entity: EntityId) -> Option<&mut Entity> {
|
pub fn get_mut(&mut self, entity: EntityId) -> Option<&mut Entity<C>> {
|
||||||
self.sm.get_mut(entity.0)
|
self.sm.get_mut(entity.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,32 +169,34 @@ impl Entities {
|
||||||
|
|
||||||
/// Returns an immutable iterator over all entities on the server in an
|
/// Returns an immutable iterator over all entities on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (EntityId, &Entity<C>)> + Clone + '_ {
|
||||||
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable iterator over all entities on the server in an
|
/// Returns a mutable iterator over all entities on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (EntityId, &mut Entity)> + '_ {
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (EntityId, &mut Entity<C>)> + '_ {
|
||||||
self.sm.iter_mut().map(|(k, v)| (EntityId(k), v))
|
self.sm.iter_mut().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel immutable iterator over all entities on the server in
|
/// Returns a parallel immutable iterator over all entities on the server in
|
||||||
/// an unspecified order.
|
/// an unspecified order.
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity<C>)> + Clone + '_ {
|
||||||
self.sm.par_iter().map(|(k, v)| (EntityId(k), v))
|
self.sm.par_iter().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel mutable iterator over all clients on the server in an
|
/// Returns a parallel mutable iterator over all clients on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (EntityId, &mut Entity)> + '_ {
|
pub fn par_iter_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> impl ParallelIterator<Item = (EntityId, &mut Entity<C>)> + '_ {
|
||||||
self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v))
|
self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&mut self) {
|
pub(crate) fn update(&mut self) {
|
||||||
for (_, e) in self.iter_mut() {
|
for (_, e) in self.iter_mut() {
|
||||||
e.old_position = e.new_position;
|
e.old_position = e.new_position;
|
||||||
e.data.clear_modifications();
|
e.state.clear_modifications();
|
||||||
|
|
||||||
e.flags.set_yaw_or_pitch_modified(false);
|
e.flags.set_yaw_or_pitch_modified(false);
|
||||||
e.flags.set_head_yaw_modified(false);
|
e.flags.set_head_yaw_modified(false);
|
||||||
|
@ -230,9 +235,12 @@ impl EntityId {
|
||||||
/// this struct. This includes position, rotation, velocity, UUID, and hitbox.
|
/// this struct. This includes position, rotation, velocity, UUID, and hitbox.
|
||||||
/// To access data that is not common to every kind of entity, see
|
/// To access data that is not common to every kind of entity, see
|
||||||
/// [`Self::data`].
|
/// [`Self::data`].
|
||||||
pub struct Entity {
|
pub struct Entity<C: Config> {
|
||||||
|
/// Custom data.
|
||||||
|
pub data: C::EntityData,
|
||||||
|
/// Kind-specific state for this entity.
|
||||||
|
pub state: EntityState,
|
||||||
flags: EntityFlags,
|
flags: EntityFlags,
|
||||||
data: EntityData,
|
|
||||||
world: WorldId,
|
world: WorldId,
|
||||||
new_position: Vec3<f64>,
|
new_position: Vec3<f64>,
|
||||||
old_position: Vec3<f64>,
|
old_position: Vec3<f64>,
|
||||||
|
@ -253,25 +261,14 @@ pub(crate) struct EntityFlags {
|
||||||
_pad: u8,
|
_pad: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity {
|
impl<C: Config> Entity<C> {
|
||||||
pub(crate) fn flags(&self) -> EntityFlags {
|
pub(crate) fn flags(&self) -> EntityFlags {
|
||||||
self.flags
|
self.flags
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to this entity's [`EntityData`].
|
|
||||||
pub fn data(&self) -> &EntityData {
|
|
||||||
&self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a mutable reference to this entity's
|
|
||||||
/// [`EntityData`].
|
|
||||||
pub fn data_mut(&mut self) -> &mut EntityData {
|
|
||||||
&mut self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the [`EntityKind`] of this entity.
|
/// Gets the [`EntityKind`] of this entity.
|
||||||
pub fn kind(&self) -> EntityKind {
|
pub fn kind(&self) -> EntityKind {
|
||||||
self.data.kind()
|
self.state.kind()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the [`WorldId`](crate::world::WorldId) of the world this entity is
|
/// Gets the [`WorldId`](crate::world::WorldId) of the world this entity is
|
||||||
|
@ -389,18 +386,18 @@ impl Entity {
|
||||||
///
|
///
|
||||||
/// [interact event]: crate::client::Event::InteractWithEntity
|
/// [interact event]: crate::client::Event::InteractWithEntity
|
||||||
pub fn hitbox(&self) -> Aabb<f64> {
|
pub fn hitbox(&self) -> Aabb<f64> {
|
||||||
let dims = match &self.data {
|
let dims = match &self.state {
|
||||||
EntityData::Allay(_) => [0.6, 0.35, 0.6],
|
EntityState::Allay(_) => [0.6, 0.35, 0.6],
|
||||||
EntityData::ChestBoat(_) => [1.375, 0.5625, 1.375],
|
EntityState::ChestBoat(_) => [1.375, 0.5625, 1.375],
|
||||||
EntityData::Frog(_) => [0.5, 0.5, 0.5],
|
EntityState::Frog(_) => [0.5, 0.5, 0.5],
|
||||||
EntityData::Tadpole(_) => [0.4, 0.3, 0.4],
|
EntityState::Tadpole(_) => [0.4, 0.3, 0.4],
|
||||||
EntityData::Warden(_) => [0.9, 2.9, 0.9],
|
EntityState::Warden(_) => [0.9, 2.9, 0.9],
|
||||||
EntityData::AreaEffectCloud(e) => [
|
EntityState::AreaEffectCloud(e) => [
|
||||||
e.get_radius() as f64 * 2.0,
|
e.get_radius() as f64 * 2.0,
|
||||||
0.5,
|
0.5,
|
||||||
e.get_radius() as f64 * 2.0,
|
e.get_radius() as f64 * 2.0,
|
||||||
],
|
],
|
||||||
EntityData::ArmorStand(e) => {
|
EntityState::ArmorStand(e) => {
|
||||||
if e.get_marker() {
|
if e.get_marker() {
|
||||||
[0.0, 0.0, 0.0]
|
[0.0, 0.0, 0.0]
|
||||||
} else if e.get_small() {
|
} else if e.get_small() {
|
||||||
|
@ -409,123 +406,123 @@ impl Entity {
|
||||||
[0.5, 1.975, 0.5]
|
[0.5, 1.975, 0.5]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EntityData::Arrow(_) => [0.5, 0.5, 0.5],
|
EntityState::Arrow(_) => [0.5, 0.5, 0.5],
|
||||||
EntityData::Axolotl(_) => [1.3, 0.6, 1.3],
|
EntityState::Axolotl(_) => [1.3, 0.6, 1.3],
|
||||||
EntityData::Bat(_) => [0.5, 0.9, 0.5],
|
EntityState::Bat(_) => [0.5, 0.9, 0.5],
|
||||||
EntityData::Bee(_) => [0.7, 0.6, 0.7], // TODO: baby size?
|
EntityState::Bee(_) => [0.7, 0.6, 0.7], // TODO: baby size?
|
||||||
EntityData::Blaze(_) => [0.6, 1.8, 0.6],
|
EntityState::Blaze(_) => [0.6, 1.8, 0.6],
|
||||||
EntityData::Boat(_) => [1.375, 0.5625, 1.375],
|
EntityState::Boat(_) => [1.375, 0.5625, 1.375],
|
||||||
EntityData::Cat(_) => [0.6, 0.7, 0.6],
|
EntityState::Cat(_) => [0.6, 0.7, 0.6],
|
||||||
EntityData::CaveSpider(_) => [0.7, 0.5, 0.7],
|
EntityState::CaveSpider(_) => [0.7, 0.5, 0.7],
|
||||||
EntityData::Chicken(_) => [0.4, 0.7, 0.4], // TODO: baby size?
|
EntityState::Chicken(_) => [0.4, 0.7, 0.4], // TODO: baby size?
|
||||||
EntityData::Cod(_) => [0.5, 0.3, 0.5],
|
EntityState::Cod(_) => [0.5, 0.3, 0.5],
|
||||||
EntityData::Cow(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
EntityState::Cow(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
||||||
EntityData::Creeper(_) => [0.6, 1.7, 0.6],
|
EntityState::Creeper(_) => [0.6, 1.7, 0.6],
|
||||||
EntityData::Dolphin(_) => [0.9, 0.6, 0.9],
|
EntityState::Dolphin(_) => [0.9, 0.6, 0.9],
|
||||||
EntityData::Donkey(_) => [1.5, 1.39648, 1.5], // TODO: baby size?
|
EntityState::Donkey(_) => [1.5, 1.39648, 1.5], // TODO: baby size?
|
||||||
EntityData::DragonFireball(_) => [1.0, 1.0, 1.0],
|
EntityState::DragonFireball(_) => [1.0, 1.0, 1.0],
|
||||||
EntityData::Drowned(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::Drowned(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::ElderGuardian(_) => [1.9975, 1.9975, 1.9975],
|
EntityState::ElderGuardian(_) => [1.9975, 1.9975, 1.9975],
|
||||||
EntityData::EndCrystal(_) => [2.0, 2.0, 2.0],
|
EntityState::EndCrystal(_) => [2.0, 2.0, 2.0],
|
||||||
EntityData::EnderDragon(_) => [16.0, 8.0, 16.0],
|
EntityState::EnderDragon(_) => [16.0, 8.0, 16.0],
|
||||||
EntityData::Enderman(_) => [0.6, 2.9, 0.6],
|
EntityState::Enderman(_) => [0.6, 2.9, 0.6],
|
||||||
EntityData::Endermite(_) => [0.4, 0.3, 0.4],
|
EntityState::Endermite(_) => [0.4, 0.3, 0.4],
|
||||||
EntityData::Evoker(_) => [0.6, 1.95, 0.6],
|
EntityState::Evoker(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::EvokerFangs(_) => [0.5, 0.8, 0.5],
|
EntityState::EvokerFangs(_) => [0.5, 0.8, 0.5],
|
||||||
EntityData::ExperienceOrb(_) => [0.5, 0.5, 0.5],
|
EntityState::ExperienceOrb(_) => [0.5, 0.5, 0.5],
|
||||||
EntityData::EyeOfEnder(_) => [0.25, 0.25, 0.25],
|
EntityState::EyeOfEnder(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::FallingBlock(_) => [0.98, 0.98, 0.98],
|
EntityState::FallingBlock(_) => [0.98, 0.98, 0.98],
|
||||||
EntityData::FireworkRocket(_) => [0.25, 0.25, 0.25],
|
EntityState::FireworkRocket(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::Fox(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
EntityState::Fox(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
||||||
EntityData::Ghast(_) => [4.0, 4.0, 4.0],
|
EntityState::Ghast(_) => [4.0, 4.0, 4.0],
|
||||||
EntityData::Giant(_) => [3.6, 12.0, 3.6],
|
EntityState::Giant(_) => [3.6, 12.0, 3.6],
|
||||||
EntityData::GlowItemFrame(_) => todo!("account for rotation"),
|
EntityState::GlowItemFrame(_) => todo!("account for rotation"),
|
||||||
EntityData::GlowSquid(_) => [0.8, 0.8, 0.8],
|
EntityState::GlowSquid(_) => [0.8, 0.8, 0.8],
|
||||||
EntityData::Goat(_) => [1.3, 0.9, 1.3], // TODO: baby size?
|
EntityState::Goat(_) => [1.3, 0.9, 1.3], // TODO: baby size?
|
||||||
EntityData::Guardian(_) => [0.85, 0.85, 0.85],
|
EntityState::Guardian(_) => [0.85, 0.85, 0.85],
|
||||||
EntityData::Hoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
EntityState::Hoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
||||||
EntityData::Horse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
EntityState::Horse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
||||||
EntityData::Husk(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::Husk(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::Illusioner(_) => [0.6, 1.95, 0.6],
|
EntityState::Illusioner(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::IronGolem(_) => [1.4, 2.7, 1.4],
|
EntityState::IronGolem(_) => [1.4, 2.7, 1.4],
|
||||||
EntityData::Item(_) => [0.25, 0.25, 0.25],
|
EntityState::Item(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::ItemFrame(_) => todo!("account for rotation"),
|
EntityState::ItemFrame(_) => todo!("account for rotation"),
|
||||||
EntityData::Fireball(_) => [1.0, 1.0, 1.0],
|
EntityState::Fireball(_) => [1.0, 1.0, 1.0],
|
||||||
EntityData::LeashKnot(_) => [0.375, 0.5, 0.375],
|
EntityState::LeashKnot(_) => [0.375, 0.5, 0.375],
|
||||||
EntityData::LightningBolt(_) => [0.0, 0.0, 0.0],
|
EntityState::LightningBolt(_) => [0.0, 0.0, 0.0],
|
||||||
EntityData::Llama(_) => [0.9, 1.87, 0.9], // TODO: baby size?
|
EntityState::Llama(_) => [0.9, 1.87, 0.9], // TODO: baby size?
|
||||||
EntityData::LlamaSpit(_) => [0.25, 0.25, 0.25],
|
EntityState::LlamaSpit(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::MagmaCube(e) => {
|
EntityState::MagmaCube(e) => {
|
||||||
let s = e.get_size() as f64 * 0.51000005;
|
let s = e.get_size() as f64 * 0.51000005;
|
||||||
[s, s, s]
|
[s, s, s]
|
||||||
}
|
}
|
||||||
EntityData::Marker(_) => [0.0, 0.0, 0.0],
|
EntityState::Marker(_) => [0.0, 0.0, 0.0],
|
||||||
EntityData::Minecart(_) => [0.98, 0.7, 0.98],
|
EntityState::Minecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::ChestMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::ChestMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::CommandBlockMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::CommandBlockMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::FurnaceMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::FurnaceMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::HopperMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::HopperMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::SpawnerMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::SpawnerMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::TntMinecart(_) => [0.98, 0.7, 0.98],
|
EntityState::TntMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
EntityData::Mule(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
EntityState::Mule(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
||||||
EntityData::Mooshroom(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
EntityState::Mooshroom(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
||||||
EntityData::Ocelot(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
EntityState::Ocelot(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
||||||
EntityData::Painting(_) => todo!("account for rotation and type"),
|
EntityState::Painting(_) => todo!("account for rotation and type"),
|
||||||
EntityData::Panda(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
EntityState::Panda(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
||||||
EntityData::Parrot(_) => [0.5, 0.9, 0.5],
|
EntityState::Parrot(_) => [0.5, 0.9, 0.5],
|
||||||
EntityData::Phantom(_) => [0.9, 0.5, 0.9],
|
EntityState::Phantom(_) => [0.9, 0.5, 0.9],
|
||||||
EntityData::Pig(_) => [0.9, 0.9, 0.9], // TODO: baby size?
|
EntityState::Pig(_) => [0.9, 0.9, 0.9], // TODO: baby size?
|
||||||
EntityData::Piglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::Piglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::PiglinBrute(_) => [0.6, 1.95, 0.6],
|
EntityState::PiglinBrute(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::Pillager(_) => [0.6, 1.95, 0.6],
|
EntityState::Pillager(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::PolarBear(_) => [1.4, 1.4, 1.4], // TODO: baby size?
|
EntityState::PolarBear(_) => [1.4, 1.4, 1.4], // TODO: baby size?
|
||||||
EntityData::Tnt(_) => [0.98, 0.98, 0.98],
|
EntityState::Tnt(_) => [0.98, 0.98, 0.98],
|
||||||
EntityData::Pufferfish(_) => [0.7, 0.7, 0.7],
|
EntityState::Pufferfish(_) => [0.7, 0.7, 0.7],
|
||||||
EntityData::Rabbit(_) => [0.4, 0.5, 0.4], // TODO: baby size?
|
EntityState::Rabbit(_) => [0.4, 0.5, 0.4], // TODO: baby size?
|
||||||
EntityData::Ravager(_) => [1.95, 2.2, 1.95],
|
EntityState::Ravager(_) => [1.95, 2.2, 1.95],
|
||||||
EntityData::Salmon(_) => [0.7, 0.4, 0.7],
|
EntityState::Salmon(_) => [0.7, 0.4, 0.7],
|
||||||
EntityData::Sheep(_) => [0.9, 1.3, 0.9], // TODO: baby size?
|
EntityState::Sheep(_) => [0.9, 1.3, 0.9], // TODO: baby size?
|
||||||
EntityData::Shulker(_) => [1.0, 1.0, 1.0], // TODO: how is height calculated?
|
EntityState::Shulker(_) => [1.0, 1.0, 1.0], // TODO: how is height calculated?
|
||||||
EntityData::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125],
|
EntityState::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125],
|
||||||
EntityData::Silverfish(_) => [0.4, 0.3, 0.4],
|
EntityState::Silverfish(_) => [0.4, 0.3, 0.4],
|
||||||
EntityData::Skeleton(_) => [0.6, 1.99, 0.6],
|
EntityState::Skeleton(_) => [0.6, 1.99, 0.6],
|
||||||
EntityData::SkeletonHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
EntityState::SkeletonHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
||||||
EntityData::Slime(e) => {
|
EntityState::Slime(e) => {
|
||||||
let s = 0.51000005 * e.get_size() as f64;
|
let s = 0.51000005 * e.get_size() as f64;
|
||||||
[s, s, s]
|
[s, s, s]
|
||||||
}
|
}
|
||||||
EntityData::SmallFireball(_) => [0.3125, 0.3125, 0.3125],
|
EntityState::SmallFireball(_) => [0.3125, 0.3125, 0.3125],
|
||||||
EntityData::SnowGolem(_) => [0.7, 1.9, 0.7],
|
EntityState::SnowGolem(_) => [0.7, 1.9, 0.7],
|
||||||
EntityData::Snowball(_) => [0.25, 0.25, 0.25],
|
EntityState::Snowball(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::SpectralArrow(_) => [0.5, 0.5, 0.5],
|
EntityState::SpectralArrow(_) => [0.5, 0.5, 0.5],
|
||||||
EntityData::Spider(_) => [1.4, 0.9, 1.4],
|
EntityState::Spider(_) => [1.4, 0.9, 1.4],
|
||||||
EntityData::Squid(_) => [0.8, 0.8, 0.8],
|
EntityState::Squid(_) => [0.8, 0.8, 0.8],
|
||||||
EntityData::Stray(_) => [0.6, 1.99, 0.6],
|
EntityState::Stray(_) => [0.6, 1.99, 0.6],
|
||||||
EntityData::Strider(_) => [0.9, 1.7, 0.9], // TODO: baby size?
|
EntityState::Strider(_) => [0.9, 1.7, 0.9], // TODO: baby size?
|
||||||
EntityData::Egg(_) => [0.25, 0.25, 0.25],
|
EntityState::Egg(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::EnderPearl(_) => [0.25, 0.25, 0.25],
|
EntityState::EnderPearl(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::ExperienceBottle(_) => [0.25, 0.25, 0.25],
|
EntityState::ExperienceBottle(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::Potion(_) => [0.25, 0.25, 0.25],
|
EntityState::Potion(_) => [0.25, 0.25, 0.25],
|
||||||
EntityData::Trident(_) => [0.5, 0.5, 0.5],
|
EntityState::Trident(_) => [0.5, 0.5, 0.5],
|
||||||
EntityData::TraderLlama(_) => [0.9, 1.87, 0.9],
|
EntityState::TraderLlama(_) => [0.9, 1.87, 0.9],
|
||||||
EntityData::TropicalFish(_) => [0.5, 0.4, 0.5],
|
EntityState::TropicalFish(_) => [0.5, 0.4, 0.5],
|
||||||
EntityData::Turtle(_) => [1.2, 0.4, 1.2], // TODO: baby size?
|
EntityState::Turtle(_) => [1.2, 0.4, 1.2], // TODO: baby size?
|
||||||
EntityData::Vex(_) => [0.4, 0.8, 0.4],
|
EntityState::Vex(_) => [0.4, 0.8, 0.4],
|
||||||
EntityData::Villager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::Villager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::Vindicator(_) => [0.6, 1.95, 0.6],
|
EntityState::Vindicator(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::WanderingTrader(_) => [0.6, 1.95, 0.6],
|
EntityState::WanderingTrader(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::Witch(_) => [0.6, 1.95, 0.6],
|
EntityState::Witch(_) => [0.6, 1.95, 0.6],
|
||||||
EntityData::Wither(_) => [0.9, 3.5, 0.9],
|
EntityState::Wither(_) => [0.9, 3.5, 0.9],
|
||||||
EntityData::WitherSkeleton(_) => [0.7, 2.4, 0.7],
|
EntityState::WitherSkeleton(_) => [0.7, 2.4, 0.7],
|
||||||
EntityData::WitherSkull(_) => [0.3125, 0.3125, 0.3125],
|
EntityState::WitherSkull(_) => [0.3125, 0.3125, 0.3125],
|
||||||
EntityData::Wolf(_) => [0.6, 0.85, 0.6], // TODO: baby size?
|
EntityState::Wolf(_) => [0.6, 0.85, 0.6], // TODO: baby size?
|
||||||
EntityData::Zoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
EntityState::Zoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
||||||
EntityData::Zombie(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::Zombie(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::ZombieHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
EntityState::ZombieHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
||||||
EntityData::ZombieVillager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::ZombieVillager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::ZombifiedPiglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
EntityState::ZombifiedPiglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
||||||
EntityData::Player(_) => [0.6, 1.8, 0.6], // TODO: changes depending on the pose.
|
EntityState::Player(_) => [0.6, 1.8, 0.6], // TODO: changes depending on the pose.
|
||||||
EntityData::FishingBobber(_) => [0.25, 0.25, 0.25],
|
EntityState::FishingBobber(_) => [0.25, 0.25, 0.25],
|
||||||
};
|
};
|
||||||
|
|
||||||
aabb_from_bottom_and_size(self.new_position, dims.into())
|
aabb_from_bottom_and_size(self.new_position, dims.into())
|
||||||
|
@ -536,7 +533,7 @@ impl Entity {
|
||||||
///
|
///
|
||||||
/// Is `None` if there is no initial metadata.
|
/// Is `None` if there is no initial metadata.
|
||||||
pub(crate) fn initial_metadata_packet(&self, this_id: EntityId) -> Option<SetEntityMetadata> {
|
pub(crate) fn initial_metadata_packet(&self, this_id: EntityId) -> Option<SetEntityMetadata> {
|
||||||
self.data.initial_metadata().map(|meta| SetEntityMetadata {
|
self.state.initial_metadata().map(|meta| SetEntityMetadata {
|
||||||
entity_id: VarInt(this_id.to_network_id()),
|
entity_id: VarInt(this_id.to_network_id()),
|
||||||
metadata: RawBytes(meta),
|
metadata: RawBytes(meta),
|
||||||
})
|
})
|
||||||
|
@ -546,23 +543,23 @@ impl Entity {
|
||||||
///
|
///
|
||||||
/// Is `None` if this entity's metadata has not been modified.
|
/// Is `None` if this entity's metadata has not been modified.
|
||||||
pub(crate) fn updated_metadata_packet(&self, this_id: EntityId) -> Option<SetEntityMetadata> {
|
pub(crate) fn updated_metadata_packet(&self, this_id: EntityId) -> Option<SetEntityMetadata> {
|
||||||
self.data.updated_metadata().map(|meta| SetEntityMetadata {
|
self.state.updated_metadata().map(|meta| SetEntityMetadata {
|
||||||
entity_id: VarInt(this_id.to_network_id()),
|
entity_id: VarInt(this_id.to_network_id()),
|
||||||
metadata: RawBytes(meta),
|
metadata: RawBytes(meta),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn spawn_packet(&self, this_id: EntityId) -> Option<EntitySpawnPacket> {
|
pub(crate) fn spawn_packet(&self, this_id: EntityId) -> Option<EntitySpawnPacket> {
|
||||||
match &self.data {
|
match &self.state {
|
||||||
EntityData::Marker(_) => None,
|
EntityState::Marker(_) => None,
|
||||||
EntityData::ExperienceOrb(_) => {
|
EntityState::ExperienceOrb(_) => {
|
||||||
Some(EntitySpawnPacket::ExperienceOrb(AddExperienceOrb {
|
Some(EntitySpawnPacket::ExperienceOrb(AddExperienceOrb {
|
||||||
entity_id: VarInt(this_id.to_network_id()),
|
entity_id: VarInt(this_id.to_network_id()),
|
||||||
position: self.new_position,
|
position: self.new_position,
|
||||||
count: 0, // TODO
|
count: 0, // TODO
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
EntityData::Player(_) => Some(EntitySpawnPacket::Player(AddPlayer {
|
EntityState::Player(_) => Some(EntitySpawnPacket::Player(AddPlayer {
|
||||||
entity_id: VarInt(this_id.to_network_id()),
|
entity_id: VarInt(this_id.to_network_id()),
|
||||||
player_uuid: self.uuid,
|
player_uuid: self.uuid,
|
||||||
position: self.new_position,
|
position: self.new_position,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![allow(clippy::all, missing_docs)]
|
#![allow(clippy::all, missing_docs)]
|
||||||
|
|
||||||
use crate::block::{BlockPos, BlockState};
|
use crate::block::{BlockPos, BlockState};
|
||||||
use crate::entity::data::*;
|
use crate::entity::state::*;
|
||||||
use crate::entity::EntityId;
|
use crate::entity::EntityId;
|
||||||
use crate::protocol_inner::{Encode, VarInt};
|
use crate::protocol_inner::{Encode, VarInt};
|
||||||
use crate::text::Text;
|
use crate::text::Text;
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -52,17 +52,23 @@
|
||||||
//! use valence::server::{Server, ShutdownResult};
|
//! use valence::server::{Server, ShutdownResult};
|
||||||
//!
|
//!
|
||||||
//! pub fn main() -> ShutdownResult {
|
//! pub fn main() -> ShutdownResult {
|
||||||
//! valence::start_server(Game)
|
//! valence::start_server(Game, ())
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! struct Game;
|
//! struct Game;
|
||||||
//!
|
//!
|
||||||
//! impl Config for Game {
|
//! impl Config for Game {
|
||||||
|
//! type ChunkData = ();
|
||||||
|
//! type ClientData = ();
|
||||||
|
//! type EntityData = ();
|
||||||
|
//! type ServerData = ();
|
||||||
|
//! type WorldData = ();
|
||||||
|
//!
|
||||||
//! fn max_connections(&self) -> usize {
|
//! fn max_connections(&self) -> usize {
|
||||||
//! 256
|
//! 256
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn update(&self, server: &mut Server) {
|
//! fn update(&self, server: &mut Server<Self>) {
|
||||||
//! server.clients.retain(|_, client| {
|
//! server.clients.retain(|_, client| {
|
||||||
//! if client.created_tick() == server.shared.current_tick() {
|
//! if client.created_tick() == server.shared.current_tick() {
|
||||||
//! println!("{} joined!", client.username());
|
//! println!("{} joined!", client.username());
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/// Reading and writing whole packets.
|
/// Reading and writing whole packets.
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
|
@ -51,15 +51,17 @@ use crate::{Ticks, PROTOCOL_VERSION, VERSION_NAME};
|
||||||
|
|
||||||
/// Contains the entire state of a running Minecraft server, accessible from
|
/// Contains the entire state of a running Minecraft server, accessible from
|
||||||
/// within the [update](crate::config::Config::update) loop.
|
/// within the [update](crate::config::Config::update) loop.
|
||||||
pub struct Server {
|
pub struct Server<C: Config> {
|
||||||
|
/// Custom data.
|
||||||
|
pub data: C::ServerData,
|
||||||
/// A handle to this server's [`SharedServer`].
|
/// A handle to this server's [`SharedServer`].
|
||||||
pub shared: SharedServer,
|
pub shared: SharedServer<C>,
|
||||||
/// All of the clients in the server.
|
/// All of the clients in the server.
|
||||||
pub clients: Clients,
|
pub clients: Clients<C>,
|
||||||
/// All of entities in the server.
|
/// All of entities in the server.
|
||||||
pub entities: Entities,
|
pub entities: Entities<C>,
|
||||||
/// All of the worlds in the server.
|
/// All of the worlds in the server.
|
||||||
pub worlds: Worlds,
|
pub worlds: Worlds<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to a Minecraft server containing the subset of functionality which
|
/// A handle to a Minecraft server containing the subset of functionality which
|
||||||
|
@ -69,11 +71,17 @@ pub struct Server {
|
||||||
/// be shared between threads.
|
/// be shared between threads.
|
||||||
///
|
///
|
||||||
/// [update]: crate::config::Config::update
|
/// [update]: crate::config::Config::update
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SharedServer(Arc<SharedServerInner>);
|
|
||||||
|
|
||||||
struct SharedServerInner {
|
pub struct SharedServer<C: Config>(Arc<SharedServerInner<C>>);
|
||||||
cfg: Box<dyn Config>,
|
|
||||||
|
impl<C: Config> Clone for SharedServer<C> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SharedServerInner<C: Config> {
|
||||||
|
cfg: C,
|
||||||
address: SocketAddr,
|
address: SocketAddr,
|
||||||
tick_rate: Ticks,
|
tick_rate: Ticks,
|
||||||
online_mode: bool,
|
online_mode: bool,
|
||||||
|
@ -130,10 +138,10 @@ pub type ShutdownResult = Result<(), Box<dyn Error + Send + Sync + 'static>>;
|
||||||
pub(crate) type S2cPacketChannels = (Sender<C2sPlayPacket>, Receiver<S2cPlayPacket>);
|
pub(crate) type S2cPacketChannels = (Sender<C2sPlayPacket>, Receiver<S2cPlayPacket>);
|
||||||
pub(crate) type C2sPacketChannels = (Sender<S2cPlayPacket>, Receiver<C2sPlayPacket>);
|
pub(crate) type C2sPacketChannels = (Sender<S2cPlayPacket>, Receiver<C2sPlayPacket>);
|
||||||
|
|
||||||
impl SharedServer {
|
impl<C: Config> SharedServer<C> {
|
||||||
/// Gets a reference to the config object used to start the server.
|
/// Gets a reference to the config object used to start the server.
|
||||||
pub fn config(&self) -> &(impl Config + ?Sized) {
|
pub fn config(&self) -> &C {
|
||||||
self.0.cfg.as_ref()
|
&self.0.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the socket address this server is bound to.
|
/// Gets the socket address this server is bound to.
|
||||||
|
@ -240,12 +248,13 @@ impl SharedServer {
|
||||||
///
|
///
|
||||||
/// The function returns once the server has shut down, a runtime error
|
/// The function returns once the server has shut down, a runtime error
|
||||||
/// occurs, or the configuration is found to be invalid.
|
/// occurs, or the configuration is found to be invalid.
|
||||||
pub fn start_server(config: impl Config) -> ShutdownResult {
|
pub fn start_server<C: Config>(config: C, data: C::ServerData) -> ShutdownResult {
|
||||||
let shared = setup_server(config).map_err(Box::<dyn Error + Send + Sync + 'static>::from)?;
|
let shared = setup_server(config).map_err(Box::<dyn Error + Send + Sync + 'static>::from)?;
|
||||||
|
|
||||||
let _guard = shared.tokio_handle().enter();
|
let _guard = shared.tokio_handle().enter();
|
||||||
|
|
||||||
let mut server = Server {
|
let mut server = Server {
|
||||||
|
data,
|
||||||
shared: shared.clone(),
|
shared: shared.clone(),
|
||||||
clients: Clients::new(),
|
clients: Clients::new(),
|
||||||
entities: Entities::new(),
|
entities: Entities::new(),
|
||||||
|
@ -259,7 +268,7 @@ pub fn start_server(config: impl Config) -> ShutdownResult {
|
||||||
do_update_loop(&mut server)
|
do_update_loop(&mut server)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_server(cfg: impl Config) -> anyhow::Result<SharedServer> {
|
fn setup_server<C: Config>(cfg: C) -> anyhow::Result<SharedServer<C>> {
|
||||||
let max_connections = cfg.max_connections();
|
let max_connections = cfg.max_connections();
|
||||||
let address = cfg.address();
|
let address = cfg.address();
|
||||||
let tick_rate = cfg.tick_rate();
|
let tick_rate = cfg.tick_rate();
|
||||||
|
@ -360,7 +369,7 @@ fn setup_server(cfg: impl Config) -> anyhow::Result<SharedServer> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = SharedServerInner {
|
let server = SharedServerInner {
|
||||||
cfg: Box::new(cfg),
|
cfg,
|
||||||
address,
|
address,
|
||||||
tick_rate,
|
tick_rate,
|
||||||
online_mode,
|
online_mode,
|
||||||
|
@ -385,7 +394,7 @@ fn setup_server(cfg: impl Config) -> anyhow::Result<SharedServer> {
|
||||||
Ok(SharedServer(Arc::new(server)))
|
Ok(SharedServer(Arc::new(server)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_update_loop(server: &mut Server) -> ShutdownResult {
|
fn do_update_loop<C: Config>(server: &mut Server<C>) -> ShutdownResult {
|
||||||
let mut tick_start = Instant::now();
|
let mut tick_start = Instant::now();
|
||||||
|
|
||||||
let shared = server.shared.clone();
|
let shared = server.shared.clone();
|
||||||
|
@ -441,7 +450,7 @@ fn do_update_loop(server: &mut Server) -> ShutdownResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join_player(server: &mut Server, msg: NewClientMessage) {
|
fn join_player<C: Config>(server: &mut Server<C>, msg: NewClientMessage) {
|
||||||
let (clientbound_tx, clientbound_rx) = flume::bounded(server.shared.0.outgoing_packet_capacity);
|
let (clientbound_tx, clientbound_rx) = flume::bounded(server.shared.0.outgoing_packet_capacity);
|
||||||
let (serverbound_tx, serverbound_rx) = flume::bounded(server.shared.0.incoming_packet_capacity);
|
let (serverbound_tx, serverbound_rx) = flume::bounded(server.shared.0.incoming_packet_capacity);
|
||||||
|
|
||||||
|
@ -450,7 +459,12 @@ fn join_player(server: &mut Server, msg: NewClientMessage) {
|
||||||
|
|
||||||
let _ = msg.reply.send(s2c_packet_channels);
|
let _ = msg.reply.send(s2c_packet_channels);
|
||||||
|
|
||||||
let client = Client::new(c2s_packet_channels, &server.shared, msg.ncd);
|
let client = Client::new(
|
||||||
|
c2s_packet_channels,
|
||||||
|
&server.shared,
|
||||||
|
msg.ncd,
|
||||||
|
C::ClientData::default(),
|
||||||
|
);
|
||||||
|
|
||||||
server.clients.insert(client);
|
server.clients.insert(client);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +474,7 @@ struct Codec {
|
||||||
dec: Decoder<OwnedReadHalf>,
|
dec: Decoder<OwnedReadHalf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_accept_loop(server: SharedServer) {
|
async fn do_accept_loop<C: Config>(server: SharedServer<C>) {
|
||||||
log::trace!("entering accept loop");
|
log::trace!("entering accept loop");
|
||||||
|
|
||||||
let listener = match TcpListener::bind(server.0.address).await {
|
let listener = match TcpListener::bind(server.0.address).await {
|
||||||
|
@ -502,8 +516,8 @@ async fn do_accept_loop(server: SharedServer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection(
|
async fn handle_connection<C: Config>(
|
||||||
server: SharedServer,
|
server: SharedServer<C>,
|
||||||
stream: TcpStream,
|
stream: TcpStream,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
@ -533,8 +547,8 @@ async fn handle_connection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_status(
|
async fn handle_status<C: Config>(
|
||||||
server: SharedServer,
|
server: SharedServer<C>,
|
||||||
c: &mut Codec,
|
c: &mut Codec,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
@ -585,8 +599,8 @@ async fn handle_status(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle the login process and return the new player's data if successful.
|
/// Handle the login process and return the new player's data if successful.
|
||||||
async fn handle_login(
|
async fn handle_login<C: Config>(
|
||||||
server: &SharedServer,
|
server: &SharedServer<C>,
|
||||||
c: &mut Codec,
|
c: &mut Codec,
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
) -> anyhow::Result<Option<NewClientData>> {
|
) -> anyhow::Result<Option<NewClientData>> {
|
||||||
|
@ -725,7 +739,11 @@ async fn handle_login(
|
||||||
Ok(Some(npd))
|
Ok(Some(npd))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_play(server: &SharedServer, c: Codec, ncd: NewClientData) -> anyhow::Result<()> {
|
async fn handle_play<C: Config>(
|
||||||
|
server: &SharedServer<C>,
|
||||||
|
c: Codec,
|
||||||
|
ncd: NewClientData,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let (reply_tx, reply_rx) = oneshot::channel();
|
let (reply_tx, reply_rx) = oneshot::channel();
|
||||||
|
|
||||||
server
|
server
|
||||||
|
|
|
@ -6,6 +6,7 @@ use rayon::iter::{IndexedParallelIterator, ParallelIterator};
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
|
||||||
use crate::bvh::{Bvh, Node};
|
use crate::bvh::{Bvh, Node};
|
||||||
|
use crate::config::Config;
|
||||||
use crate::entity::{Entities, EntityId};
|
use crate::entity::{Entities, EntityId};
|
||||||
use crate::util::ray_box_intersect;
|
use crate::util::ray_box_intersect;
|
||||||
use crate::world::WorldId;
|
use crate::world::WorldId;
|
||||||
|
@ -225,7 +226,7 @@ impl SpatialIndex {
|
||||||
self.bvh.par_iter().map(|(&id, bb)| (id, bb))
|
self.bvh.par_iter().map(|(&id, bb)| (id, bb))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&mut self, entities: &Entities, id: WorldId) {
|
pub(crate) fn update<C: Config>(&mut self, entities: &Entities<C>, id: WorldId) {
|
||||||
self.bvh.build(
|
self.bvh.build(
|
||||||
entities
|
entities
|
||||||
.iter()
|
.iter()
|
||||||
|
|
34
src/world.rs
34
src/world.rs
|
@ -5,6 +5,7 @@ use std::iter::FusedIterator;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
|
||||||
use crate::chunk::Chunks;
|
use crate::chunk::Chunks;
|
||||||
|
use crate::config::Config;
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::player_list::PlayerList;
|
use crate::player_list::PlayerList;
|
||||||
use crate::server::SharedServer;
|
use crate::server::SharedServer;
|
||||||
|
@ -12,9 +13,9 @@ use crate::slotmap::{Key, SlotMap};
|
||||||
use crate::spatial_index::SpatialIndex;
|
use crate::spatial_index::SpatialIndex;
|
||||||
|
|
||||||
/// A container for all [`World`]s on a [`Server`](crate::server::Server).
|
/// A container for all [`World`]s on a [`Server`](crate::server::Server).
|
||||||
pub struct Worlds {
|
pub struct Worlds<C: Config> {
|
||||||
sm: SlotMap<World>,
|
sm: SlotMap<World<C>>,
|
||||||
server: SharedServer,
|
server: SharedServer<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An identifier for a [`World`] on the server.
|
/// An identifier for a [`World`] on the server.
|
||||||
|
@ -34,8 +35,8 @@ impl WorldId {
|
||||||
pub const NULL: Self = Self(Key::NULL);
|
pub const NULL: Self = Self(Key::NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Worlds {
|
impl<C: Config> Worlds<C> {
|
||||||
pub(crate) fn new(server: SharedServer) -> Self {
|
pub(crate) fn new(server: SharedServer<C>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sm: SlotMap::new(),
|
sm: SlotMap::new(),
|
||||||
server,
|
server,
|
||||||
|
@ -44,8 +45,9 @@ impl Worlds {
|
||||||
|
|
||||||
/// Creates a new world on the server with the provided dimension. A
|
/// Creates a new world on the server with the provided dimension. A
|
||||||
/// reference to the world along with its ID is returned.
|
/// reference to the world along with its ID is returned.
|
||||||
pub fn create(&mut self, dim: DimensionId) -> (WorldId, &mut World) {
|
pub fn create(&mut self, dim: DimensionId, data: C::WorldData) -> (WorldId, &mut World<C>) {
|
||||||
let (id, world) = self.sm.insert(World {
|
let (id, world) = self.sm.insert(World {
|
||||||
|
data,
|
||||||
spatial_index: SpatialIndex::new(),
|
spatial_index: SpatialIndex::new(),
|
||||||
chunks: Chunks::new(self.server.clone(), dim),
|
chunks: Chunks::new(self.server.clone(), dim),
|
||||||
meta: WorldMeta {
|
meta: WorldMeta {
|
||||||
|
@ -71,7 +73,7 @@ impl Worlds {
|
||||||
/// `f` returns `true`.
|
/// `f` returns `true`.
|
||||||
///
|
///
|
||||||
/// All worlds are visited in an unspecified order.
|
/// All worlds are visited in an unspecified order.
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World) -> bool) {
|
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World<C>) -> bool) {
|
||||||
self.sm.retain(|k, v| f(WorldId(k), v))
|
self.sm.retain(|k, v| f(WorldId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,47 +84,49 @@ impl Worlds {
|
||||||
|
|
||||||
/// Returns a shared reference to the world with the given ID. If
|
/// Returns a shared reference to the world with the given ID. If
|
||||||
/// the ID is invalid, then `None` is returned.
|
/// the ID is invalid, then `None` is returned.
|
||||||
pub fn get(&self, world: WorldId) -> Option<&World> {
|
pub fn get(&self, world: WorldId) -> Option<&World<C>> {
|
||||||
self.sm.get(world.0)
|
self.sm.get(world.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an exclusive reference to the world with the given ID. If the
|
/// Returns an exclusive reference to the world with the given ID. If the
|
||||||
/// ID is invalid, then `None` is returned.
|
/// ID is invalid, then `None` is returned.
|
||||||
pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World> {
|
pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World<C>> {
|
||||||
self.sm.get_mut(world.0)
|
self.sm.get_mut(world.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an immutable iterator over all worlds on the server in an
|
/// Returns an immutable iterator over all worlds on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (WorldId, &World)> + Clone + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (WorldId, &World<C>)> + Clone + '_ {
|
||||||
self.sm.iter().map(|(k, v)| (WorldId(k), v))
|
self.sm.iter().map(|(k, v)| (WorldId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable iterator over all worlds on the server in an
|
/// Returns a mutable iterator over all worlds on the server in an
|
||||||
/// unspecified ordder.
|
/// unspecified ordder.
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (WorldId, &mut World)> + '_ {
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (WorldId, &mut World<C>)> + '_ {
|
||||||
self.sm.iter_mut().map(|(k, v)| (WorldId(k), v))
|
self.sm.iter_mut().map(|(k, v)| (WorldId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel immutable iterator over all worlds on the server in
|
/// Returns a parallel immutable iterator over all worlds on the server in
|
||||||
/// an unspecified order.
|
/// an unspecified order.
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, &World)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, &World<C>)> + Clone + '_ {
|
||||||
self.sm.par_iter().map(|(k, v)| (WorldId(k), v))
|
self.sm.par_iter().map(|(k, v)| (WorldId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel mutable iterator over all worlds on the server in an
|
/// Returns a parallel mutable iterator over all worlds on the server in an
|
||||||
/// unspecified order.
|
/// unspecified order.
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (WorldId, &mut World)> + '_ {
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (WorldId, &mut World<C>)> + '_ {
|
||||||
self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v))
|
self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A space for chunks, entities, and clients to occupy.
|
/// A space for chunks, entities, and clients to occupy.
|
||||||
pub struct World {
|
pub struct World<C: Config> {
|
||||||
|
/// Custom data.
|
||||||
|
pub data: C::WorldData,
|
||||||
/// Contains all of the entities in this world.
|
/// Contains all of the entities in this world.
|
||||||
pub spatial_index: SpatialIndex,
|
pub spatial_index: SpatialIndex,
|
||||||
/// All of the chunks in this world.
|
/// All of the chunks in this world.
|
||||||
pub chunks: Chunks,
|
pub chunks: Chunks<C>,
|
||||||
/// This world's metadata.
|
/// This world's metadata.
|
||||||
pub meta: WorldMeta,
|
pub meta: WorldMeta,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue