mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Remove &mut wrappers
The mutable reference wappers were causing more problems than they were solving. We will document the dangers of mem::swap later.
This commit is contained in:
parent
e97df76a75
commit
806ffa4f42
|
@ -9,8 +9,8 @@ use valence::config::{Config, ServerListPing};
|
||||||
use valence::text::Color;
|
use valence::text::Color;
|
||||||
use valence::util::to_yaw_and_pitch;
|
use valence::util::to_yaw_and_pitch;
|
||||||
use valence::{
|
use valence::{
|
||||||
async_trait, ClientMut, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text,
|
async_trait, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text,
|
||||||
TextFormat, WorldId, WorldsMut,
|
TextFormat, WorldId, Worlds, Client,
|
||||||
};
|
};
|
||||||
use vek::{Mat3, Vec3};
|
use vek::{Mat3, Vec3};
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ impl Config for Game {
|
||||||
fn join(
|
fn join(
|
||||||
&self,
|
&self,
|
||||||
_server: &Server,
|
_server: &Server,
|
||||||
_client: ClientMut,
|
_client: &mut Client,
|
||||||
worlds: WorldsMut,
|
worlds: &mut Worlds,
|
||||||
) -> Result<WorldId, Text> {
|
) -> Result<WorldId, Text> {
|
||||||
if let Ok(_) = self
|
if let Ok(_) = self
|
||||||
.player_count
|
.player_count
|
||||||
|
@ -72,8 +72,8 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
|
fn init(&self, _server: &Server, worlds: &mut Worlds) {
|
||||||
let mut world = worlds.create(DimensionId::default()).1;
|
let world = worlds.create(DimensionId::default()).1;
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
|
|
||||||
let size = 5;
|
let size = 5;
|
||||||
|
@ -84,16 +84,16 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cows.lock().unwrap().extend((0..200).map(|_| {
|
self.cows.lock().unwrap().extend((0..200).map(|_| {
|
||||||
let (id, mut e) = world.entities.create();
|
let (id, e) = world.entities.create();
|
||||||
e.set_type(EntityType::Cow);
|
e.set_type(EntityType::Cow);
|
||||||
id
|
id
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &Server, mut worlds: WorldsMut) {
|
fn update(&self, server: &Server, worlds: &mut Worlds) {
|
||||||
let mut world = worlds.iter_mut().next().unwrap().1;
|
let world = worlds.iter_mut().next().unwrap().1;
|
||||||
|
|
||||||
world.clients.retain(|_, mut client| {
|
world.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.current_tick() {
|
if client.created_tick() == server.current_tick() {
|
||||||
client.set_game_mode(GameMode::Creative);
|
client.set_game_mode(GameMode::Creative);
|
||||||
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
||||||
|
@ -136,7 +136,7 @@ impl Config for Game {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
for (cow_id, p) in cows.iter().cloned().zip(fibonacci_spiral(cow_count)) {
|
for (cow_id, p) in cows.iter().cloned().zip(fibonacci_spiral(cow_count)) {
|
||||||
let mut cow = world.entities.get_mut(cow_id).expect("missing cow");
|
let cow = world.entities.get_mut(cow_id).expect("missing cow");
|
||||||
let rotated = p * rot;
|
let rotated = p * rot;
|
||||||
let transformed = rotated * radius + [0.0, 100.0, 0.0];
|
let transformed = rotated * radius + [0.0, 100.0, 0.0];
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ use valence::config::{Config, ServerListPing};
|
||||||
use valence::text::Color;
|
use valence::text::Color;
|
||||||
use valence::util::chunks_in_view_distance;
|
use valence::util::chunks_in_view_distance;
|
||||||
use valence::{
|
use valence::{
|
||||||
async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, TextFormat,
|
async_trait, ChunkPos, Client, DimensionId, Server, ShutdownResult, Text, TextFormat, WorldId,
|
||||||
WorldId, WorldsMut,
|
Worlds,
|
||||||
};
|
};
|
||||||
use vek::Lerp;
|
use vek::Lerp;
|
||||||
|
|
||||||
|
@ -69,8 +69,8 @@ impl Config for Game {
|
||||||
fn join(
|
fn join(
|
||||||
&self,
|
&self,
|
||||||
_server: &Server,
|
_server: &Server,
|
||||||
_client: ClientMut,
|
_client: &mut Client,
|
||||||
worlds: WorldsMut,
|
worlds: &mut Worlds,
|
||||||
) -> Result<WorldId, Text> {
|
) -> Result<WorldId, Text> {
|
||||||
if let Ok(_) = self
|
if let Ok(_) = self
|
||||||
.player_count
|
.player_count
|
||||||
|
@ -84,17 +84,17 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
|
fn init(&self, _server: &Server, worlds: &mut Worlds) {
|
||||||
let (_, mut world) = worlds.create(DimensionId::default());
|
let (_, world) = worlds.create(DimensionId::default());
|
||||||
world.meta.set_flat(true);
|
world.meta.set_flat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &Server, mut worlds: WorldsMut) {
|
fn update(&self, server: &Server, worlds: &mut Worlds) {
|
||||||
let mut world = worlds.iter_mut().next().unwrap().1;
|
let world = worlds.iter_mut().next().unwrap().1;
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
world.clients.retain(|_, mut client| {
|
world.clients.retain(|_, client| {
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
return false;
|
return false;
|
||||||
|
@ -132,7 +132,7 @@ impl Config for Game {
|
||||||
world.chunks.delete(pos);
|
world.chunks.delete(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
world.chunks.par_iter_mut().for_each(|(pos, mut chunk)| {
|
world.chunks.par_iter_mut().for_each(|(pos, chunk)| {
|
||||||
if chunk.created_tick() == server.current_tick() {
|
if chunk.created_tick() == server.current_tick() {
|
||||||
for z in 0..16 {
|
for z in 0..16 {
|
||||||
for x in 0..16 {
|
for x in 0..16 {
|
||||||
|
|
158
src/chunk.rs
158
src/chunk.rs
|
@ -3,7 +3,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use bitvec::vec::BitVec;
|
use bitvec::vec::BitVec;
|
||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
@ -33,6 +32,16 @@ impl Chunks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> bool {
|
||||||
|
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
||||||
|
let chunk = Chunk::new(section_count, self.server.current_tick());
|
||||||
|
self.chunks.insert(pos.into(), chunk).is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, pos: ChunkPos) -> bool {
|
||||||
|
self.chunks.remove(&pos).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.chunks.len()
|
self.chunks.len()
|
||||||
}
|
}
|
||||||
|
@ -41,6 +50,10 @@ impl Chunks {
|
||||||
self.chunks.get(&pos.into())
|
self.chunks.get(&pos.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, pos: impl Into<ChunkPos>) -> Option<&mut Chunk> {
|
||||||
|
self.chunks.get_mut(&pos.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.chunks.clear();
|
self.chunks.clear();
|
||||||
}
|
}
|
||||||
|
@ -49,10 +62,18 @@ impl Chunks {
|
||||||
self.chunks.iter().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.iter().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ChunkPos, &mut Chunk)> + '_ {
|
||||||
|
self.chunks.iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ChunkPos, &Chunk)> + Clone + '_ {
|
||||||
self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk))
|
self.chunks.par_iter().map(|(&pos, chunk)| (pos, chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ChunkPos, &mut Chunk)> + '_ {
|
||||||
|
self.chunks.par_iter_mut().map(|(&pos, chunk)| (pos, chunk))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> {
|
pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> {
|
||||||
let pos = pos.into();
|
let pos = pos.into();
|
||||||
let chunk_pos = ChunkPos::from(pos);
|
let chunk_pos = ChunkPos::from(pos);
|
||||||
|
@ -73,55 +94,17 @@ impl Chunks {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ChunksMut<'a> {
|
|
||||||
pub(crate) fn new(chunks: &'a mut Chunks) -> Self {
|
|
||||||
Self(chunks)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> ChunksMut {
|
|
||||||
ChunksMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> bool {
|
|
||||||
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
|
||||||
let chunk = Chunk::new(section_count, self.server.current_tick());
|
|
||||||
self.0.chunks.insert(pos.into(), chunk).is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self, pos: ChunkPos) -> bool {
|
|
||||||
self.0.chunks.remove(&pos).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self, pos: impl Into<ChunkPos>) -> Option<ChunkMut> {
|
|
||||||
self.0.chunks.get_mut(&pos.into()).map(ChunkMut)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ChunkPos, ChunkMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.chunks
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(&pos, chunk)| (pos, ChunkMut(chunk)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ChunkPos, ChunkMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.chunks
|
|
||||||
.par_iter_mut()
|
|
||||||
.map(|(&pos, chunk)| (pos, ChunkMut(chunk)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_block_state(&mut self, pos: impl Into<BlockPos>, block: BlockState) -> bool {
|
pub fn set_block_state(&mut self, pos: impl Into<BlockPos>, block: BlockState) -> bool {
|
||||||
let pos = pos.into();
|
let pos = pos.into();
|
||||||
let chunk_pos = ChunkPos::from(pos);
|
let chunk_pos = ChunkPos::from(pos);
|
||||||
|
|
||||||
if let Some(chunk) = self.0.chunks.get_mut(&chunk_pos) {
|
if let Some(chunk) = self.chunks.get_mut(&chunk_pos) {
|
||||||
let min_y = self.0.server.dimension(self.0.dimension).min_y;
|
let min_y = self.server.dimension(self.dimension).min_y;
|
||||||
|
|
||||||
if let Some(y) = pos.y.checked_sub(min_y).and_then(|y| y.try_into().ok()) {
|
if let Some(y) = pos.y.checked_sub(min_y).and_then(|y| y.try_into().ok()) {
|
||||||
if y < chunk.height() {
|
if y < chunk.height() {
|
||||||
ChunkMut(chunk).set_block_state(
|
chunk.set_block_state(
|
||||||
pos.x.rem_euclid(16) as usize,
|
pos.x.rem_euclid(16) as usize,
|
||||||
y,
|
y,
|
||||||
pos.z.rem_euclid(16) as usize,
|
pos.z.rem_euclid(16) as usize,
|
||||||
|
@ -136,16 +119,6 @@ impl<'a> ChunksMut<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunksMut<'a>(&'a mut Chunks);
|
|
||||||
|
|
||||||
impl<'a> Deref for ChunksMut<'a> {
|
|
||||||
type Target = Chunks;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
sections: Box<[ChunkSection]>,
|
sections: Box<[ChunkSection]>,
|
||||||
// TODO block_entities: HashMap<u32, BlockEntity>,
|
// TODO block_entities: HashMap<u32, BlockEntity>,
|
||||||
|
@ -169,7 +142,7 @@ impl Chunk {
|
||||||
created_tick: current_tick,
|
created_tick: current_tick,
|
||||||
};
|
};
|
||||||
|
|
||||||
ChunkMut(&mut chunk).apply_modifications();
|
chunk.apply_modifications();
|
||||||
chunk
|
chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +164,27 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) {
|
||||||
|
assert!(
|
||||||
|
x < 16 && y < self.height() && z < 16,
|
||||||
|
"the chunk block coordinates must be within bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
let sect = &mut self.sections[y / 16];
|
||||||
|
let idx = x + z * 16 + y % 16 * 16 * 16;
|
||||||
|
|
||||||
|
if block.to_raw() != sect.blocks[idx] & BLOCK_STATE_MASK {
|
||||||
|
if sect.blocks[idx] & !BLOCK_STATE_MASK == 0 {
|
||||||
|
sect.modified_count += 1;
|
||||||
|
}
|
||||||
|
sect.blocks[idx] = block.to_raw() | !BLOCK_STATE_MASK;
|
||||||
|
|
||||||
|
// TODO: if the block type was modified and the old block type
|
||||||
|
// could be a block entity, then the block entity at this
|
||||||
|
// position must be cleared.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_biome(&self, x: usize, y: usize, z: usize) -> BiomeId {
|
pub fn get_biome(&self, x: usize, y: usize, z: usize) -> BiomeId {
|
||||||
if x < 4 && y < self.height() / 4 && z < 4 {
|
if x < 4 && y < self.height() / 4 && z < 4 {
|
||||||
self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4]
|
self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4]
|
||||||
|
@ -199,6 +193,15 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) {
|
||||||
|
assert!(
|
||||||
|
x < 4 && y < self.height() / 4 && z < 4,
|
||||||
|
"the chunk biome coordinates must be within bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
self.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b;
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the chunk data packet for this chunk with the given position. This
|
/// Gets the chunk data packet for this chunk with the given position. This
|
||||||
/// does not include unapplied changes.
|
/// does not include unapplied changes.
|
||||||
pub(crate) fn chunk_data_packet(&self, pos: ChunkPos) -> ChunkDataAndUpdateLight {
|
pub(crate) fn chunk_data_packet(&self, pos: ChunkPos) -> ChunkDataAndUpdateLight {
|
||||||
|
@ -283,53 +286,11 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ChunkMut<'a>(&'a mut Chunk);
|
|
||||||
|
|
||||||
impl<'a> Deref for ChunkMut<'a> {
|
|
||||||
type Target = Chunk;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ChunkMut<'a> {
|
|
||||||
pub fn set_block_state(&mut self, x: usize, y: usize, z: usize, block: BlockState) {
|
|
||||||
assert!(
|
|
||||||
x < 16 && y < self.height() && z < 16,
|
|
||||||
"the chunk block coordinates must be within bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
let sect = &mut self.0.sections[y / 16];
|
|
||||||
let idx = x + z * 16 + y % 16 * 16 * 16;
|
|
||||||
|
|
||||||
if block.to_raw() != sect.blocks[idx] & BLOCK_STATE_MASK {
|
|
||||||
if sect.blocks[idx] & !BLOCK_STATE_MASK == 0 {
|
|
||||||
sect.modified_count += 1;
|
|
||||||
}
|
|
||||||
sect.blocks[idx] = block.to_raw() | !BLOCK_STATE_MASK;
|
|
||||||
|
|
||||||
// TODO: if the block type was modified and the old block type
|
|
||||||
// could be a block entity, then the block entity at this
|
|
||||||
// position must be cleared.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_biome(&mut self, x: usize, y: usize, z: usize, b: BiomeId) {
|
|
||||||
assert!(
|
|
||||||
x < 4 && y < self.height() / 4 && z < 4,
|
|
||||||
"the chunk biome coordinates must be within bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.0.sections[y / 4].biomes[x + z * 4 + y % 4 * 4 * 4] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn apply_modifications(&mut self) {
|
pub(crate) fn apply_modifications(&mut self) {
|
||||||
let mut any_modified = false;
|
let mut any_modified = false;
|
||||||
|
|
||||||
for sect in self.0.sections.iter_mut() {
|
for sect in self.sections.iter_mut() {
|
||||||
if sect.modified_count > 0 {
|
if sect.modified_count > 0 {
|
||||||
sect.modified_count = 0;
|
sect.modified_count = 0;
|
||||||
any_modified = true;
|
any_modified = true;
|
||||||
|
@ -370,7 +331,7 @@ impl<'a> ChunkMut<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if any_modified {
|
if any_modified {
|
||||||
build_heightmap(&self.0.sections, &mut self.0.heightmap);
|
build_heightmap(&self.sections, &mut self.heightmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,7 +491,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_get() {
|
fn set_get() {
|
||||||
let mut chunk = Chunk::new(16, 0);
|
let mut chunk = Chunk::new(16, 0);
|
||||||
let mut chunk = ChunkMut(&mut chunk);
|
|
||||||
|
|
||||||
chunk.set_block_state(1, 2, 3, BlockState::CAKE);
|
chunk.set_block_state(1, 2, 3, BlockState::CAKE);
|
||||||
assert_eq!(chunk.get_block_state(1, 2, 3), BlockState::CAKE);
|
assert_eq!(chunk.get_block_state(1, 2, 3), BlockState::CAKE);
|
||||||
|
@ -543,7 +503,6 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn block_state_oob() {
|
fn block_state_oob() {
|
||||||
let mut chunk = Chunk::new(16, 0);
|
let mut chunk = Chunk::new(16, 0);
|
||||||
let mut chunk = ChunkMut(&mut chunk);
|
|
||||||
|
|
||||||
chunk.set_block_state(16, 0, 0, BlockState::CAKE);
|
chunk.set_block_state(16, 0, 0, BlockState::CAKE);
|
||||||
}
|
}
|
||||||
|
@ -552,7 +511,6 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn biome_oob() {
|
fn biome_oob() {
|
||||||
let mut chunk = Chunk::new(16, 0);
|
let mut chunk = Chunk::new(16, 0);
|
||||||
let mut chunk = ChunkMut(&mut chunk);
|
|
||||||
|
|
||||||
chunk.set_biome(4, 0, 0, BiomeId(0));
|
chunk.set_biome(4, 0, 0, BiomeId(0));
|
||||||
}
|
}
|
||||||
|
|
368
src/client.rs
368
src/client.rs
|
@ -2,7 +2,6 @@
|
||||||
mod event;
|
mod event;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
use flume::{Receiver, Sender, TrySendError};
|
use flume::{Receiver, Sender, TrySendError};
|
||||||
|
@ -40,21 +39,24 @@ pub struct Clients {
|
||||||
sm: SlotMap<Client>,
|
sm: SlotMap<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientsMut<'a>(&'a mut Clients);
|
|
||||||
|
|
||||||
impl<'a> Deref for ClientsMut<'a> {
|
|
||||||
type Target = Clients;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clients {
|
impl Clients {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { sm: SlotMap::new() }
|
Self { sm: SlotMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create(&mut self, client: Client) -> (ClientId, &mut Client) {
|
||||||
|
let (id, client) = self.sm.insert(client);
|
||||||
|
(ClientId(id), client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, client: ClientId) -> bool {
|
||||||
|
self.sm.remove(client.0).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retain(&mut self, mut f: impl FnMut(ClientId, &mut Client) -> bool) {
|
||||||
|
self.sm.retain(|k, v| f(ClientId(k), v))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.sm.len()
|
self.sm.len()
|
||||||
}
|
}
|
||||||
|
@ -63,55 +65,27 @@ impl Clients {
|
||||||
self.sm.get(client.0)
|
self.sm.get(client.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, client: ClientId) -> Option<&mut Client> {
|
||||||
|
self.sm.get_mut(client.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
||||||
self.sm.iter().map(|(k, v)| (ClientId(k), v))
|
self.sm.iter().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ClientId, &mut Client)> + '_ {
|
||||||
|
self.sm.iter_mut().map(|(k, v)| (ClientId(k), v))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (ClientId, &Client)> + Clone + '_ {
|
||||||
self.sm.par_iter().map(|(k, v)| (ClientId(k), v))
|
self.sm.par_iter().map(|(k, v)| (ClientId(k), v))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ClientsMut<'a> {
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ClientId, &mut Client)> + '_ {
|
||||||
pub(crate) fn new(c: &'a mut Clients) -> Self {
|
self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v))
|
||||||
Self(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> ClientsMut {
|
|
||||||
ClientsMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create(&mut self, client: Client) -> (ClientId, ClientMut) {
|
|
||||||
let (id, client) = self.0.sm.insert(client);
|
|
||||||
(ClientId(id), ClientMut(client))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self, client: ClientId) -> bool {
|
|
||||||
self.0.sm.remove(client.0).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(ClientId, ClientMut) -> bool) {
|
|
||||||
self.0.sm.retain(|k, v| f(ClientId(k), ClientMut(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self, client: ClientId) -> Option<ClientMut> {
|
|
||||||
self.0.sm.get_mut(client.0).map(ClientMut)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (ClientId, ClientMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.sm
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(k, v)| (ClientId(k), ClientMut(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ClientId, ClientMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.sm
|
|
||||||
.par_iter_mut()
|
|
||||||
.map(|(k, v)| (ClientId(k), ClientMut(v)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientId(Key);
|
pub struct ClientId(Key);
|
||||||
|
|
||||||
/// Represents a client connected to the server after logging in.
|
/// Represents a client connected to the server after logging in.
|
||||||
|
@ -165,16 +139,6 @@ pub struct Client {
|
||||||
// player_meta: Player,
|
// player_meta: Player,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientMut<'a>(&'a mut Client);
|
|
||||||
|
|
||||||
impl<'a> Deref for ClientMut<'a> {
|
|
||||||
type Target = Client;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
packet_channels: C2sPacketChannels,
|
packet_channels: C2sPacketChannels,
|
||||||
|
@ -236,6 +200,27 @@ impl Client {
|
||||||
self.new_position
|
self.new_position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
||||||
|
self.new_position = pos.into();
|
||||||
|
|
||||||
|
self.yaw = yaw;
|
||||||
|
self.pitch = pitch;
|
||||||
|
|
||||||
|
if !self.teleported_this_tick {
|
||||||
|
self.teleported_this_tick = true;
|
||||||
|
|
||||||
|
self.pending_teleports = match self.pending_teleports.checked_add(1) {
|
||||||
|
Some(n) => n,
|
||||||
|
None => {
|
||||||
|
self.disconnect("Too many pending teleports");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.teleport_id_counter = self.teleport_id_counter.wrapping_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn yaw(&self) -> f32 {
|
pub fn yaw(&self) -> f32 {
|
||||||
self.yaw
|
self.yaw
|
||||||
}
|
}
|
||||||
|
@ -250,6 +235,17 @@ impl Client {
|
||||||
self.spawn_position
|
self.spawn_position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the spawn position. The client will see regular compasses point at
|
||||||
|
/// the provided position.
|
||||||
|
pub fn set_spawn_position(&mut self, pos: impl Into<BlockPos>, yaw_degrees: f32) {
|
||||||
|
let pos = pos.into();
|
||||||
|
if pos != self.spawn_position || yaw_degrees != self.spawn_position_yaw {
|
||||||
|
self.spawn_position = pos;
|
||||||
|
self.spawn_position_yaw = yaw_degrees;
|
||||||
|
self.modified_spawn_position = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the last death location. The client will see recovery compasses
|
/// Gets the last death location. The client will see recovery compasses
|
||||||
/// point at the returned position. If the client's current dimension
|
/// point at the returned position. If the client's current dimension
|
||||||
/// differs from the returned dimension or the location is `None` then the
|
/// differs from the returned dimension or the location is `None` then the
|
||||||
|
@ -258,10 +254,25 @@ impl Client {
|
||||||
self.death_location
|
self.death_location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the last death location. The client will see recovery compasses
|
||||||
|
/// point at the provided position. If the client's current dimension
|
||||||
|
/// differs from the provided dimension or the location is `None` then the
|
||||||
|
/// compass will spin randomly.
|
||||||
|
///
|
||||||
|
/// Changes to the last death location take effect when the client
|
||||||
|
/// (re)spawns.
|
||||||
|
pub fn set_death_location(&mut self, location: Option<(DimensionId, BlockPos)>) {
|
||||||
|
self.death_location = location;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn game_mode(&self) -> GameMode {
|
pub fn game_mode(&self) -> GameMode {
|
||||||
self.new_game_mode
|
self.new_game_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_game_mode(&mut self, new_game_mode: GameMode) {
|
||||||
|
self.new_game_mode = new_game_mode;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_ground(&self) -> bool {
|
pub fn on_ground(&self) -> bool {
|
||||||
self.on_ground
|
self.on_ground
|
||||||
}
|
}
|
||||||
|
@ -274,6 +285,10 @@ impl Client {
|
||||||
&self.events
|
&self.events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn events_mut(&mut self) -> &mut Vec<Event> {
|
||||||
|
&mut self.events
|
||||||
|
}
|
||||||
|
|
||||||
/// The current view distance of this client measured in chunks.
|
/// The current view distance of this client measured in chunks.
|
||||||
pub fn view_distance(&self) -> u8 {
|
pub fn view_distance(&self) -> u8 {
|
||||||
self.settings
|
self.settings
|
||||||
|
@ -286,110 +301,47 @@ impl Client {
|
||||||
self.new_max_view_distance
|
self.new_max_view_distance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The new view distance is clamped to `2..=32`.
|
||||||
|
pub fn set_max_view_distance(&mut self, dist: u8) {
|
||||||
|
self.new_max_view_distance = dist.clamp(2, 32);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn settings(&self) -> Option<&Settings> {
|
pub fn settings(&self) -> Option<&Settings> {
|
||||||
self.settings.as_ref()
|
self.settings.as_ref()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ClientMut<'a> {
|
pub fn disconnect(&mut self, reason: impl Into<Text>) {
|
||||||
pub(crate) fn new(client: &'a mut Client) -> Self {
|
if self.send.is_some() {
|
||||||
Self(client)
|
let txt = reason.into();
|
||||||
}
|
log::info!("disconnecting client '{}': \"{txt}\"", self.username);
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> ClientMut {
|
self.send_packet(Disconnect { reason: txt });
|
||||||
ClientMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn events_mut(&mut self) -> &mut Vec<Event> {
|
self.send = None;
|
||||||
&mut self.0.events
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
|
||||||
self.0.new_position = pos.into();
|
|
||||||
|
|
||||||
self.0.yaw = yaw;
|
|
||||||
self.0.pitch = pitch;
|
|
||||||
|
|
||||||
if !self.teleported_this_tick {
|
|
||||||
self.0.teleported_this_tick = true;
|
|
||||||
|
|
||||||
self.0.pending_teleports = match self.0.pending_teleports.checked_add(1) {
|
|
||||||
Some(n) => n,
|
|
||||||
None => {
|
|
||||||
self.disconnect("Too many pending teleports");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.0.teleport_id_counter = self.0.teleport_id_counter.wrapping_add(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_game_mode(&mut self, new_game_mode: GameMode) {
|
pub fn disconnect_no_reason(&mut self) {
|
||||||
self.0.new_game_mode = new_game_mode;
|
if self.send.is_some() {
|
||||||
}
|
log::info!("disconnecting client '{}' (no reason)", self.username);
|
||||||
|
self.send = None;
|
||||||
/// Sets the spawn position. The client will see regular compasses point at
|
|
||||||
/// the provided position.
|
|
||||||
pub fn set_spawn_position(&mut self, pos: impl Into<BlockPos>, yaw_degrees: f32) {
|
|
||||||
let pos = pos.into();
|
|
||||||
if pos != self.0.spawn_position || yaw_degrees != self.0.spawn_position_yaw {
|
|
||||||
self.0.spawn_position = pos;
|
|
||||||
self.0.spawn_position_yaw = yaw_degrees;
|
|
||||||
self.0.modified_spawn_position = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the last death location. The client will see recovery compasses
|
|
||||||
/// point at the provided position. If the client's current dimension
|
|
||||||
/// differs from the provided dimension or the location is `None` then the
|
|
||||||
/// compass will spin randomly.
|
|
||||||
///
|
|
||||||
/// Changes to the last death location take effect when the client
|
|
||||||
/// (re)spawns.
|
|
||||||
pub fn set_death_location(&mut self, location: Option<(DimensionId, BlockPos)>) {
|
|
||||||
self.0.death_location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to enqueue a play packet to be sent to this client. The client
|
/// Attempts to enqueue a play packet to be sent to this client. The client
|
||||||
/// is disconnected if the clientbound packet buffer is full.
|
/// is disconnected if the clientbound packet buffer is full.
|
||||||
pub(crate) fn send_packet(&mut self, packet: impl Into<S2cPlayPacket>) {
|
pub(crate) fn send_packet(&mut self, packet: impl Into<S2cPlayPacket>) {
|
||||||
send_packet(&mut self.0.send, packet);
|
send_packet(&mut self.send, packet);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disconnect(&mut self, reason: impl Into<Text>) {
|
|
||||||
if self.0.send.is_some() {
|
|
||||||
let txt = reason.into();
|
|
||||||
log::info!("disconnecting client '{}': \"{txt}\"", self.0.username);
|
|
||||||
|
|
||||||
self.send_packet(Disconnect { reason: txt });
|
|
||||||
|
|
||||||
self.0.send = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disconnect_no_reason(&mut self) {
|
|
||||||
if self.0.send.is_some() {
|
|
||||||
log::info!("disconnecting client '{}' (no reason)", self.0.username);
|
|
||||||
self.0.send = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The new view distance is clamped to `2..=32`.
|
|
||||||
pub fn set_max_view_distance(&mut self, dist: u8) {
|
|
||||||
self.0.new_max_view_distance = dist.clamp(2, 32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) {
|
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) {
|
||||||
self.0.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, pkt: C2sPlayPacket) {
|
||||||
let client = &mut self.0;
|
|
||||||
|
|
||||||
fn handle_movement_packet(
|
fn handle_movement_packet(
|
||||||
client: &mut Client,
|
client: &mut Client,
|
||||||
_vehicle: bool,
|
_vehicle: bool,
|
||||||
|
@ -419,18 +371,18 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
match pkt {
|
match pkt {
|
||||||
C2sPlayPacket::TeleportConfirm(p) => {
|
C2sPlayPacket::TeleportConfirm(p) => {
|
||||||
if client.pending_teleports == 0 {
|
if self.pending_teleports == 0 {
|
||||||
self.disconnect("Unexpected teleport confirmation");
|
self.disconnect("Unexpected teleport confirmation");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let got = p.teleport_id.0 as u32;
|
let got = p.teleport_id.0 as u32;
|
||||||
let expected = client
|
let expected = self
|
||||||
.teleport_id_counter
|
.teleport_id_counter
|
||||||
.wrapping_sub(client.pending_teleports);
|
.wrapping_sub(self.pending_teleports);
|
||||||
|
|
||||||
if got == expected {
|
if got == expected {
|
||||||
client.pending_teleports -= 1;
|
self.pending_teleports -= 1;
|
||||||
} else {
|
} else {
|
||||||
self.disconnect(format!(
|
self.disconnect(format!(
|
||||||
"Unexpected teleport ID (expected {expected}, got {got})"
|
"Unexpected teleport ID (expected {expected}, got {got})"
|
||||||
|
@ -444,7 +396,7 @@ impl<'a> ClientMut<'a> {
|
||||||
C2sPlayPacket::ChatPreview(_) => {}
|
C2sPlayPacket::ChatPreview(_) => {}
|
||||||
C2sPlayPacket::ClientStatus(_) => {}
|
C2sPlayPacket::ClientStatus(_) => {}
|
||||||
C2sPlayPacket::ClientSettings(p) => {
|
C2sPlayPacket::ClientSettings(p) => {
|
||||||
let old = client.settings.replace(Settings {
|
let old = self.settings.replace(Settings {
|
||||||
locale: p.locale.0,
|
locale: p.locale.0,
|
||||||
view_distance: p.view_distance.0,
|
view_distance: p.view_distance.0,
|
||||||
chat_mode: p.chat_mode,
|
chat_mode: p.chat_mode,
|
||||||
|
@ -454,7 +406,7 @@ impl<'a> ClientMut<'a> {
|
||||||
allow_server_listings: p.allow_server_listings,
|
allow_server_listings: p.allow_server_listings,
|
||||||
});
|
});
|
||||||
|
|
||||||
client.events.push(Event::SettingsChanged(old));
|
self.events.push(Event::SettingsChanged(old));
|
||||||
}
|
}
|
||||||
C2sPlayPacket::TabComplete(_) => {}
|
C2sPlayPacket::TabComplete(_) => {}
|
||||||
C2sPlayPacket::ClickWindowButton(_) => {}
|
C2sPlayPacket::ClickWindowButton(_) => {}
|
||||||
|
@ -468,7 +420,7 @@ impl<'a> ClientMut<'a> {
|
||||||
// that the distance is <=4 blocks.
|
// that the distance is <=4 blocks.
|
||||||
|
|
||||||
use crate::packets::play::c2s::InteractType;
|
use crate::packets::play::c2s::InteractType;
|
||||||
client.events.push(Event::InteractWithEntity {
|
self.events.push(Event::InteractWithEntity {
|
||||||
id,
|
id,
|
||||||
sneaking: p.sneaking,
|
sneaking: p.sneaking,
|
||||||
typ: match p.typ {
|
typ: match p.typ {
|
||||||
|
@ -483,8 +435,8 @@ impl<'a> ClientMut<'a> {
|
||||||
}
|
}
|
||||||
C2sPlayPacket::GenerateStructure(_) => {}
|
C2sPlayPacket::GenerateStructure(_) => {}
|
||||||
C2sPlayPacket::KeepAlive(p) => {
|
C2sPlayPacket::KeepAlive(p) => {
|
||||||
let last_keepalive_id = client.last_keepalive_id;
|
let last_keepalive_id = self.last_keepalive_id;
|
||||||
if client.got_keepalive {
|
if self.got_keepalive {
|
||||||
self.disconnect("Unexpected keepalive");
|
self.disconnect("Unexpected keepalive");
|
||||||
} else if p.id != last_keepalive_id {
|
} else if p.id != last_keepalive_id {
|
||||||
self.disconnect(format!(
|
self.disconnect(format!(
|
||||||
|
@ -492,42 +444,32 @@ impl<'a> ClientMut<'a> {
|
||||||
last_keepalive_id, p.id
|
last_keepalive_id, p.id
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
client.got_keepalive = true;
|
self.got_keepalive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::LockDifficulty(_) => {}
|
C2sPlayPacket::LockDifficulty(_) => {}
|
||||||
C2sPlayPacket::PlayerPosition(p) => handle_movement_packet(
|
C2sPlayPacket::PlayerPosition(p) => {
|
||||||
client,
|
handle_movement_packet(self, false, p.position, self.yaw, self.pitch, p.on_ground)
|
||||||
false,
|
}
|
||||||
p.position,
|
C2sPlayPacket::PlayerPositionAndRotation(p) => {
|
||||||
client.yaw,
|
handle_movement_packet(self, false, p.position, p.yaw, p.pitch, p.on_ground)
|
||||||
client.pitch,
|
}
|
||||||
p.on_ground,
|
C2sPlayPacket::PlayerRotation(p) => {
|
||||||
),
|
handle_movement_packet(self, false, self.new_position, p.yaw, p.pitch, p.on_ground)
|
||||||
C2sPlayPacket::PlayerPositionAndRotation(p) => {
|
|
||||||
handle_movement_packet(client, false, p.position, p.yaw, p.pitch, p.on_ground)
|
|
||||||
}
|
}
|
||||||
C2sPlayPacket::PlayerRotation(p) => handle_movement_packet(
|
|
||||||
client,
|
|
||||||
false,
|
|
||||||
client.new_position,
|
|
||||||
p.yaw,
|
|
||||||
p.pitch,
|
|
||||||
p.on_ground,
|
|
||||||
),
|
|
||||||
C2sPlayPacket::PlayerMovement(p) => handle_movement_packet(
|
C2sPlayPacket::PlayerMovement(p) => handle_movement_packet(
|
||||||
client,
|
self,
|
||||||
false,
|
false,
|
||||||
client.new_position,
|
self.new_position,
|
||||||
client.yaw,
|
self.yaw,
|
||||||
client.pitch,
|
self.pitch,
|
||||||
p.on_ground,
|
p.on_ground,
|
||||||
),
|
),
|
||||||
C2sPlayPacket::VehicleMove(p) => {
|
C2sPlayPacket::VehicleMove(p) => {
|
||||||
handle_movement_packet(client, true, p.position, p.yaw, p.pitch, client.on_ground);
|
handle_movement_packet(self, true, p.position, p.yaw, p.pitch, self.on_ground);
|
||||||
}
|
}
|
||||||
C2sPlayPacket::SteerBoat(p) => {
|
C2sPlayPacket::SteerBoat(p) => {
|
||||||
client.events.push(Event::SteerBoat {
|
self.events.push(Event::SteerBoat {
|
||||||
left_paddle_turning: p.left_paddle_turning,
|
left_paddle_turning: p.left_paddle_turning,
|
||||||
right_paddle_turning: p.right_paddle_turning,
|
right_paddle_turning: p.right_paddle_turning,
|
||||||
});
|
});
|
||||||
|
@ -540,10 +482,10 @@ impl<'a> ClientMut<'a> {
|
||||||
// TODO: verify that the broken block is allowed to be broken?
|
// TODO: verify that the broken block is allowed to be broken?
|
||||||
|
|
||||||
if p.sequence.0 != 0 {
|
if p.sequence.0 != 0 {
|
||||||
client.dug_blocks.push(p.sequence.0);
|
self.dug_blocks.push(p.sequence.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.events.push(match p.status {
|
self.events.push(match p.status {
|
||||||
DiggingStatus::StartedDigging => Event::Digging(Digging {
|
DiggingStatus::StartedDigging => Event::Digging(Digging {
|
||||||
status: event::DiggingStatus::Start,
|
status: event::DiggingStatus::Start,
|
||||||
position: p.location,
|
position: p.location,
|
||||||
|
@ -599,7 +541,7 @@ impl<'a> ClientMut<'a> {
|
||||||
) {
|
) {
|
||||||
// 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.0.send = None;
|
self.send = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,11 +583,11 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
self.teleport(self.position(), self.yaw(), self.pitch());
|
self.teleport(self.position(), self.yaw(), self.pitch());
|
||||||
} else {
|
} else {
|
||||||
if self.0.old_game_mode != self.0.new_game_mode {
|
if self.old_game_mode != self.new_game_mode {
|
||||||
self.0.old_game_mode = self.0.new_game_mode;
|
self.old_game_mode = self.new_game_mode;
|
||||||
self.send_packet(ChangeGameState {
|
self.send_packet(ChangeGameState {
|
||||||
reason: ChangeGameStateReason::ChangeGameMode,
|
reason: ChangeGameStateReason::ChangeGameMode,
|
||||||
value: self.0.new_game_mode as i32 as f32,
|
value: self.new_game_mode as i32 as f32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,8 +595,8 @@ impl<'a> ClientMut<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the players spawn position (compass position)
|
// Update the players spawn position (compass position)
|
||||||
if self.0.modified_spawn_position {
|
if self.modified_spawn_position {
|
||||||
self.0.modified_spawn_position = false;
|
self.modified_spawn_position = false;
|
||||||
|
|
||||||
self.send_packet(SpawnPosition {
|
self.send_packet(SpawnPosition {
|
||||||
location: self.spawn_position,
|
location: self.spawn_position,
|
||||||
|
@ -663,22 +605,22 @@ impl<'a> ClientMut<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update view distance fog on the client if necessary.
|
// Update view distance fog on the client if necessary.
|
||||||
if self.0.old_max_view_distance != self.0.new_max_view_distance {
|
if self.old_max_view_distance != self.new_max_view_distance {
|
||||||
self.0.old_max_view_distance = self.0.new_max_view_distance;
|
self.old_max_view_distance = self.new_max_view_distance;
|
||||||
if self.0.created_tick != current_tick {
|
if self.created_tick != current_tick {
|
||||||
self.send_packet(UpdateViewDistance {
|
self.send_packet(UpdateViewDistance {
|
||||||
view_distance: BoundedInt(VarInt(self.0.new_max_view_distance as i32)),
|
view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's time to send another keepalive.
|
// Check if it's time to send another keepalive.
|
||||||
if current_tick % (server.tick_rate() * 8) == 0 {
|
if current_tick % (server.tick_rate() * 8) == 0 {
|
||||||
if self.0.got_keepalive {
|
if self.got_keepalive {
|
||||||
let id = rand::random();
|
let id = rand::random();
|
||||||
self.send_packet(KeepAlive { id });
|
self.send_packet(KeepAlive { id });
|
||||||
self.0.last_keepalive_id = id;
|
self.last_keepalive_id = id;
|
||||||
self.0.got_keepalive = false;
|
self.got_keepalive = false;
|
||||||
} else {
|
} else {
|
||||||
self.disconnect("Timed out (no keepalive response)");
|
self.disconnect("Timed out (no keepalive response)");
|
||||||
}
|
}
|
||||||
|
@ -691,8 +633,8 @@ impl<'a> ClientMut<'a> {
|
||||||
// Send the update view position packet if the client changes the chunk section
|
// Send the update view position packet if the client changes the chunk section
|
||||||
// they're in.
|
// they're in.
|
||||||
{
|
{
|
||||||
let old_section = self.0.old_position.map(|n| (n / 16.0).floor() as i32);
|
let old_section = self.old_position.map(|n| (n / 16.0).floor() as i32);
|
||||||
let new_section = self.0.new_position.map(|n| (n / 16.0).floor() as i32);
|
let new_section = self.new_position.map(|n| (n / 16.0).floor() as i32);
|
||||||
|
|
||||||
if old_section != new_section {
|
if old_section != new_section {
|
||||||
self.send_packet(UpdateViewPosition {
|
self.send_packet(UpdateViewPosition {
|
||||||
|
@ -706,7 +648,7 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
// Update existing chunks and unload those outside the view distance. Chunks
|
// Update existing chunks and unload those outside the view distance. Chunks
|
||||||
// that have been overwritten also need to be unloaded.
|
// that have been overwritten also need to be unloaded.
|
||||||
self.0.loaded_chunks.retain(|&pos| {
|
self.loaded_chunks.retain(|&pos| {
|
||||||
// The cache stops chunk data packets from needing to be sent when a player
|
// The cache stops chunk data packets from needing to be sent when a player
|
||||||
// moves to an adjacent chunk and back to the original.
|
// moves to an adjacent chunk and back to the original.
|
||||||
let cache = 2;
|
let cache = 2;
|
||||||
|
@ -716,14 +658,14 @@ impl<'a> ClientMut<'a> {
|
||||||
&& chunk.created_tick() != current_tick
|
&& chunk.created_tick() != current_tick
|
||||||
{
|
{
|
||||||
chunk.block_change_packets(pos, dimension.min_y, |pkt| {
|
chunk.block_change_packets(pos, dimension.min_y, |pkt| {
|
||||||
send_packet(&mut self.0.send, pkt)
|
send_packet(&mut self.send, pkt)
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
UnloadChunk {
|
UnloadChunk {
|
||||||
chunk_x: pos.x,
|
chunk_x: pos.x,
|
||||||
chunk_z: pos.z,
|
chunk_z: pos.z,
|
||||||
|
@ -735,7 +677,7 @@ impl<'a> ClientMut<'a> {
|
||||||
// Load new chunks within the view distance
|
// Load new chunks within the view distance
|
||||||
for pos in chunks_in_view_distance(center, view_dist) {
|
for pos in chunks_in_view_distance(center, view_dist) {
|
||||||
if let Some(chunk) = chunks.get(pos) {
|
if let Some(chunk) = chunks.get(pos) {
|
||||||
if self.0.loaded_chunks.insert(pos) {
|
if self.loaded_chunks.insert(pos) {
|
||||||
self.send_packet(chunk.chunk_data_packet(pos));
|
self.send_packet(chunk.chunk_data_packet(pos));
|
||||||
chunk.block_change_packets(pos, dimension.min_y, |pkt| self.send_packet(pkt));
|
chunk.block_change_packets(pos, dimension.min_y, |pkt| self.send_packet(pkt));
|
||||||
}
|
}
|
||||||
|
@ -743,9 +685,9 @@ impl<'a> ClientMut<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acknowledge broken blocks.
|
// Acknowledge broken blocks.
|
||||||
for seq in self.0.dug_blocks.drain(..) {
|
for seq in self.dug_blocks.drain(..) {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
AcknowledgeBlockChanges {
|
AcknowledgeBlockChanges {
|
||||||
sequence: VarInt(seq),
|
sequence: VarInt(seq),
|
||||||
},
|
},
|
||||||
|
@ -754,8 +696,8 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
// This is done after the chunks are loaded so that the "downloading terrain"
|
// This is done after the chunks are loaded so that the "downloading terrain"
|
||||||
// screen is closed at the appropriate time.
|
// screen is closed at the appropriate time.
|
||||||
if self.0.teleported_this_tick {
|
if self.teleported_this_tick {
|
||||||
self.0.teleported_this_tick = false;
|
self.teleported_this_tick = false;
|
||||||
|
|
||||||
self.send_packet(PlayerPositionAndLook {
|
self.send_packet(PlayerPositionAndLook {
|
||||||
position: self.new_position,
|
position: self.new_position,
|
||||||
|
@ -771,14 +713,14 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
// Update all entities that are visible and unload entities that are no
|
// Update all entities that are visible and unload entities that are no
|
||||||
// longer visible.
|
// longer visible.
|
||||||
self.0.loaded_entities.retain(|&id| {
|
self.loaded_entities.retain(|&id| {
|
||||||
if let Some(entity) = entities.get(id) {
|
if let Some(entity) = entities.get(id) {
|
||||||
debug_assert!(entity.typ() != EntityType::Marker);
|
debug_assert!(entity.typ() != EntityType::Marker);
|
||||||
if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0
|
if self.new_position.distance(entity.position()) <= view_dist as f64 * 16.0
|
||||||
&& !entity.flags().type_modified()
|
&& !entity.flags().type_modified()
|
||||||
{
|
{
|
||||||
if let Some(meta) = entity.updated_metadata_packet(id) {
|
if let Some(meta) = entity.updated_metadata_packet(id) {
|
||||||
send_packet(&mut self.0.send, meta);
|
send_packet(&mut self.send, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
let position_delta = entity.position() - entity.old_position();
|
let position_delta = entity.position() - entity.old_position();
|
||||||
|
@ -790,7 +732,7 @@ impl<'a> ClientMut<'a> {
|
||||||
&& flags.yaw_or_pitch_modified()
|
&& flags.yaw_or_pitch_modified()
|
||||||
{
|
{
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityPositionAndRotation {
|
EntityPositionAndRotation {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
delta: (position_delta * 4096.0).as_(),
|
delta: (position_delta * 4096.0).as_(),
|
||||||
|
@ -802,7 +744,7 @@ impl<'a> ClientMut<'a> {
|
||||||
} else {
|
} else {
|
||||||
if entity.position() != entity.old_position() && !needs_teleport {
|
if entity.position() != entity.old_position() && !needs_teleport {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityPosition {
|
EntityPosition {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
delta: (position_delta * 4096.0).as_(),
|
delta: (position_delta * 4096.0).as_(),
|
||||||
|
@ -813,7 +755,7 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
if flags.yaw_or_pitch_modified() {
|
if flags.yaw_or_pitch_modified() {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityRotation {
|
EntityRotation {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
yaw: ByteAngle::from_degrees(entity.yaw()),
|
yaw: ByteAngle::from_degrees(entity.yaw()),
|
||||||
|
@ -826,7 +768,7 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
if needs_teleport {
|
if needs_teleport {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityTeleport {
|
EntityTeleport {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
position: entity.position(),
|
position: entity.position(),
|
||||||
|
@ -839,7 +781,7 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
if flags.velocity_modified() {
|
if flags.velocity_modified() {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityVelocity {
|
EntityVelocity {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
velocity: velocity_to_packet_units(entity.velocity()),
|
velocity: velocity_to_packet_units(entity.velocity()),
|
||||||
|
@ -849,7 +791,7 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
if flags.head_yaw_modified() {
|
if flags.head_yaw_modified() {
|
||||||
send_packet(
|
send_packet(
|
||||||
&mut self.0.send,
|
&mut self.send,
|
||||||
EntityHeadLook {
|
EntityHeadLook {
|
||||||
entity_id: VarInt(id.to_network_id()),
|
entity_id: VarInt(id.to_network_id()),
|
||||||
head_yaw: ByteAngle::from_degrees(entity.head_yaw()),
|
head_yaw: ByteAngle::from_degrees(entity.head_yaw()),
|
||||||
|
@ -876,7 +818,7 @@ impl<'a> ClientMut<'a> {
|
||||||
spatial_index.query::<_, _, ()>(
|
spatial_index.query::<_, _, ()>(
|
||||||
|bb| bb.projected_point(pos).distance(pos) <= view_dist as f64 * 16.0,
|
|bb| bb.projected_point(pos).distance(pos) <= view_dist as f64 * 16.0,
|
||||||
|id, _| {
|
|id, _| {
|
||||||
if self.0.loaded_entities.insert(id) {
|
if self.loaded_entities.insert(id) {
|
||||||
let entity = entities.get(id).unwrap();
|
let entity = entities.get(id).unwrap();
|
||||||
if entity.typ() != EntityType::Marker {
|
if entity.typ() != EntityType::Marker {
|
||||||
self.send_packet(
|
self.send_packet(
|
||||||
|
@ -894,13 +836,7 @@ impl<'a> ClientMut<'a> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.0.old_position = self.0.new_position;
|
self.old_position = self.new_position;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Client {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
log::trace!("Dropping client '{}'", self.username);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::runtime::Handle as TokioHandle;
|
use tokio::runtime::Handle as TokioHandle;
|
||||||
|
|
||||||
use crate::client::ClientMut;
|
use crate::{Biome, Client, Dimension, NewClientData, Server, Text, Ticks, WorldId, Worlds};
|
||||||
use crate::{Biome, Dimension, NewClientData, Server, Text, Ticks, WorldId, WorldsMut};
|
|
||||||
|
|
||||||
/// A trait containing callbacks which are invoked by the running Minecraft
|
/// A trait containing callbacks which are invoked by the running Minecraft
|
||||||
/// server.
|
/// server.
|
||||||
|
@ -182,7 +181,12 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// If the returned [`WorldId`] is invalid, then the client is disconnected.
|
/// If the returned [`WorldId`] is invalid, then the client is disconnected.
|
||||||
///
|
///
|
||||||
/// This method is called from within a tokio runtime.
|
/// This method is called from within a tokio runtime.
|
||||||
fn join(&self, server: &Server, client: ClientMut, worlds: WorldsMut) -> Result<WorldId, Text>;
|
fn join(
|
||||||
|
&self,
|
||||||
|
server: &Server,
|
||||||
|
client: &mut Client,
|
||||||
|
worlds: &mut Worlds,
|
||||||
|
) -> Result<WorldId, Text>;
|
||||||
|
|
||||||
/// Called after the server is created, but prior to accepting connections
|
/// Called after the server is created, but prior to accepting connections
|
||||||
/// and entering the update loop.
|
/// and entering the update loop.
|
||||||
|
@ -191,7 +195,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: &Server, worlds: WorldsMut) {}
|
fn init(&self, server: &Server, worlds: &mut Worlds) {}
|
||||||
|
|
||||||
/// Called once at the beginning of every server update (also known as
|
/// Called once at the beginning of every server update (also known as
|
||||||
/// a "tick").
|
/// a "tick").
|
||||||
|
@ -203,7 +207,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: &Server, worlds: WorldsMut);
|
fn update(&self, server: &Server, worlds: &mut Worlds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of the [`server_list_ping`](Handler::server_list_ping) callback.
|
/// The result of the [`server_list_ping`](Handler::server_list_ping) callback.
|
||||||
|
|
339
src/entity.rs
339
src/entity.rs
|
@ -5,10 +5,10 @@ use std::collections::hash_map::Entry;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
pub use types::{EntityMeta, EntityType};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
|
||||||
|
@ -27,16 +27,6 @@ pub struct Entities {
|
||||||
network_id_to_entity: HashMap<NonZeroU32, u32>,
|
network_id_to_entity: HashMap<NonZeroU32, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EntitiesMut<'a>(&'a mut Entities);
|
|
||||||
|
|
||||||
impl<'a> Deref for EntitiesMut<'a> {
|
|
||||||
type Target = Entities;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entities {
|
impl Entities {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -46,8 +36,80 @@ impl Entities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawns a new entity with the default data. The new entity's [`EntityId`]
|
||||||
|
/// is returned.
|
||||||
|
///
|
||||||
|
/// To actually see the new entity, set its position to somewhere nearby and
|
||||||
|
/// [set its type](EntityData::set_type) to something visible.
|
||||||
|
pub fn create(&mut self) -> (EntityId, &mut Entity) {
|
||||||
|
self.create_with_uuid(Uuid::from_bytes(rand::random()))
|
||||||
|
.expect("UUID collision")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`create`](Entities::create), but requires specifying the new
|
||||||
|
/// entity's UUID.
|
||||||
|
///
|
||||||
|
/// The provided UUID must not conflict with an existing entity UUID in this
|
||||||
|
/// world. If it does, `None` is returned and the entity is not spawned.
|
||||||
|
pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<(EntityId, &mut Entity)> {
|
||||||
|
match self.uuid_to_entity.entry(uuid) {
|
||||||
|
Entry::Occupied(_) => None,
|
||||||
|
Entry::Vacant(ve) => {
|
||||||
|
let (k, e) = self.sm.insert(Entity {
|
||||||
|
flags: EntityFlags(0),
|
||||||
|
meta: EntityMeta::new(EntityType::Marker),
|
||||||
|
new_position: Vec3::default(),
|
||||||
|
old_position: Vec3::default(),
|
||||||
|
yaw: 0.0,
|
||||||
|
pitch: 0.0,
|
||||||
|
head_yaw: 0.0,
|
||||||
|
velocity: Vec3::default(),
|
||||||
|
uuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
ve.insert(EntityId(k));
|
||||||
|
|
||||||
|
Some((EntityId(k), e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, entity: EntityId) -> bool {
|
||||||
|
if let Some(e) = self.sm.remove(entity.0) {
|
||||||
|
self.uuid_to_entity
|
||||||
|
.remove(&e.uuid)
|
||||||
|
.expect("UUID should have been in UUID map");
|
||||||
|
|
||||||
|
self.network_id_to_entity
|
||||||
|
.remove(&entity.0.version())
|
||||||
|
.expect("network ID should have been in the network ID map");
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retain(&mut self, mut f: impl FnMut(EntityId, &mut Entity) -> bool) {
|
||||||
|
self.sm.retain(|k, v| {
|
||||||
|
if f(EntityId(k), v) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.uuid_to_entity
|
||||||
|
.remove(&v.uuid)
|
||||||
|
.expect("UUID should have been in UUID map");
|
||||||
|
|
||||||
|
self.network_id_to_entity
|
||||||
|
.remove(&k.version())
|
||||||
|
.expect("network ID should have been in the network ID map");
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of live entities.
|
/// Returns the number of live entities.
|
||||||
pub fn len(&self) -> usize {
|
pub fn count(&self) -> usize {
|
||||||
self.sm.len()
|
self.sm.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +126,10 @@ impl Entities {
|
||||||
self.sm.get(entity.0)
|
self.sm.get(entity.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, entity: EntityId) -> Option<&mut Entity> {
|
||||||
|
self.sm.get_mut(entity.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option<EntityId> {
|
pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option<EntityId> {
|
||||||
let version = NonZeroU32::new(network_id as u32)?;
|
let version = NonZeroU32::new(network_id as u32)?;
|
||||||
let index = *self.network_id_to_entity.get(&version)?;
|
let index = *self.network_id_to_entity.get(&version)?;
|
||||||
|
@ -74,122 +140,26 @@ impl Entities {
|
||||||
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (EntityId, &mut Entity)> + '_ {
|
||||||
|
self.sm.iter_mut().map(|(k, v)| (EntityId(k), v))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
||||||
self.sm.par_iter().map(|(k, v)| (EntityId(k), v))
|
self.sm.par_iter().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EntitiesMut<'a> {
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (EntityId, &mut Entity)> + '_ {
|
||||||
pub(crate) fn new(entities: &'a mut Entities) -> Self {
|
self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v))
|
||||||
Self(entities)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> EntitiesMut {
|
|
||||||
EntitiesMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawns a new entity with the default data. The new entity's [`EntityId`]
|
|
||||||
/// is returned.
|
|
||||||
///
|
|
||||||
/// To actually see the new entity, set its position to somewhere nearby and
|
|
||||||
/// [set its type](EntityData::set_type) to something visible.
|
|
||||||
pub fn create(&mut self) -> (EntityId, EntityMut) {
|
|
||||||
self.create_with_uuid(Uuid::from_bytes(rand::random()))
|
|
||||||
.expect("UUID collision")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like [`create`](Entities::create), but requires specifying the new
|
|
||||||
/// entity's UUID.
|
|
||||||
///
|
|
||||||
/// The provided UUID must not conflict with an existing entity UUID in this
|
|
||||||
/// world. If it does, `None` is returned and the entity is not spawned.
|
|
||||||
pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<(EntityId, EntityMut)> {
|
|
||||||
match self.0.uuid_to_entity.entry(uuid) {
|
|
||||||
Entry::Occupied(_) => None,
|
|
||||||
Entry::Vacant(ve) => {
|
|
||||||
let (k, e) = self.0.sm.insert(Entity {
|
|
||||||
flags: EntityFlags(0),
|
|
||||||
meta: EntityMeta::new(EntityType::Marker),
|
|
||||||
new_position: Vec3::default(),
|
|
||||||
old_position: Vec3::default(),
|
|
||||||
yaw: 0.0,
|
|
||||||
pitch: 0.0,
|
|
||||||
head_yaw: 0.0,
|
|
||||||
velocity: Vec3::default(),
|
|
||||||
uuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
ve.insert(EntityId(k));
|
|
||||||
|
|
||||||
Some((EntityId(k), EntityMut(e)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self, entity: EntityId) -> bool {
|
|
||||||
if let Some(e) = self.0.sm.remove(entity.0) {
|
|
||||||
self.0
|
|
||||||
.uuid_to_entity
|
|
||||||
.remove(&e.uuid)
|
|
||||||
.expect("UUID should have been in UUID map");
|
|
||||||
|
|
||||||
self.0
|
|
||||||
.network_id_to_entity
|
|
||||||
.remove(&entity.0.version())
|
|
||||||
.expect("network ID should have been in the network ID map");
|
|
||||||
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(EntityId, EntityMut) -> bool) {
|
|
||||||
self.0.sm.retain(|k, v| {
|
|
||||||
if f(EntityId(k), EntityMut(v)) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
self.0
|
|
||||||
.uuid_to_entity
|
|
||||||
.remove(&v.uuid)
|
|
||||||
.expect("UUID should have been in UUID map");
|
|
||||||
|
|
||||||
self.0
|
|
||||||
.network_id_to_entity
|
|
||||||
.remove(&k.version())
|
|
||||||
.expect("network ID should have been in the network ID map");
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self, entity: EntityId) -> Option<EntityMut> {
|
|
||||||
self.0.sm.get_mut(entity.0).map(EntityMut)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (EntityId, EntityMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.sm
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(k, v)| (EntityId(k), EntityMut(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (EntityId, EntityMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.sm
|
|
||||||
.par_iter_mut()
|
|
||||||
.map(|(k, v)| (EntityId(k), EntityMut(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.0.old_position = e.new_position;
|
e.old_position = e.new_position;
|
||||||
e.0.meta.clear_modifications();
|
e.meta.clear_modifications();
|
||||||
|
|
||||||
let on_ground = e.0.flags.on_ground();
|
let on_ground = e.flags.on_ground();
|
||||||
e.0.flags = EntityFlags(0);
|
e.flags = EntityFlags(0);
|
||||||
e.0.flags.set_on_ground(on_ground);
|
e.flags.set_on_ground(on_ground);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,16 +185,6 @@ pub struct Entity {
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EntityMut<'a>(&'a mut Entity);
|
|
||||||
|
|
||||||
impl<'a> Deref for EntityMut<'a> {
|
|
||||||
type Target = Entity;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains a bit for certain fields in [`Entity`] to track if they have been
|
/// Contains a bit for certain fields in [`Entity`] to track if they have been
|
||||||
/// modified.
|
/// modified.
|
||||||
#[bitfield(u8)]
|
#[bitfield(u8)]
|
||||||
|
@ -249,16 +209,35 @@ impl Entity {
|
||||||
&self.meta
|
&self.meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to this entity's [`EntityMeta`].
|
||||||
|
pub fn meta_mut(&mut self) -> &mut EntityMeta {
|
||||||
|
&mut self.meta
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`EntityType`] of this entity.
|
/// Returns the [`EntityType`] of this entity.
|
||||||
pub fn typ(&self) -> EntityType {
|
pub fn typ(&self) -> EntityType {
|
||||||
self.meta.typ()
|
self.meta.typ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changes the [`EntityType`] of this entity to the provided type.
|
||||||
|
///
|
||||||
|
/// All metadata of this entity is reset to the default values.
|
||||||
|
pub fn set_type(&mut self, typ: EntityType) {
|
||||||
|
self.meta = EntityMeta::new(typ);
|
||||||
|
// All metadata is lost so we must mark it as modified unconditionally.
|
||||||
|
self.flags.set_type_modified(true);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the position of this entity in the world it inhabits.
|
/// Returns the position of this entity in the world it inhabits.
|
||||||
pub fn position(&self) -> Vec3<f64> {
|
pub fn position(&self) -> Vec3<f64> {
|
||||||
self.new_position
|
self.new_position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the position of this entity in the world it inhabits.
|
||||||
|
pub fn set_position(&mut self, pos: impl Into<Vec3<f64>>) {
|
||||||
|
self.new_position = pos.into();
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the position of this entity as it existed at the end of the
|
/// Returns the position of this entity as it existed at the end of the
|
||||||
/// previous tick.
|
/// previous tick.
|
||||||
pub fn old_position(&self) -> Vec3<f64> {
|
pub fn old_position(&self) -> Vec3<f64> {
|
||||||
|
@ -270,25 +249,62 @@ impl Entity {
|
||||||
self.yaw
|
self.yaw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the yaw of this entity (in degrees).
|
||||||
|
pub fn set_yaw(&mut self, yaw: f32) {
|
||||||
|
if self.yaw != yaw {
|
||||||
|
self.yaw = yaw;
|
||||||
|
self.flags.set_yaw_or_pitch_modified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the pitch of this entity (in degrees).
|
/// Gets the pitch of this entity (in degrees).
|
||||||
pub fn pitch(&self) -> f32 {
|
pub fn pitch(&self) -> f32 {
|
||||||
self.pitch
|
self.pitch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the pitch of this entity (in degrees).
|
||||||
|
pub fn set_pitch(&mut self, pitch: f32) {
|
||||||
|
if self.pitch != pitch {
|
||||||
|
self.pitch = pitch;
|
||||||
|
self.flags.set_yaw_or_pitch_modified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the head yaw of this entity (in degrees).
|
/// Gets the head yaw of this entity (in degrees).
|
||||||
pub fn head_yaw(&self) -> f32 {
|
pub fn head_yaw(&self) -> f32 {
|
||||||
self.head_yaw
|
self.head_yaw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the head yaw of this entity (in degrees).
|
||||||
|
pub fn set_head_yaw(&mut self, head_yaw: f32) {
|
||||||
|
if self.head_yaw != head_yaw {
|
||||||
|
self.head_yaw = head_yaw;
|
||||||
|
self.flags.set_head_yaw_modified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the velocity of this entity in meters per second.
|
/// Gets the velocity of this entity in meters per second.
|
||||||
pub fn velocity(&self) -> Vec3<f32> {
|
pub fn velocity(&self) -> Vec3<f32> {
|
||||||
self.velocity
|
self.velocity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
|
||||||
|
let new_vel = velocity.into();
|
||||||
|
|
||||||
|
if self.velocity != new_vel {
|
||||||
|
self.velocity = new_vel;
|
||||||
|
self.flags.set_velocity_modified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_ground(&self) -> bool {
|
pub fn on_ground(&self) -> bool {
|
||||||
self.flags.on_ground()
|
self.flags.on_ground()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_on_ground(&mut self, on_ground: bool) {
|
||||||
|
self.flags.set_on_ground(on_ground);
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the metadata packet to send to clients after this entity has been
|
/// Gets the metadata packet to send to clients after this entity has been
|
||||||
/// spawned.
|
/// spawned.
|
||||||
///
|
///
|
||||||
|
@ -490,69 +506,6 @@ pub(crate) fn velocity_to_packet_units(vel: Vec3<f32>) -> Vec3<i16> {
|
||||||
(vel * 400.0).as_()
|
(vel * 400.0).as_()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EntityMut<'a> {
|
|
||||||
// TODO: exposing &mut EntityMeta is unsound?
|
|
||||||
/// Returns a mutable reference to this entity's [`EntityMeta`].
|
|
||||||
///
|
|
||||||
/// **NOTE:** Never call [`std::mem::swap`] on the returned reference or any
|
|
||||||
/// part of `EntityMeta` as this would break invariants within the
|
|
||||||
/// library.
|
|
||||||
pub fn meta_mut(&mut self) -> &mut EntityMeta {
|
|
||||||
&mut self.0.meta
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes the [`EntityType`] of this entity to the provided type.
|
|
||||||
///
|
|
||||||
/// All metadata of this entity is reset to the default values.
|
|
||||||
pub fn set_type(&mut self, typ: EntityType) {
|
|
||||||
self.0.meta = EntityMeta::new(typ);
|
|
||||||
// All metadata is lost so we must mark it as modified unconditionally.
|
|
||||||
self.0.flags.set_type_modified(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the position of this entity in the world it inhabits.
|
|
||||||
pub fn set_position(&mut self, pos: impl Into<Vec3<f64>>) {
|
|
||||||
self.0.new_position = pos.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the yaw of this entity (in degrees).
|
|
||||||
pub fn set_yaw(&mut self, yaw: f32) {
|
|
||||||
if self.0.yaw != yaw {
|
|
||||||
self.0.yaw = yaw;
|
|
||||||
self.0.flags.set_yaw_or_pitch_modified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the pitch of this entity (in degrees).
|
|
||||||
pub fn set_pitch(&mut self, pitch: f32) {
|
|
||||||
if self.0.pitch != pitch {
|
|
||||||
self.0.pitch = pitch;
|
|
||||||
self.0.flags.set_yaw_or_pitch_modified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the head yaw of this entity (in degrees).
|
|
||||||
pub fn set_head_yaw(&mut self, head_yaw: f32) {
|
|
||||||
if self.0.head_yaw != head_yaw {
|
|
||||||
self.0.head_yaw = head_yaw;
|
|
||||||
self.0.flags.set_head_yaw_modified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
|
|
||||||
let new_vel = velocity.into();
|
|
||||||
|
|
||||||
if self.0.velocity != new_vel {
|
|
||||||
self.0.velocity = new_vel;
|
|
||||||
self.0.flags.set_velocity_modified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_on_ground(&mut self, on_ground: bool) {
|
|
||||||
self.0.flags.set_on_ground(on_ground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) enum EntitySpawnPacket {
|
pub(crate) enum EntitySpawnPacket {
|
||||||
SpawnEntity(SpawnEntity),
|
SpawnEntity(SpawnEntity),
|
||||||
SpawnExperienceOrb(SpawnExperienceOrb),
|
SpawnExperienceOrb(SpawnExperienceOrb),
|
||||||
|
@ -568,5 +521,3 @@ impl From<EntitySpawnPacket> for S2cPlayPacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use types::{EntityMeta, EntityType};
|
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -22,6 +22,7 @@ pub mod entity;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
mod packets;
|
mod packets;
|
||||||
mod player_list;
|
mod player_list;
|
||||||
|
pub mod player_textures;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
mod slotmap;
|
mod slotmap;
|
||||||
|
@ -31,24 +32,23 @@ pub mod util;
|
||||||
mod var_int;
|
mod var_int;
|
||||||
mod var_long;
|
mod var_long;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
pub mod player_textures;
|
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
pub use biome::{Biome, BiomeId};
|
pub use biome::{Biome, BiomeId};
|
||||||
pub use block::BlockState;
|
pub use block::BlockState;
|
||||||
pub use block_pos::BlockPos;
|
pub use block_pos::BlockPos;
|
||||||
pub use chunk::{Chunk, Chunks, ChunksMut};
|
pub use chunk::{Chunk, Chunks};
|
||||||
pub use chunk_pos::ChunkPos;
|
pub use chunk_pos::ChunkPos;
|
||||||
pub use client::{Client, ClientMut, Clients, ClientsMut};
|
pub use client::{Client, Clients};
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub use dimension::{Dimension, DimensionId};
|
pub use dimension::{Dimension, DimensionId};
|
||||||
pub use entity::{Entities, EntitiesMut, Entity, EntityId, EntityType};
|
pub use entity::{Entities, Entity, EntityId, EntityType};
|
||||||
pub use ident::Ident;
|
pub use ident::Ident;
|
||||||
pub use server::{start_server, NewClientData, Server, ShutdownResult};
|
pub use server::{start_server, NewClientData, Server, ShutdownResult};
|
||||||
pub use spatial_index::{SpatialIndex, SpatialIndexMut};
|
pub use spatial_index::SpatialIndex;
|
||||||
pub use text::{Text, TextFormat};
|
pub use text::{Text, TextFormat};
|
||||||
pub use uuid::Uuid;
|
pub use uuid::Uuid;
|
||||||
pub use world::{WorldId, WorldMeta, WorldMetaMut, WorldMut, WorldRef, Worlds, WorldsMut};
|
pub use world::{WorldId, WorldMeta, Worlds};
|
||||||
pub use {nbt, uuid, vek};
|
pub use {nbt, uuid, vek};
|
||||||
|
|
||||||
/// The Minecraft protocol version that this library targets.
|
/// The Minecraft protocol version that this library targets.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -33,18 +32,91 @@ impl PlayerList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
uuid: Uuid,
|
||||||
|
username: impl Into<String>,
|
||||||
|
textures: Option<SignedPlayerTextures>,
|
||||||
|
game_mode: GameMode,
|
||||||
|
ping: i32,
|
||||||
|
display_name: impl Into<Option<Text>>,
|
||||||
|
) {
|
||||||
|
match self.entries.entry(uuid) {
|
||||||
|
Entry::Occupied(mut oe) => {
|
||||||
|
let e = oe.get_mut();
|
||||||
|
let username = username.into();
|
||||||
|
|
||||||
|
if e.username() != username || e.textures != textures {
|
||||||
|
self.removed.insert(*oe.key());
|
||||||
|
|
||||||
|
oe.insert(PlayerListEntry {
|
||||||
|
username,
|
||||||
|
textures,
|
||||||
|
game_mode,
|
||||||
|
ping,
|
||||||
|
display_name: display_name.into(),
|
||||||
|
flags: EntryFlags::new().with_created_this_tick(true),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
e.set_game_mode(game_mode);
|
||||||
|
e.set_ping(ping);
|
||||||
|
e.set_display_name(display_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(ve) => {
|
||||||
|
ve.insert(PlayerListEntry {
|
||||||
|
username: username.into(),
|
||||||
|
textures,
|
||||||
|
game_mode,
|
||||||
|
ping,
|
||||||
|
display_name: display_name.into(),
|
||||||
|
flags: EntryFlags::new().with_created_this_tick(true),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, uuid: Uuid) -> bool {
|
||||||
|
if self.entries.remove(&uuid).is_some() {
|
||||||
|
self.removed.insert(uuid);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn header(&self) -> &Text {
|
pub fn header(&self) -> &Text {
|
||||||
&self.header
|
&self.header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_header(&mut self, header: impl Into<Text>) {
|
||||||
|
let header = header.into();
|
||||||
|
if self.header != header {
|
||||||
|
self.header = header;
|
||||||
|
self.modified_header_or_footer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn footer(&self) -> &Text {
|
pub fn footer(&self) -> &Text {
|
||||||
&self.footer
|
&self.footer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_footer(&mut self, footer: impl Into<Text>) {
|
||||||
|
let footer = footer.into();
|
||||||
|
if self.footer != footer {
|
||||||
|
self.footer = footer;
|
||||||
|
self.modified_header_or_footer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn entries(&self) -> impl Iterator<Item = (Uuid, &PlayerListEntry)> + '_ {
|
pub fn entries(&self) -> impl Iterator<Item = (Uuid, &PlayerListEntry)> + '_ {
|
||||||
self.entries.iter().map(|(k, v)| (*k, v))
|
self.entries.iter().map(|(k, v)| (*k, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Uuid, &mut PlayerListEntry)> + '_ {
|
||||||
|
self.entries.iter_mut().map(|(k, v)| (*k, v))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn initial_packets(&self, mut packet: impl FnMut(S2cPlayPacket)) {
|
pub(crate) fn initial_packets(&self, mut packet: impl FnMut(S2cPlayPacket)) {
|
||||||
let add_player: Vec<_> = self
|
let add_player: Vec<_> = self
|
||||||
.entries
|
.entries
|
||||||
|
@ -158,105 +230,13 @@ impl PlayerList {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PlayerListMut<'a>(pub(crate) &'a mut PlayerList);
|
|
||||||
|
|
||||||
impl<'a> Deref for PlayerListMut<'a> {
|
|
||||||
type Target = PlayerList;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PlayerListMut<'a> {
|
|
||||||
pub fn reborrow(&mut self) -> PlayerListMut {
|
|
||||||
PlayerListMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(
|
|
||||||
&mut self,
|
|
||||||
uuid: Uuid,
|
|
||||||
username: impl Into<String>,
|
|
||||||
textures: Option<SignedPlayerTextures>,
|
|
||||||
game_mode: GameMode,
|
|
||||||
ping: i32,
|
|
||||||
display_name: impl Into<Option<Text>>,
|
|
||||||
) {
|
|
||||||
match self.0.entries.entry(uuid) {
|
|
||||||
Entry::Occupied(mut oe) => {
|
|
||||||
let mut e = PlayerListEntryMut(oe.get_mut());
|
|
||||||
let username = username.into();
|
|
||||||
|
|
||||||
if e.username() != username || e.textures != textures {
|
|
||||||
self.0.removed.insert(*oe.key());
|
|
||||||
|
|
||||||
oe.insert(PlayerListEntry {
|
|
||||||
username,
|
|
||||||
textures,
|
|
||||||
game_mode,
|
|
||||||
ping,
|
|
||||||
display_name: display_name.into(),
|
|
||||||
flags: EntryFlags::new().with_created_this_tick(true),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
e.set_game_mode(game_mode);
|
|
||||||
e.set_ping(ping);
|
|
||||||
e.set_display_name(display_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Entry::Vacant(ve) => {
|
|
||||||
ve.insert(PlayerListEntry {
|
|
||||||
username: username.into(),
|
|
||||||
textures,
|
|
||||||
game_mode,
|
|
||||||
ping,
|
|
||||||
display_name: display_name.into(),
|
|
||||||
flags: EntryFlags::new().with_created_this_tick(true),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, uuid: Uuid) -> bool {
|
|
||||||
if self.0.entries.remove(&uuid).is_some() {
|
|
||||||
self.0.removed.insert(uuid);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_header(&mut self, header: impl Into<Text>) {
|
|
||||||
let header = header.into();
|
|
||||||
if self.0.header != header {
|
|
||||||
self.0.header = header;
|
|
||||||
self.0.modified_header_or_footer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_footer(&mut self, footer: impl Into<Text>) {
|
|
||||||
let footer = footer.into();
|
|
||||||
if self.0.footer != footer {
|
|
||||||
self.0.footer = footer;
|
|
||||||
self.0.modified_header_or_footer = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Uuid, PlayerListEntryMut)> + '_ {
|
|
||||||
self.0
|
|
||||||
.entries
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(k, v)| (*k, PlayerListEntryMut(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn update(&mut self) {
|
pub(crate) fn update(&mut self) {
|
||||||
for e in self.0.entries.values_mut() {
|
for e in self.entries.values_mut() {
|
||||||
e.flags = EntryFlags(0);
|
e.flags = EntryFlags(0);
|
||||||
}
|
}
|
||||||
self.0.removed.clear();
|
self.removed.clear();
|
||||||
self.0.modified_header_or_footer = false;
|
self.modified_header_or_footer = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,49 +262,33 @@ impl PlayerListEntry {
|
||||||
self.game_mode
|
self.game_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
||||||
|
if self.game_mode != game_mode {
|
||||||
|
self.game_mode = game_mode;
|
||||||
|
self.flags.set_modified_game_mode(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ping(&self) -> i32 {
|
pub fn ping(&self) -> i32 {
|
||||||
self.ping
|
self.ping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_ping(&mut self, ping: i32) {
|
||||||
|
if self.ping != ping {
|
||||||
|
self.ping = ping;
|
||||||
|
self.flags.set_modified_ping(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn display_name(&self) -> Option<&Text> {
|
pub fn display_name(&self) -> Option<&Text> {
|
||||||
self.display_name.as_ref()
|
self.display_name.as_ref()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PlayerListEntryMut<'a>(&'a mut PlayerListEntry);
|
|
||||||
|
|
||||||
impl<'a> Deref for PlayerListEntryMut<'a> {
|
|
||||||
type Target = PlayerListEntry;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PlayerListEntryMut<'a> {
|
|
||||||
pub fn reborrow(&mut self) -> PlayerListEntryMut {
|
|
||||||
PlayerListEntryMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
|
||||||
if self.0.game_mode != game_mode {
|
|
||||||
self.0.game_mode = game_mode;
|
|
||||||
self.0.flags.set_modified_game_mode(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_ping(&mut self, ping: i32) {
|
|
||||||
if self.0.ping != ping {
|
|
||||||
self.0.ping = ping;
|
|
||||||
self.0.flags.set_modified_ping(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_display_name(&mut self, display_name: impl Into<Option<Text>>) {
|
pub fn set_display_name(&mut self, display_name: impl Into<Option<Text>>) {
|
||||||
let display_name = display_name.into();
|
let display_name = display_name.into();
|
||||||
if self.0.display_name != display_name {
|
if self.display_name != display_name {
|
||||||
self.0.display_name = display_name;
|
self.display_name = display_name;
|
||||||
self.0.flags.set_modified_display_name(true);
|
self.flags.set_modified_display_name(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,7 @@ use crate::util::valid_username;
|
||||||
use crate::var_int::VarInt;
|
use crate::var_int::VarInt;
|
||||||
use crate::world::Worlds;
|
use crate::world::Worlds;
|
||||||
use crate::{
|
use crate::{
|
||||||
Biome, BiomeId, Client, ClientMut, Dimension, DimensionId, Ticks, WorldsMut, PROTOCOL_VERSION,
|
Biome, BiomeId, Client, Dimension, DimensionId, Ticks, PROTOCOL_VERSION, VERSION_NAME,
|
||||||
VERSION_NAME,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A handle to a running Minecraft server containing state which is accessible
|
/// A handle to a running Minecraft server containing state which is accessible
|
||||||
|
@ -215,13 +214,12 @@ pub fn start_server(config: impl Config) -> ShutdownResult {
|
||||||
let _guard = server.tokio_handle().enter();
|
let _guard = server.tokio_handle().enter();
|
||||||
|
|
||||||
let mut worlds = Worlds::new(server.clone());
|
let mut worlds = Worlds::new(server.clone());
|
||||||
let mut worlds_mut = WorldsMut::new(&mut worlds);
|
|
||||||
|
|
||||||
server.config().init(&server, worlds_mut.reborrow());
|
server.config().init(&server, &mut worlds);
|
||||||
|
|
||||||
tokio::spawn(do_accept_loop(server.clone()));
|
tokio::spawn(do_accept_loop(server.clone()));
|
||||||
|
|
||||||
do_update_loop(server, worlds_mut)
|
do_update_loop(server, &mut worlds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_server(cfg: impl Config) -> anyhow::Result<Server> {
|
fn setup_server(cfg: impl Config) -> anyhow::Result<Server> {
|
||||||
|
@ -350,7 +348,7 @@ fn setup_server(cfg: impl Config) -> anyhow::Result<Server> {
|
||||||
Ok(Server(Arc::new(server)))
|
Ok(Server(Arc::new(server)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
fn do_update_loop(server: Server, worlds: &mut Worlds) -> ShutdownResult {
|
||||||
let mut tick_start = Instant::now();
|
let mut tick_start = Instant::now();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -359,20 +357,20 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(msg) = server.0.new_clients_rx.try_recv() {
|
while let Ok(msg) = server.0.new_clients_rx.try_recv() {
|
||||||
join_player(&server, worlds.reborrow(), msg);
|
join_player(&server, worlds, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get serverbound packets first so they are not dealt with a tick late.
|
// Get serverbound packets first so they are not dealt with a tick late.
|
||||||
worlds.par_iter_mut().for_each(|(_, mut world)| {
|
worlds.par_iter_mut().for_each(|(_, world)| {
|
||||||
world.clients.par_iter_mut().for_each(|(_, mut client)| {
|
world.clients.par_iter_mut().for_each(|(_, client)| {
|
||||||
client.handle_serverbound_packets(&world.entities);
|
client.handle_serverbound_packets(&world.entities);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.config().update(&server, worlds.reborrow());
|
server.config().update(&server, worlds);
|
||||||
|
|
||||||
worlds.par_iter_mut().for_each(|(_, mut world)| {
|
worlds.par_iter_mut().for_each(|(_, world)| {
|
||||||
world.chunks.par_iter_mut().for_each(|(_, mut chunk)| {
|
world.chunks.par_iter_mut().for_each(|(_, chunk)| {
|
||||||
if chunk.created_tick() == server.current_tick() {
|
if chunk.created_tick() == server.current_tick() {
|
||||||
// Chunks created this tick can have their changes applied immediately because
|
// Chunks created this tick can have their changes applied immediately because
|
||||||
// they have not been observed by clients yet. Clients will not have to be sent
|
// they have not been observed by clients yet. Clients will not have to be sent
|
||||||
|
@ -381,9 +379,9 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
world.spatial_index.update(world.entities.reborrow());
|
world.spatial_index.update(&world.entities);
|
||||||
|
|
||||||
world.clients.par_iter_mut().for_each(|(_, mut client)| {
|
world.clients.par_iter_mut().for_each(|(_, client)| {
|
||||||
client.update(
|
client.update(
|
||||||
&server,
|
&server,
|
||||||
&world.entities,
|
&world.entities,
|
||||||
|
@ -395,7 +393,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
||||||
|
|
||||||
world.entities.update();
|
world.entities.update();
|
||||||
|
|
||||||
world.chunks.par_iter_mut().for_each(|(_, mut chunk)| {
|
world.chunks.par_iter_mut().for_each(|(_, chunk)| {
|
||||||
chunk.apply_modifications();
|
chunk.apply_modifications();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -411,7 +409,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) {
|
fn join_player(server: &Server, worlds: &mut Worlds, msg: NewClientMessage) {
|
||||||
let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity);
|
let (clientbound_tx, clientbound_rx) = flume::bounded(server.0.outgoing_packet_capacity);
|
||||||
let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity);
|
let (serverbound_tx, serverbound_rx) = flume::bounded(server.0.incoming_packet_capacity);
|
||||||
|
|
||||||
|
@ -421,15 +419,10 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) {
|
||||||
let _ = msg.reply.send(s2c_packet_channels);
|
let _ = msg.reply.send(s2c_packet_channels);
|
||||||
|
|
||||||
let mut client = Client::new(c2s_packet_channels, server, msg.ncd);
|
let mut client = Client::new(c2s_packet_channels, server, msg.ncd);
|
||||||
let mut client_mut = ClientMut::new(&mut client);
|
|
||||||
|
|
||||||
match server
|
match server.0.cfg.join(server, &mut client, worlds) {
|
||||||
.0
|
|
||||||
.cfg
|
|
||||||
.join(server, client_mut.reborrow(), worlds.reborrow())
|
|
||||||
{
|
|
||||||
Ok(world_id) => {
|
Ok(world_id) => {
|
||||||
if let Some(mut world) = worlds.get_mut(world_id) {
|
if let Some(world) = worlds.get_mut(world_id) {
|
||||||
if world.entities.get_with_uuid(client.uuid()).is_none() {
|
if world.entities.get_with_uuid(client.uuid()).is_none() {
|
||||||
world.clients.create(client);
|
world.clients.create(client);
|
||||||
} else {
|
} else {
|
||||||
|
@ -448,7 +441,7 @@ fn join_player(server: &Server, mut worlds: WorldsMut, msg: NewClientMessage) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errmsg) => client_mut.disconnect(errmsg),
|
Err(errmsg) => client.disconnect(errmsg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,13 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
|
||||||
use crate::bvh::Bvh;
|
use crate::bvh::Bvh;
|
||||||
pub use crate::bvh::TraverseStep;
|
pub use crate::bvh::TraverseStep;
|
||||||
use crate::{EntitiesMut, EntityId};
|
use crate::{Entities, EntityId};
|
||||||
|
|
||||||
pub struct SpatialIndex {
|
pub struct SpatialIndex {
|
||||||
bvh: Bvh<EntityId>,
|
bvh: Bvh<EntityId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpatialIndexMut<'a>(&'a mut SpatialIndex);
|
|
||||||
|
|
||||||
impl<'a> Deref for SpatialIndexMut<'a> {
|
|
||||||
type Target = SpatialIndex;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpatialIndex {
|
impl SpatialIndex {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { bvh: Bvh::new() }
|
Self { bvh: Bvh::new() }
|
||||||
|
@ -107,16 +95,9 @@ impl SpatialIndex {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SpatialIndexMut<'a> {
|
pub(crate) fn update(&mut self, entities: &Entities) {
|
||||||
pub(crate) fn new(si: &'a mut SpatialIndex) -> Self {
|
self.bvh
|
||||||
Self(si)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn update(&mut self, entities: EntitiesMut) {
|
|
||||||
self.0
|
|
||||||
.bvh
|
|
||||||
.build(entities.iter().map(|(id, e)| (id, e.hitbox())))
|
.build(entities.iter().map(|(id, e)| (id, e.hitbox())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
188
src/world.rs
188
src/world.rs
|
@ -1,30 +1,16 @@
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
|
||||||
use crate::player_list::{PlayerList, PlayerListMut};
|
use crate::player_list::PlayerList;
|
||||||
use crate::slotmap::{Key, SlotMap};
|
use crate::slotmap::{Key, SlotMap};
|
||||||
use crate::{
|
use crate::{Chunks, Clients, DimensionId, Entities, Server, SpatialIndex};
|
||||||
Chunks, ChunksMut, Clients, ClientsMut, DimensionId, Entities, EntitiesMut, Server,
|
|
||||||
SpatialIndex, SpatialIndexMut,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Worlds {
|
pub struct Worlds {
|
||||||
sm: SlotMap<World>,
|
sm: SlotMap<World>,
|
||||||
server: Server,
|
server: Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorldsMut<'a>(&'a mut Worlds);
|
|
||||||
|
|
||||||
impl<'a> Deref for WorldsMut<'a> {
|
|
||||||
type Target = Worlds;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub struct WorldId(Key);
|
pub struct WorldId(Key);
|
||||||
|
|
||||||
|
@ -36,30 +22,8 @@ impl Worlds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count(&self) -> usize {
|
pub fn create(&mut self, dim: DimensionId) -> (WorldId, &mut World) {
|
||||||
self.sm.len()
|
let (id, world) = self.sm.insert(World {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, world: WorldId) -> Option<WorldRef> {
|
|
||||||
self.sm.get(world.0).map(WorldRef::new)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl FusedIterator<Item = (WorldId, WorldRef)> + Clone + '_ {
|
|
||||||
self.sm.iter().map(|(k, v)| (WorldId(k), WorldRef::new(v)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> WorldsMut<'a> {
|
|
||||||
pub(crate) fn new(worlds: &'a mut Worlds) -> Self {
|
|
||||||
Self(worlds)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reborrow(&mut self) -> WorldsMut {
|
|
||||||
WorldsMut(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(&mut self, dim: DimensionId) -> (WorldId, WorldMut) {
|
|
||||||
let (id, world) = self.0.sm.insert(World {
|
|
||||||
clients: Clients::new(),
|
clients: Clients::new(),
|
||||||
entities: Entities::new(),
|
entities: Entities::new(),
|
||||||
spatial_index: SpatialIndex::new(),
|
spatial_index: SpatialIndex::new(),
|
||||||
|
@ -71,7 +35,7 @@ impl<'a> WorldsMut<'a> {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
(WorldId(id), WorldMut::new(world))
|
(WorldId(id), world)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a world from the server. Any [`WorldId`] referring to the
|
/// Deletes a world from the server. Any [`WorldId`] referring to the
|
||||||
|
@ -80,106 +44,48 @@ impl<'a> WorldsMut<'a> {
|
||||||
/// Note that any entities with positions inside the deleted world will not
|
/// Note that any entities with positions inside the deleted world will not
|
||||||
/// be deleted themselves.
|
/// be deleted themselves.
|
||||||
pub fn delete(&mut self, world: WorldId) -> bool {
|
pub fn delete(&mut self, world: WorldId) -> bool {
|
||||||
self.0.sm.remove(world.0).is_some()
|
self.sm.remove(world.0).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retain(&mut self, mut f: impl FnMut(WorldId, WorldMut) -> bool) {
|
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World) -> bool) {
|
||||||
self.0.sm.retain(|k, v| f(WorldId(k), WorldMut::new(v)))
|
self.sm.retain(|k, v| f(WorldId(k), v))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, world: WorldId) -> Option<WorldMut> {
|
pub fn count(&self) -> usize {
|
||||||
self.0.sm.get_mut(world.0).map(WorldMut::new)
|
self.sm.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (WorldId, WorldMut)> + '_ {
|
pub fn get(&self, world: WorldId) -> Option<&World> {
|
||||||
self.0
|
self.sm.get(world.0)
|
||||||
.sm
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(k, v)| (WorldId(k), WorldMut::new(v)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, WorldRef)> + Clone + '_ {
|
pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World> {
|
||||||
self.0
|
self.sm.get_mut(world.0)
|
||||||
.sm
|
|
||||||
.par_iter()
|
|
||||||
.map(|(k, v)| (WorldId(k), WorldRef::new(v)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (WorldId, WorldMut)> + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (WorldId, &World)> + Clone + '_ {
|
||||||
self.0
|
self.sm.iter().map(|(k, v)| (WorldId(k), v))
|
||||||
.sm
|
}
|
||||||
.par_iter_mut()
|
|
||||||
.map(|(k, v)| (WorldId(k), WorldMut::new(v)))
|
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (WorldId, &mut World)> + '_ {
|
||||||
|
self.sm.iter_mut().map(|(k, v)| (WorldId(k), v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, &World)> + Clone + '_ {
|
||||||
|
self.sm.par_iter().map(|(k, v)| (WorldId(k), v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (WorldId, &mut World)> + '_ {
|
||||||
|
self.sm.par_iter_mut().map(|(k, v)| (WorldId(k), v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A world on the server is a space for chunks, entities, and clients to
|
pub struct World {
|
||||||
/// inhabit.
|
pub clients: Clients,
|
||||||
///
|
pub entities: Entities,
|
||||||
/// Worlds maintain a collection of chunks and entities that are a part of it.
|
pub spatial_index: SpatialIndex,
|
||||||
/// For a chunk or entity to appear, it must be inserted into the world. Chunks
|
pub chunks: Chunks,
|
||||||
/// and entities can be in multiple worlds at the same time.
|
pub meta: WorldMeta,
|
||||||
///
|
|
||||||
/// Deleted chunks and entities are automatically removed from worlds at the end
|
|
||||||
/// of each tick.
|
|
||||||
pub(crate) struct World {
|
|
||||||
clients: Clients,
|
|
||||||
entities: Entities,
|
|
||||||
spatial_index: SpatialIndex,
|
|
||||||
chunks: Chunks,
|
|
||||||
meta: WorldMeta,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A bag of immutable references to the components of a world.
|
|
||||||
pub struct WorldRef<'a> {
|
|
||||||
pub clients: &'a Clients,
|
|
||||||
pub entities: &'a Entities,
|
|
||||||
pub spatial_index: &'a SpatialIndex,
|
|
||||||
pub chunks: &'a Chunks,
|
|
||||||
pub meta: &'a WorldMeta,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> WorldRef<'a> {
|
|
||||||
pub(crate) fn new(w: &'a World) -> Self {
|
|
||||||
Self {
|
|
||||||
clients: &w.clients,
|
|
||||||
entities: &w.entities,
|
|
||||||
spatial_index: &w.spatial_index,
|
|
||||||
chunks: &w.chunks,
|
|
||||||
meta: &w.meta,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A bag of mutable references to the components of a world.
|
|
||||||
pub struct WorldMut<'a> {
|
|
||||||
pub clients: ClientsMut<'a>,
|
|
||||||
pub entities: EntitiesMut<'a>,
|
|
||||||
pub spatial_index: SpatialIndexMut<'a>,
|
|
||||||
pub chunks: ChunksMut<'a>,
|
|
||||||
pub meta: WorldMetaMut<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> WorldMut<'a> {
|
|
||||||
pub(crate) fn new(w: &'a mut World) -> Self {
|
|
||||||
WorldMut {
|
|
||||||
clients: ClientsMut::new(&mut w.clients),
|
|
||||||
entities: EntitiesMut::new(&mut w.entities),
|
|
||||||
spatial_index: SpatialIndexMut::new(&mut w.spatial_index),
|
|
||||||
chunks: ChunksMut::new(&mut w.chunks),
|
|
||||||
meta: WorldMetaMut(&mut w.meta),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn immutable(&'a self) -> WorldRef<'a> {
|
|
||||||
WorldRef {
|
|
||||||
clients: &self.clients,
|
|
||||||
entities: &self.entities,
|
|
||||||
spatial_index: &self.spatial_index,
|
|
||||||
chunks: &self.chunks,
|
|
||||||
meta: &self.meta,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WorldMeta {
|
pub struct WorldMeta {
|
||||||
|
@ -198,31 +104,19 @@ impl WorldMeta {
|
||||||
self.is_flat
|
self.is_flat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_flat(&mut self, flat: bool) {
|
||||||
|
self.is_flat = flat;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn player_list(&self) -> &PlayerList {
|
pub fn player_list(&self) -> &PlayerList {
|
||||||
&self.player_list
|
&self.player_list
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WorldMetaMut<'a>(&'a mut WorldMeta);
|
pub fn player_list_mut(&mut self) -> &mut PlayerList {
|
||||||
|
&mut self.player_list
|
||||||
impl<'a> Deref for WorldMetaMut<'a> {
|
|
||||||
type Target = WorldMeta;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> WorldMetaMut<'a> {
|
|
||||||
pub fn set_flat(&mut self, flat: bool) {
|
|
||||||
self.0.is_flat = flat;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn player_list_mut(&mut self) -> PlayerListMut {
|
|
||||||
PlayerListMut(&mut self.0.player_list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&mut self) {
|
pub(crate) fn update(&mut self) {
|
||||||
self.player_list_mut().update();
|
self.player_list.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue