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:
Ryan 2022-06-29 11:09:00 -07:00
parent e97df76a75
commit 806ffa4f42
11 changed files with 545 additions and 864 deletions

View file

@ -9,8 +9,8 @@ use valence::config::{Config, ServerListPing};
use valence::text::Color;
use valence::util::to_yaw_and_pitch;
use valence::{
async_trait, ClientMut, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text,
TextFormat, WorldId, WorldsMut,
async_trait, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text,
TextFormat, WorldId, Worlds, Client,
};
use vek::{Mat3, Vec3};
@ -57,8 +57,8 @@ impl Config for Game {
fn join(
&self,
_server: &Server,
_client: ClientMut,
worlds: WorldsMut,
_client: &mut Client,
worlds: &mut Worlds,
) -> Result<WorldId, Text> {
if let Ok(_) = self
.player_count
@ -72,8 +72,8 @@ impl Config for Game {
}
}
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
let mut world = worlds.create(DimensionId::default()).1;
fn init(&self, _server: &Server, worlds: &mut Worlds) {
let world = worlds.create(DimensionId::default()).1;
world.meta.set_flat(true);
let size = 5;
@ -84,16 +84,16 @@ impl Config for Game {
}
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);
id
}));
}
fn update(&self, server: &Server, mut worlds: WorldsMut) {
let mut world = worlds.iter_mut().next().unwrap().1;
fn update(&self, server: &Server, worlds: &mut Worlds) {
let world = worlds.iter_mut().next().unwrap().1;
world.clients.retain(|_, mut client| {
world.clients.retain(|_, client| {
if client.created_tick() == server.current_tick() {
client.set_game_mode(GameMode::Creative);
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
@ -136,7 +136,7 @@ impl Config for Game {
.unwrap_or_default();
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 transformed = rotated * radius + [0.0, 100.0, 0.0];

View file

@ -11,8 +11,8 @@ use valence::config::{Config, ServerListPing};
use valence::text::Color;
use valence::util::chunks_in_view_distance;
use valence::{
async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, TextFormat,
WorldId, WorldsMut,
async_trait, ChunkPos, Client, DimensionId, Server, ShutdownResult, Text, TextFormat, WorldId,
Worlds,
};
use vek::Lerp;
@ -69,8 +69,8 @@ impl Config for Game {
fn join(
&self,
_server: &Server,
_client: ClientMut,
worlds: WorldsMut,
_client: &mut Client,
worlds: &mut Worlds,
) -> Result<WorldId, Text> {
if let Ok(_) = self
.player_count
@ -84,17 +84,17 @@ impl Config for Game {
}
}
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
let (_, mut world) = worlds.create(DimensionId::default());
fn init(&self, _server: &Server, worlds: &mut Worlds) {
let (_, world) = worlds.create(DimensionId::default());
world.meta.set_flat(true);
}
fn update(&self, server: &Server, mut worlds: WorldsMut) {
let mut world = worlds.iter_mut().next().unwrap().1;
fn update(&self, server: &Server, worlds: &mut Worlds) {
let world = worlds.iter_mut().next().unwrap().1;
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() {
self.player_count.fetch_sub(1, Ordering::SeqCst);
return false;
@ -132,7 +132,7 @@ impl Config for Game {
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() {
for z in 0..16 {
for x in 0..16 {

View file

@ -3,7 +3,6 @@
use std::collections::HashMap;
use std::io::Write;
use std::iter::FusedIterator;
use std::ops::Deref;
use bitvec::vec::BitVec;
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 {
self.chunks.len()
}
@ -41,6 +50,10 @@ impl Chunks {
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) {
self.chunks.clear();
}
@ -49,10 +62,18 @@ impl Chunks {
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 + '_ {
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> {
let pos = pos.into();
let chunk_pos = ChunkPos::from(pos);
@ -73,55 +94,17 @@ impl Chunks {
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 {
let pos = pos.into();
let chunk_pos = ChunkPos::from(pos);
if let Some(chunk) = self.0.chunks.get_mut(&chunk_pos) {
let min_y = self.0.server.dimension(self.0.dimension).min_y;
if let Some(chunk) = self.chunks.get_mut(&chunk_pos) {
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 y < chunk.height() {
ChunkMut(chunk).set_block_state(
chunk.set_block_state(
pos.x.rem_euclid(16) as usize,
y,
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 {
sections: Box<[ChunkSection]>,
// TODO block_entities: HashMap<u32, BlockEntity>,
@ -169,7 +142,7 @@ impl Chunk {
created_tick: current_tick,
};
ChunkMut(&mut chunk).apply_modifications();
chunk.apply_modifications();
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 {
if x < 4 && y < self.height() / 4 && z < 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
/// does not include unapplied changes.
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) {
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 {
sect.modified_count = 0;
any_modified = true;
@ -370,7 +331,7 @@ impl<'a> ChunkMut<'a> {
}
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]
fn set_get() {
let mut chunk = Chunk::new(16, 0);
let mut chunk = ChunkMut(&mut chunk);
chunk.set_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]
fn block_state_oob() {
let mut chunk = Chunk::new(16, 0);
let mut chunk = ChunkMut(&mut chunk);
chunk.set_block_state(16, 0, 0, BlockState::CAKE);
}
@ -552,7 +511,6 @@ mod tests {
#[should_panic]
fn biome_oob() {
let mut chunk = Chunk::new(16, 0);
let mut chunk = ChunkMut(&mut chunk);
chunk.set_biome(4, 0, 0, BiomeId(0));
}

View file

@ -2,7 +2,6 @@
mod event;
use std::collections::HashSet;
use std::iter::FusedIterator;
use std::ops::Deref;
pub use event::*;
use flume::{Receiver, Sender, TrySendError};
@ -40,21 +39,24 @@ pub struct Clients {
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 {
pub(crate) fn new() -> Self {
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 {
self.sm.len()
}
@ -63,55 +65,27 @@ impl Clients {
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 + '_ {
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 + '_ {
self.sm.par_iter().map(|(k, v)| (ClientId(k), v))
}
}
impl<'a> ClientsMut<'a> {
pub(crate) fn new(c: &'a mut Clients) -> Self {
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 fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (ClientId, &mut Client)> + '_ {
self.sm.par_iter_mut().map(|(k, v)| (ClientId(k), v))
}
}
pub struct ClientId(Key);
/// Represents a client connected to the server after logging in.
@ -165,16 +139,6 @@ pub struct Client {
// 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 {
pub(crate) fn new(
packet_channels: C2sPacketChannels,
@ -236,6 +200,27 @@ impl Client {
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 {
self.yaw
}
@ -250,6 +235,17 @@ impl Client {
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
/// point at the returned position. If the client's current dimension
/// differs from the returned dimension or the location is `None` then the
@ -258,10 +254,25 @@ impl Client {
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 {
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 {
self.on_ground
}
@ -274,6 +285,10 @@ impl Client {
&self.events
}
pub fn events_mut(&mut self) -> &mut Vec<Event> {
&mut self.events
}
/// The current view distance of this client measured in chunks.
pub fn view_distance(&self) -> u8 {
self.settings
@ -286,110 +301,47 @@ impl Client {
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> {
self.settings.as_ref()
}
}
impl<'a> ClientMut<'a> {
pub(crate) fn new(client: &'a mut Client) -> Self {
Self(client)
}
pub fn disconnect(&mut self, reason: impl Into<Text>) {
if self.send.is_some() {
let txt = reason.into();
log::info!("disconnecting client '{}': \"{txt}\"", self.username);
pub fn reborrow(&mut self) -> ClientMut {
ClientMut(self.0)
}
self.send_packet(Disconnect { reason: txt });
pub fn events_mut(&mut self) -> &mut Vec<Event> {
&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);
self.send = None;
}
}
pub fn set_game_mode(&mut self, new_game_mode: GameMode) {
self.0.new_game_mode = new_game_mode;
}
/// 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;
pub fn disconnect_no_reason(&mut self) {
if self.send.is_some() {
log::info!("disconnecting client '{}' (no reason)", self.username);
self.send = None;
}
}
/// 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
/// is disconnected if the clientbound packet buffer is full.
pub(crate) fn send_packet(&mut self, packet: impl Into<S2cPlayPacket>) {
send_packet(&mut self.0.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);
send_packet(&mut self.send, packet);
}
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) {
self.0.events.clear();
self.events.clear();
for _ in 0..self.recv.len() {
self.handle_serverbound_packet(entities, self.recv.try_recv().unwrap());
}
}
fn handle_serverbound_packet(&mut self, entities: &Entities, pkt: C2sPlayPacket) {
let client = &mut self.0;
fn handle_movement_packet(
client: &mut Client,
_vehicle: bool,
@ -419,18 +371,18 @@ impl<'a> ClientMut<'a> {
match pkt {
C2sPlayPacket::TeleportConfirm(p) => {
if client.pending_teleports == 0 {
if self.pending_teleports == 0 {
self.disconnect("Unexpected teleport confirmation");
return;
}
let got = p.teleport_id.0 as u32;
let expected = client
let expected = self
.teleport_id_counter
.wrapping_sub(client.pending_teleports);
.wrapping_sub(self.pending_teleports);
if got == expected {
client.pending_teleports -= 1;
self.pending_teleports -= 1;
} else {
self.disconnect(format!(
"Unexpected teleport ID (expected {expected}, got {got})"
@ -444,7 +396,7 @@ impl<'a> ClientMut<'a> {
C2sPlayPacket::ChatPreview(_) => {}
C2sPlayPacket::ClientStatus(_) => {}
C2sPlayPacket::ClientSettings(p) => {
let old = client.settings.replace(Settings {
let old = self.settings.replace(Settings {
locale: p.locale.0,
view_distance: p.view_distance.0,
chat_mode: p.chat_mode,
@ -454,7 +406,7 @@ impl<'a> ClientMut<'a> {
allow_server_listings: p.allow_server_listings,
});
client.events.push(Event::SettingsChanged(old));
self.events.push(Event::SettingsChanged(old));
}
C2sPlayPacket::TabComplete(_) => {}
C2sPlayPacket::ClickWindowButton(_) => {}
@ -468,7 +420,7 @@ impl<'a> ClientMut<'a> {
// that the distance is <=4 blocks.
use crate::packets::play::c2s::InteractType;
client.events.push(Event::InteractWithEntity {
self.events.push(Event::InteractWithEntity {
id,
sneaking: p.sneaking,
typ: match p.typ {
@ -483,8 +435,8 @@ impl<'a> ClientMut<'a> {
}
C2sPlayPacket::GenerateStructure(_) => {}
C2sPlayPacket::KeepAlive(p) => {
let last_keepalive_id = client.last_keepalive_id;
if client.got_keepalive {
let last_keepalive_id = self.last_keepalive_id;
if self.got_keepalive {
self.disconnect("Unexpected keepalive");
} else if p.id != last_keepalive_id {
self.disconnect(format!(
@ -492,42 +444,32 @@ impl<'a> ClientMut<'a> {
last_keepalive_id, p.id
));
} else {
client.got_keepalive = true;
self.got_keepalive = true;
}
}
C2sPlayPacket::LockDifficulty(_) => {}
C2sPlayPacket::PlayerPosition(p) => handle_movement_packet(
client,
false,
p.position,
client.yaw,
client.pitch,
p.on_ground,
),
C2sPlayPacket::PlayerPositionAndRotation(p) => {
handle_movement_packet(client, false, p.position, p.yaw, p.pitch, p.on_ground)
C2sPlayPacket::PlayerPosition(p) => {
handle_movement_packet(self, false, p.position, self.yaw, self.pitch, p.on_ground)
}
C2sPlayPacket::PlayerPositionAndRotation(p) => {
handle_movement_packet(self, false, p.position, p.yaw, p.pitch, p.on_ground)
}
C2sPlayPacket::PlayerRotation(p) => {
handle_movement_packet(self, false, self.new_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(
client,
self,
false,
client.new_position,
client.yaw,
client.pitch,
self.new_position,
self.yaw,
self.pitch,
p.on_ground,
),
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) => {
client.events.push(Event::SteerBoat {
self.events.push(Event::SteerBoat {
left_paddle_turning: p.left_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?
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 {
status: event::DiggingStatus::Start,
position: p.location,
@ -599,7 +541,7 @@ impl<'a> ClientMut<'a> {
) {
// Mark the client as disconnected when appropriate.
if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) {
self.0.send = None;
self.send = None;
return;
}
@ -641,11 +583,11 @@ impl<'a> ClientMut<'a> {
self.teleport(self.position(), self.yaw(), self.pitch());
} else {
if self.0.old_game_mode != self.0.new_game_mode {
self.0.old_game_mode = self.0.new_game_mode;
if self.old_game_mode != self.new_game_mode {
self.old_game_mode = self.new_game_mode;
self.send_packet(ChangeGameState {
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)
if self.0.modified_spawn_position {
self.0.modified_spawn_position = false;
if self.modified_spawn_position {
self.modified_spawn_position = false;
self.send_packet(SpawnPosition {
location: self.spawn_position,
@ -663,22 +605,22 @@ impl<'a> ClientMut<'a> {
}
// Update view distance fog on the client if necessary.
if self.0.old_max_view_distance != self.0.new_max_view_distance {
self.0.old_max_view_distance = self.0.new_max_view_distance;
if self.0.created_tick != current_tick {
if self.old_max_view_distance != self.new_max_view_distance {
self.old_max_view_distance = self.new_max_view_distance;
if self.created_tick != current_tick {
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.
if current_tick % (server.tick_rate() * 8) == 0 {
if self.0.got_keepalive {
if self.got_keepalive {
let id = rand::random();
self.send_packet(KeepAlive { id });
self.0.last_keepalive_id = id;
self.0.got_keepalive = false;
self.last_keepalive_id = id;
self.got_keepalive = false;
} else {
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
// they're in.
{
let old_section = self.0.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 old_section = self.old_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 {
self.send_packet(UpdateViewPosition {
@ -706,7 +648,7 @@ impl<'a> ClientMut<'a> {
// Update existing chunks and unload those outside the view distance. Chunks
// 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
// moves to an adjacent chunk and back to the original.
let cache = 2;
@ -716,14 +658,14 @@ impl<'a> ClientMut<'a> {
&& chunk.created_tick() != current_tick
{
chunk.block_change_packets(pos, dimension.min_y, |pkt| {
send_packet(&mut self.0.send, pkt)
send_packet(&mut self.send, pkt)
});
return true;
}
}
send_packet(
&mut self.0.send,
&mut self.send,
UnloadChunk {
chunk_x: pos.x,
chunk_z: pos.z,
@ -735,7 +677,7 @@ impl<'a> ClientMut<'a> {
// Load new chunks within the view distance
for pos in chunks_in_view_distance(center, view_dist) {
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));
chunk.block_change_packets(pos, dimension.min_y, |pkt| self.send_packet(pkt));
}
@ -743,9 +685,9 @@ impl<'a> ClientMut<'a> {
}
// Acknowledge broken blocks.
for seq in self.0.dug_blocks.drain(..) {
for seq in self.dug_blocks.drain(..) {
send_packet(
&mut self.0.send,
&mut self.send,
AcknowledgeBlockChanges {
sequence: VarInt(seq),
},
@ -754,8 +696,8 @@ impl<'a> ClientMut<'a> {
// This is done after the chunks are loaded so that the "downloading terrain"
// screen is closed at the appropriate time.
if self.0.teleported_this_tick {
self.0.teleported_this_tick = false;
if self.teleported_this_tick {
self.teleported_this_tick = false;
self.send_packet(PlayerPositionAndLook {
position: self.new_position,
@ -771,14 +713,14 @@ impl<'a> ClientMut<'a> {
// Update all entities that are visible and unload entities that are no
// longer visible.
self.0.loaded_entities.retain(|&id| {
self.loaded_entities.retain(|&id| {
if let Some(entity) = entities.get(id) {
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()
{
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();
@ -790,7 +732,7 @@ impl<'a> ClientMut<'a> {
&& flags.yaw_or_pitch_modified()
{
send_packet(
&mut self.0.send,
&mut self.send,
EntityPositionAndRotation {
entity_id: VarInt(id.to_network_id()),
delta: (position_delta * 4096.0).as_(),
@ -802,7 +744,7 @@ impl<'a> ClientMut<'a> {
} else {
if entity.position() != entity.old_position() && !needs_teleport {
send_packet(
&mut self.0.send,
&mut self.send,
EntityPosition {
entity_id: VarInt(id.to_network_id()),
delta: (position_delta * 4096.0).as_(),
@ -813,7 +755,7 @@ impl<'a> ClientMut<'a> {
if flags.yaw_or_pitch_modified() {
send_packet(
&mut self.0.send,
&mut self.send,
EntityRotation {
entity_id: VarInt(id.to_network_id()),
yaw: ByteAngle::from_degrees(entity.yaw()),
@ -826,7 +768,7 @@ impl<'a> ClientMut<'a> {
if needs_teleport {
send_packet(
&mut self.0.send,
&mut self.send,
EntityTeleport {
entity_id: VarInt(id.to_network_id()),
position: entity.position(),
@ -839,7 +781,7 @@ impl<'a> ClientMut<'a> {
if flags.velocity_modified() {
send_packet(
&mut self.0.send,
&mut self.send,
EntityVelocity {
entity_id: VarInt(id.to_network_id()),
velocity: velocity_to_packet_units(entity.velocity()),
@ -849,7 +791,7 @@ impl<'a> ClientMut<'a> {
if flags.head_yaw_modified() {
send_packet(
&mut self.0.send,
&mut self.send,
EntityHeadLook {
entity_id: VarInt(id.to_network_id()),
head_yaw: ByteAngle::from_degrees(entity.head_yaw()),
@ -876,7 +818,7 @@ impl<'a> ClientMut<'a> {
spatial_index.query::<_, _, ()>(
|bb| bb.projected_point(pos).distance(pos) <= view_dist as f64 * 16.0,
|id, _| {
if self.0.loaded_entities.insert(id) {
if self.loaded_entities.insert(id) {
let entity = entities.get(id).unwrap();
if entity.typ() != EntityType::Marker {
self.send_packet(
@ -894,13 +836,7 @@ impl<'a> ClientMut<'a> {
},
);
self.0.old_position = self.0.new_position;
}
}
impl Drop for Client {
fn drop(&mut self) {
log::trace!("Dropping client '{}'", self.username);
self.old_position = self.new_position;
}
}

View file

@ -5,8 +5,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
use async_trait::async_trait;
use tokio::runtime::Handle as TokioHandle;
use crate::client::ClientMut;
use crate::{Biome, Dimension, NewClientData, Server, Text, Ticks, WorldId, WorldsMut};
use crate::{Biome, Client, Dimension, NewClientData, Server, Text, Ticks, WorldId, Worlds};
/// A trait containing callbacks which are invoked by the running Minecraft
/// server.
@ -182,7 +181,12 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
/// If the returned [`WorldId`] is invalid, then the client is disconnected.
///
/// 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
/// 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.
///
/// 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
/// a "tick").
@ -203,7 +207,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
///
/// # Default Implementation
/// 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.

View file

@ -5,10 +5,10 @@ use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::iter::FusedIterator;
use std::num::NonZeroU32;
use std::ops::Deref;
use bitfield_struct::bitfield;
use rayon::iter::ParallelIterator;
pub use types::{EntityMeta, EntityType};
use uuid::Uuid;
use vek::{Aabb, Vec3};
@ -27,16 +27,6 @@ pub struct Entities {
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 {
pub(crate) fn new() -> 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.
pub fn len(&self) -> usize {
pub fn count(&self) -> usize {
self.sm.len()
}
@ -64,6 +126,10 @@ impl Entities {
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> {
let version = NonZeroU32::new(network_id as u32)?;
let index = *self.network_id_to_entity.get(&version)?;
@ -74,122 +140,26 @@ impl Entities {
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 + '_ {
self.sm.par_iter().map(|(k, v)| (EntityId(k), v))
}
}
impl<'a> EntitiesMut<'a> {
pub(crate) fn new(entities: &'a mut Entities) -> Self {
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 fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (EntityId, &mut Entity)> + '_ {
self.sm.par_iter_mut().map(|(k, v)| (EntityId(k), v))
}
pub(crate) fn update(&mut self) {
for (_, e) in self.iter_mut() {
e.0.old_position = e.new_position;
e.0.meta.clear_modifications();
e.old_position = e.new_position;
e.meta.clear_modifications();
let on_ground = e.0.flags.on_ground();
e.0.flags = EntityFlags(0);
e.0.flags.set_on_ground(on_ground);
let on_ground = e.flags.on_ground();
e.flags = EntityFlags(0);
e.flags.set_on_ground(on_ground);
}
}
}
@ -215,16 +185,6 @@ pub struct Entity {
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
/// modified.
#[bitfield(u8)]
@ -249,16 +209,35 @@ impl Entity {
&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.
pub fn typ(&self) -> EntityType {
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.
pub fn position(&self) -> Vec3<f64> {
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
/// previous tick.
pub fn old_position(&self) -> Vec3<f64> {
@ -270,25 +249,62 @@ impl Entity {
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).
pub fn pitch(&self) -> f32 {
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).
pub fn head_yaw(&self) -> f32 {
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.
pub fn velocity(&self) -> Vec3<f32> {
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 {
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
/// spawned.
///
@ -490,69 +506,6 @@ pub(crate) fn velocity_to_packet_units(vel: Vec3<f32>) -> Vec3<i16> {
(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 {
SpawnEntity(SpawnEntity),
SpawnExperienceOrb(SpawnExperienceOrb),
@ -568,5 +521,3 @@ impl From<EntitySpawnPacket> for S2cPlayPacket {
}
}
}
pub use types::{EntityMeta, EntityType};

View file

@ -22,6 +22,7 @@ pub mod entity;
pub mod ident;
mod packets;
mod player_list;
pub mod player_textures;
mod protocol;
pub mod server;
mod slotmap;
@ -31,24 +32,23 @@ pub mod util;
mod var_int;
mod var_long;
pub mod world;
pub mod player_textures;
pub use async_trait::async_trait;
pub use biome::{Biome, BiomeId};
pub use block::BlockState;
pub use block_pos::BlockPos;
pub use chunk::{Chunk, Chunks, ChunksMut};
pub use chunk::{Chunk, Chunks};
pub use chunk_pos::ChunkPos;
pub use client::{Client, ClientMut, Clients, ClientsMut};
pub use client::{Client, Clients};
pub use config::Config;
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 server::{start_server, NewClientData, Server, ShutdownResult};
pub use spatial_index::{SpatialIndex, SpatialIndexMut};
pub use spatial_index::SpatialIndex;
pub use text::{Text, TextFormat};
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};
/// The Minecraft protocol version that this library targets.

View file

@ -1,6 +1,5 @@
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::ops::Deref;
use bitfield_struct::bitfield;
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 {
&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 {
&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)> + '_ {
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)) {
let add_player: Vec<_> = self
.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) {
for e in self.0.entries.values_mut() {
for e in self.entries.values_mut() {
e.flags = EntryFlags(0);
}
self.0.removed.clear();
self.0.modified_header_or_footer = false;
self.removed.clear();
self.modified_header_or_footer = false;
}
}
@ -282,49 +262,33 @@ impl PlayerListEntry {
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 {
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> {
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>>) {
let display_name = display_name.into();
if self.0.display_name != display_name {
self.0.display_name = display_name;
self.0.flags.set_modified_display_name(true);
if self.display_name != display_name {
self.display_name = display_name;
self.flags.set_modified_display_name(true);
}
}
}

View file

@ -42,8 +42,7 @@ use crate::util::valid_username;
use crate::var_int::VarInt;
use crate::world::Worlds;
use crate::{
Biome, BiomeId, Client, ClientMut, Dimension, DimensionId, Ticks, WorldsMut, PROTOCOL_VERSION,
VERSION_NAME,
Biome, BiomeId, Client, Dimension, DimensionId, Ticks, PROTOCOL_VERSION, VERSION_NAME,
};
/// 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 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()));
do_update_loop(server, worlds_mut)
do_update_loop(server, &mut worlds)
}
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)))
}
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();
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() {
join_player(&server, worlds.reborrow(), msg);
join_player(&server, worlds, msg);
}
// Get serverbound packets first so they are not dealt with a tick late.
worlds.par_iter_mut().for_each(|(_, mut world)| {
world.clients.par_iter_mut().for_each(|(_, mut client)| {
worlds.par_iter_mut().for_each(|(_, world)| {
world.clients.par_iter_mut().for_each(|(_, client)| {
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)| {
world.chunks.par_iter_mut().for_each(|(_, mut chunk)| {
worlds.par_iter_mut().for_each(|(_, world)| {
world.chunks.par_iter_mut().for_each(|(_, chunk)| {
if chunk.created_tick() == server.current_tick() {
// 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
@ -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(
&server,
&world.entities,
@ -395,7 +393,7 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
world.entities.update();
world.chunks.par_iter_mut().for_each(|(_, mut chunk)| {
world.chunks.par_iter_mut().for_each(|(_, chunk)| {
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 (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 mut client = Client::new(c2s_packet_channels, server, msg.ncd);
let mut client_mut = ClientMut::new(&mut client);
match server
.0
.cfg
.join(server, client_mut.reborrow(), worlds.reborrow())
{
match server.0.cfg.join(server, &mut client, worlds) {
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() {
world.clients.create(client);
} 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),
}
}

View file

@ -1,25 +1,13 @@
use std::ops::Deref;
use vek::{Aabb, Vec3};
use crate::bvh::Bvh;
pub use crate::bvh::TraverseStep;
use crate::{EntitiesMut, EntityId};
use crate::{Entities, EntityId};
pub struct SpatialIndex {
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 {
pub(crate) fn new() -> Self {
Self { bvh: Bvh::new() }
@ -107,16 +95,9 @@ impl SpatialIndex {
},
)
}
}
impl<'a> SpatialIndexMut<'a> {
pub(crate) fn new(si: &'a mut SpatialIndex) -> Self {
Self(si)
}
pub(crate) fn update(&mut self, entities: EntitiesMut) {
self.0
.bvh
pub(crate) fn update(&mut self, entities: &Entities) {
self.bvh
.build(entities.iter().map(|(id, e)| (id, e.hitbox())))
}
}

View file

@ -1,30 +1,16 @@
use std::iter::FusedIterator;
use std::ops::Deref;
use rayon::iter::ParallelIterator;
use crate::player_list::{PlayerList, PlayerListMut};
use crate::player_list::PlayerList;
use crate::slotmap::{Key, SlotMap};
use crate::{
Chunks, ChunksMut, Clients, ClientsMut, DimensionId, Entities, EntitiesMut, Server,
SpatialIndex, SpatialIndexMut,
};
use crate::{Chunks, Clients, DimensionId, Entities, Server, SpatialIndex};
pub struct Worlds {
sm: SlotMap<World>,
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)]
pub struct WorldId(Key);
@ -36,30 +22,8 @@ impl Worlds {
}
}
pub fn count(&self) -> usize {
self.sm.len()
}
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 {
pub fn create(&mut self, dim: DimensionId) -> (WorldId, &mut World) {
let (id, world) = self.sm.insert(World {
clients: Clients::new(),
entities: Entities::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
@ -80,106 +44,48 @@ impl<'a> WorldsMut<'a> {
/// Note that any entities with positions inside the deleted world will not
/// be deleted themselves.
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) {
self.0.sm.retain(|k, v| f(WorldId(k), WorldMut::new(v)))
pub fn retain(&mut self, mut f: impl FnMut(WorldId, &mut World) -> bool) {
self.sm.retain(|k, v| f(WorldId(k), v))
}
pub fn get_mut(&mut self, world: WorldId) -> Option<WorldMut> {
self.0.sm.get_mut(world.0).map(WorldMut::new)
pub fn count(&self) -> usize {
self.sm.len()
}
pub fn iter_mut(&mut self) -> impl FusedIterator<Item = (WorldId, WorldMut)> + '_ {
self.0
.sm
.iter_mut()
.map(|(k, v)| (WorldId(k), WorldMut::new(v)))
pub fn get(&self, world: WorldId) -> Option<&World> {
self.sm.get(world.0)
}
pub fn par_iter(&self) -> impl ParallelIterator<Item = (WorldId, WorldRef)> + Clone + '_ {
self.0
.sm
.par_iter()
.map(|(k, v)| (WorldId(k), WorldRef::new(v)))
pub fn get_mut(&mut self, world: WorldId) -> Option<&mut World> {
self.sm.get_mut(world.0)
}
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = (WorldId, WorldMut)> + '_ {
self.0
.sm
.par_iter_mut()
.map(|(k, v)| (WorldId(k), WorldMut::new(v)))
pub fn iter(&self) -> impl FusedIterator<Item = (WorldId, &World)> + Clone + '_ {
self.sm.iter().map(|(k, v)| (WorldId(k), 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
/// inhabit.
///
/// Worlds maintain a collection of chunks and entities that are a part of it.
/// For a chunk or entity to appear, it must be inserted into the world. Chunks
/// and entities can be in multiple worlds at the same time.
///
/// 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 World {
pub clients: Clients,
pub entities: Entities,
pub spatial_index: SpatialIndex,
pub chunks: Chunks,
pub meta: WorldMeta,
}
pub struct WorldMeta {
@ -198,31 +104,19 @@ impl WorldMeta {
self.is_flat
}
pub fn set_flat(&mut self, flat: bool) {
self.is_flat = flat;
}
pub fn player_list(&self) -> &PlayerList {
&self.player_list
}
}
pub struct WorldMetaMut<'a>(&'a mut WorldMeta);
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 fn player_list_mut(&mut self) -> &mut PlayerList {
&mut self.player_list
}
pub(crate) fn update(&mut self) {
self.player_list_mut().update();
self.player_list.update();
}
}