Get entity movement working

This commit is contained in:
Ryan 2022-05-17 02:58:43 -07:00
parent 8da32d0b8b
commit 1570c95ac8
9 changed files with 362 additions and 70 deletions

View file

@ -109,12 +109,6 @@ impl Config for Game {
} }
} }
} }
let entity_id = world.entities.create();
let mut entity = world.entities.get_mut(entity_id).unwrap();
entity.set_type(EntityType::Cow);
entity.set_position([0.0, 50.0, 0.0]);
} }
fn update(&self, server: &Server, mut worlds: WorldsMut) { fn update(&self, server: &Server, mut worlds: WorldsMut) {

119
examples/cow_sphere.rs Normal file
View file

@ -0,0 +1,119 @@
use std::net::SocketAddr;
use std::sync::atomic::{AtomicUsize, Ordering};
use log::LevelFilter;
use valence::block::BlockState;
use valence::client::GameMode;
use valence::config::{Config, ServerListPing};
use valence::text::Color;
use valence::{
async_trait, ChunkPos, ClientMut, DimensionId, EntityType, Server, ShutdownResult, Text,
TextFormat, WorldId, WorldsMut,
};
pub fn main() -> ShutdownResult {
env_logger::Builder::new()
.filter_module("valence", LevelFilter::Trace)
.parse_default_env()
.init();
valence::start_server(Game {
player_count: AtomicUsize::new(0),
})
}
struct Game {
player_count: AtomicUsize,
}
const MAX_PLAYERS: usize = 10;
#[async_trait]
impl Config for Game {
fn max_connections(&self) -> usize {
// We want status pings to be successful even if the server is full.
MAX_PLAYERS + 64
}
fn online_mode(&self) -> bool {
// You'll want this to be true on real servers.
false
}
async fn server_list_ping(&self, _server: &Server, _remote_addr: SocketAddr) -> ServerListPing {
ServerListPing::Respond {
online_players: self.player_count.load(Ordering::SeqCst) as i32,
max_players: MAX_PLAYERS as i32,
description: "Hello Valence!".color(Color::AQUA),
favicon_png: Some(include_bytes!("favicon.png")),
}
}
fn join(
&self,
_server: &Server,
_client: ClientMut,
worlds: WorldsMut,
) -> Result<WorldId, Text> {
if let Ok(_) = self
.player_count
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
(count < MAX_PLAYERS).then(|| count + 1)
})
{
Ok(worlds.iter().next().unwrap().0)
} else {
Err("The server is full!".into())
}
}
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
let world_id = worlds.create(DimensionId::default());
let mut world = worlds.get_mut(world_id).unwrap();
world.meta.set_flat(true);
let size = 5;
for z in -size..size {
for x in -size..size {
world.chunks.create([x, z]);
}
}
let entity_id = world.entities.create();
let mut entity = world.entities.get_mut(entity_id).unwrap();
entity.set_type(EntityType::Cow);
entity.set_position([0.0, 100.0, 0.0]);
//entity.set_yaw(30.0);
//entity.set_pitch(0.0);
}
fn update(&self, server: &Server, mut worlds: WorldsMut) {
let mut world = worlds.iter_mut().next().unwrap().1;
world.clients.retain(|_, mut 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);
}
if client.is_disconnected() {
self.player_count.fetch_sub(1, Ordering::SeqCst);
false
} else {
true
}
});
for (_, mut e) in world.entities.iter_mut() {
let time = server.current_tick() as f64 / server.tick_rate() as f64;
if e.typ() == EntityType::Cow {
e.set_position(e.position() + [0.0, 0.0, 0.02]);
let yaw = (time % 1.0 * 360.0) as f32;
e.set_yaw(yaw);
e.set_head_yaw(yaw);
}
}
}
}

View file

@ -418,7 +418,8 @@ fn encode_paletted_container(
let palette_idx = palette let palette_idx = palette
.iter() .iter()
.position(|&e| e == entry) .position(|&e| e == entry)
.expect("entry should be in the palette") as u64; .expect("entry should be in the palette")
as u64;
val |= palette_idx << (i * bits_per_idx); val |= palette_idx << (i * bits_per_idx);
} }

View file

@ -8,25 +8,29 @@ use uuid::Uuid;
use vek::Vec3; use vek::Vec3;
use crate::block_pos::BlockPos; use crate::block_pos::BlockPos;
use crate::byte_angle::ByteAngle;
use crate::config::{ use crate::config::{
Biome, BiomeGrassColorModifier, BiomePrecipitation, Dimension, DimensionEffects, DimensionId, Biome, BiomeGrassColorModifier, BiomePrecipitation, Dimension, DimensionEffects, DimensionId,
}; };
use crate::entity::EntityType; use crate::entity::{velocity_to_packet_units, EntityType};
pub use crate::packets::play::GameMode; pub use crate::packets::play::GameMode;
use crate::packets::play::{ use crate::packets::play::{
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState,
ChangeGameStateReason, ClientPlayPacket, DestroyEntities, DimensionCodec, DimensionType, ChangeGameStateReason, ClientPlayPacket, DestroyEntities, DimensionCodec, DimensionType,
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, JoinGame, KeepAliveClientbound, DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
PlayerPositionAndLook, PlayerPositionAndLookFlags, ServerPlayPacket, SpawnPosition, EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame,
UnloadChunk, UpdateViewDistance, UpdateViewPosition, KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, ServerPlayPacket,
SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition,
}; };
use crate::protocol::{BoundedInt, Nbt}; use crate::protocol::{BoundedInt, Nbt};
use crate::server::ServerPacketChannels; use crate::server::ServerPacketChannels;
use crate::slotmap::{Key, SlotMap}; use crate::slotmap::{Key, SlotMap};
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance}; use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
use crate::var_int::VarInt; use crate::var_int::VarInt;
use crate::{ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, LIBRARY_NAMESPACE}; use crate::{
ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, WorldMeta, LIBRARY_NAMESPACE,
};
pub struct Clients { pub struct Clients {
sm: SlotMap<Client>, sm: SlotMap<Client>,
@ -328,7 +332,7 @@ impl<'a> ClientMut<'a> {
server: &Server, server: &Server,
entities: &Entities, entities: &Entities,
chunks: &Chunks, chunks: &Chunks,
dimension_id: DimensionId, meta: &WorldMeta,
) { ) {
self.0.events.clear(); self.0.events.clear();
@ -347,11 +351,13 @@ impl<'a> ClientMut<'a> {
return; return;
} }
let dimension = server.dimension(dimension_id); let dimension = server.dimension(meta.dimension());
let current_tick = server.current_tick();
// Send the join game packet and other initial packets. We defer this until now // Send the join game packet and other initial packets. We defer this until now
// so that the user can set the client's location, game mode, etc. // so that the user can set the client's location, game mode, etc.
if self.created_tick == server.current_tick() { if self.created_tick == current_tick {
self.send_packet(JoinGame { self.send_packet(JoinGame {
entity_id: 0, // EntityId 0 is reserved for clients. entity_id: 0, // EntityId 0 is reserved for clients.
is_hardcore: false, // TODO is_hardcore: false, // TODO
@ -363,7 +369,7 @@ impl<'a> ClientMut<'a> {
.collect(), .collect(),
dimension_codec: Nbt(make_dimension_codec(server)), dimension_codec: Nbt(make_dimension_codec(server)),
dimension: Nbt(to_dimension_registry_item(dimension)), dimension: Nbt(to_dimension_registry_item(dimension)),
dimension_name: ident!("{LIBRARY_NAMESPACE}:dimension_{}", dimension_id.0), dimension_name: ident!("{LIBRARY_NAMESPACE}:dimension_{}", meta.dimension().0),
hashed_seed: 0, hashed_seed: 0,
max_players: VarInt(0), max_players: VarInt(0),
view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)), view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)),
@ -396,7 +402,7 @@ 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.0.old_max_view_distance != self.0.new_max_view_distance {
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 != server.current_tick() { if self.0.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.0.new_max_view_distance as i32)),
}) })
@ -404,7 +410,7 @@ impl<'a> ClientMut<'a> {
} }
// Check if it's time to send another keepalive. // Check if it's time to send another keepalive.
if server.current_tick() % (server.tick_rate() * 8) == 0 { if current_tick % (server.tick_rate() * 8) == 0 {
if self.0.got_keepalive { if self.0.got_keepalive {
let id = rand::random(); let id = rand::random();
self.send_packet(KeepAliveClientbound { id }); self.send_packet(KeepAliveClientbound { id });
@ -451,7 +457,7 @@ impl<'a> ClientMut<'a> {
if let Some(chunk) = chunks.get(pos) { if let Some(chunk) = chunks.get(pos) {
if is_chunk_in_view_distance(center, pos, view_dist + cache) if is_chunk_in_view_distance(center, pos, view_dist + cache)
&& chunk.created_tick() != server.current_tick() && chunk.created_tick() != current_tick
{ {
if let Some(pkt) = chunk.block_change_packet(pos) { if let Some(pkt) = chunk.block_change_packet(pos) {
send_packet(&mut self.0.send, pkt); send_packet(&mut self.0.send, pkt);
@ -484,7 +490,6 @@ 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.0.teleported_this_tick {
self.0.teleported_this_tick = false; self.0.teleported_this_tick = false;
@ -504,10 +509,90 @@ impl<'a> ClientMut<'a> {
// longer visible. // longer visible.
self.0.loaded_entities.retain(|&id| { self.0.loaded_entities.retain(|&id| {
if let Some(entity) = entities.get(id) { if let Some(entity) = entities.get(id) {
if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 { debug_assert!(entity.typ() != EntityType::Marker);
if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0
&& !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.0.send, meta);
} }
let position_delta = entity.position() - entity.old_position();
let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0;
let flags = entity.flags();
if entity.position() != entity.old_position()
&& !needs_teleport
&& flags.yaw_or_pitch_modified()
{
send_packet(
&mut self.0.send,
EntityPositionAndRotation {
entity_id: VarInt(id.to_network_id()),
delta: (position_delta * 4096.0).as_(),
yaw: ByteAngle::from_degrees(entity.yaw()),
pitch: ByteAngle::from_degrees(entity.pitch()),
on_ground: entity.on_ground(),
},
);
} else {
if entity.position() != entity.old_position() && !needs_teleport {
send_packet(
&mut self.0.send,
EntityPosition {
entity_id: VarInt(id.to_network_id()),
delta: (position_delta * 4096.0).as_(),
on_ground: entity.on_ground(),
},
);
}
if flags.yaw_or_pitch_modified() {
send_packet(
&mut self.0.send,
EntityRotation {
entity_id: VarInt(id.to_network_id()),
yaw: ByteAngle::from_degrees(entity.yaw()),
pitch: ByteAngle::from_degrees(entity.pitch()),
on_ground: entity.on_ground(),
},
);
}
}
if needs_teleport {
send_packet(
&mut self.0.send,
EntityTeleport {
entity_id: VarInt(id.to_network_id()),
position: entity.position(),
yaw: ByteAngle::from_degrees(entity.yaw()),
pitch: ByteAngle::from_degrees(entity.pitch()),
on_ground: entity.on_ground(),
},
);
}
if flags.velocity_modified() {
send_packet(
&mut self.0.send,
EntityVelocity {
entity_id: VarInt(id.to_network_id()),
velocity: velocity_to_packet_units(entity.velocity()),
},
);
}
if flags.head_yaw_modified() {
send_packet(
&mut self.0.send,
EntityHeadLook {
entity_id: VarInt(id.to_network_id()),
head_yaw: ByteAngle::from_degrees(entity.head_yaw()),
},
)
}
return true; return true;
} }
} }
@ -523,6 +608,7 @@ impl<'a> ClientMut<'a> {
} }
// Spawn new entities within the view distance. // Spawn new entities within the view distance.
// TODO: use BVH
for (id, entity) in entities.iter() { for (id, entity) in entities.iter() {
if self.position().distance(entity.position()) <= view_dist as f64 * 16.0 if self.position().distance(entity.position()) <= view_dist as f64 * 16.0
&& entity.typ() != EntityType::Marker && entity.typ() != EntityType::Marker

View file

@ -162,7 +162,10 @@ impl<'a> EntitiesMut<'a> {
for (_, e) in self.iter_mut() { for (_, e) in self.iter_mut() {
e.0.old_position = e.new_position; e.0.old_position = e.new_position;
e.0.meta.clear_modifications(); e.0.meta.clear_modifications();
let on_ground = e.0.flags.on_ground();
e.0.flags = EntityFlags(0); e.0.flags = EntityFlags(0);
e.0.flags.set_on_ground(on_ground);
} }
} }
} }
@ -204,11 +207,12 @@ impl<'a> Deref for EntityMut<'a> {
/// modified. /// modified.
#[bitfield(u8)] #[bitfield(u8)]
pub(crate) struct EntityFlags { pub(crate) struct EntityFlags {
meta_modified: bool, /// When the type of this entity changes.
yaw_or_pitch_modified: bool, pub type_modified: bool,
head_yaw_modified: bool, pub yaw_or_pitch_modified: bool,
head_pitch_modified: bool, pub head_yaw_modified: bool,
velocity_modified: bool, pub velocity_modified: bool,
pub on_ground: bool,
#[bits(3)] #[bits(3)]
_pad: u8, _pad: u8,
} }
@ -254,16 +258,15 @@ impl Entity {
self.head_yaw self.head_yaw
} }
/// Gets the head pitch of this entity (in degrees).
pub fn head_pitch(&self) -> f32 {
self.head_pitch
}
/// 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 on_ground(&self) -> bool {
self.flags.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.
/// ///
@ -361,7 +364,7 @@ impl Entity {
position: self.new_position, position: self.new_position,
yaw: ByteAngle::from_degrees(self.yaw), yaw: ByteAngle::from_degrees(self.yaw),
pitch: ByteAngle::from_degrees(self.pitch), pitch: ByteAngle::from_degrees(self.pitch),
head_pitch: ByteAngle::from_degrees(self.head_pitch), head_yaw: ByteAngle::from_degrees(self.head_yaw),
velocity: velocity_to_packet_units(self.velocity), velocity: velocity_to_packet_units(self.velocity),
})) }))
} }
@ -507,7 +510,7 @@ impl Entity {
} }
} }
fn velocity_to_packet_units(vel: Vec3<f32>) -> Vec3<i16> { pub(crate) fn velocity_to_packet_units(vel: Vec3<f32>) -> Vec3<i16> {
// The saturating cast to i16 is desirable. // The saturating cast to i16 is desirable.
(vel * 400.0).as_() (vel * 400.0).as_()
} }
@ -528,8 +531,8 @@ impl<'a> EntityMut<'a> {
/// All metadata of this entity is reset to the default values. /// All metadata of this entity is reset to the default values.
pub fn set_type(&mut self, typ: EntityType) { pub fn set_type(&mut self, typ: EntityType) {
self.0.meta = EntityMeta::new(typ); self.0.meta = EntityMeta::new(typ);
// All metadata is lost, so we must mark it as modified unconditionally. // All metadata is lost so we must mark it as modified unconditionally.
self.0.flags.set_meta_modified(true); self.0.flags.set_type_modified(true);
} }
/// Sets the position of this entity in the world it inhabits. /// Sets the position of this entity in the world it inhabits.
@ -539,43 +542,40 @@ impl<'a> EntityMut<'a> {
/// Sets the yaw of this entity (in degrees). /// Sets the yaw of this entity (in degrees).
pub fn set_yaw(&mut self, yaw: f32) { pub fn set_yaw(&mut self, yaw: f32) {
self.0.yaw = yaw; if self.0.yaw != yaw {
if ByteAngle::from_degrees(self.yaw) != ByteAngle::from_degrees(yaw) { self.0.yaw = yaw;
self.0.flags.set_yaw_or_pitch_modified(true); self.0.flags.set_yaw_or_pitch_modified(true);
} }
} }
/// Sets the pitch of this entity (in degrees). /// Sets the pitch of this entity (in degrees).
pub fn set_pitch(&mut self, pitch: f32) { pub fn set_pitch(&mut self, pitch: f32) {
self.0.pitch = pitch; if self.0.pitch != pitch {
if ByteAngle::from_degrees(self.pitch) != ByteAngle::from_degrees(pitch) { self.0.pitch = pitch;
self.0.flags.set_yaw_or_pitch_modified(true); self.0.flags.set_yaw_or_pitch_modified(true);
} }
} }
/// Sets the head yaw of this entity (in degrees). /// Sets the head yaw of this entity (in degrees).
pub fn set_head_yaw(&mut self, head_yaw: f32) { pub fn set_head_yaw(&mut self, head_yaw: f32) {
self.0.head_yaw = head_yaw; if self.0.head_yaw != head_yaw {
if ByteAngle::from_degrees(self.head_yaw) != ByteAngle::from_degrees(head_yaw) { self.0.head_yaw = head_yaw;
self.0.flags.set_head_yaw_modified(true); self.0.flags.set_head_yaw_modified(true);
} }
} }
/// Sets the head pitch of this entity (in degrees).
pub fn set_head_pitch(&mut self, head_pitch: f32) {
self.0.head_pitch = head_pitch;
if ByteAngle::from_degrees(self.head_pitch) != ByteAngle::from_degrees(head_pitch) {
self.0.flags.set_head_pitch_modified(true);
}
}
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) { pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
let new_vel = velocity.into(); let new_vel = velocity.into();
self.0.velocity = new_vel;
if velocity_to_packet_units(self.velocity) != velocity_to_packet_units(new_vel) { if self.0.velocity != new_vel {
self.0.velocity = new_vel;
self.0.flags.set_velocity_modified(true); 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 {

View file

@ -36,7 +36,7 @@ pub use identifier::Identifier;
pub use server::{start_server, NewClientData, Server, ShutdownResult}; pub use server::{start_server, NewClientData, Server, ShutdownResult};
pub use text::{Text, TextFormat}; pub use text::{Text, TextFormat};
pub use uuid::Uuid; pub use uuid::Uuid;
pub use world::{WorldId, WorldMut, WorldRef, Worlds, WorldsMut}; pub use world::{WorldId, WorldMeta, WorldMetaMut, WorldMut, WorldRef, Worlds, WorldsMut};
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.

View file

@ -13,7 +13,7 @@ use num::{One, Zero};
use paste::paste; use paste::paste;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use vek::{Vec2, Vec3}; use vek::Vec3;
use crate::block_pos::BlockPos; use crate::block_pos::BlockPos;
use crate::byte_angle::ByteAngle; use crate::byte_angle::ByteAngle;
@ -469,7 +469,7 @@ pub mod play {
position: Vec3<f64>, position: Vec3<f64>,
yaw: ByteAngle, yaw: ByteAngle,
pitch: ByteAngle, pitch: ByteAngle,
head_pitch: ByteAngle, head_yaw: ByteAngle,
velocity: Vec3<i16>, velocity: Vec3<i16>,
} }
} }
@ -964,6 +964,33 @@ pub mod play {
} }
} }
def_struct! {
EntityPosition 0x29 {
entity_id: VarInt,
delta: Vec3<i16>,
on_ground: bool,
}
}
def_struct! {
EntityPositionAndRotation 0x2a {
entity_id: VarInt,
delta: Vec3<i16>,
yaw: ByteAngle,
pitch: ByteAngle,
on_ground: bool,
}
}
def_struct! {
EntityRotation 0x2b {
entity_id: VarInt,
yaw: ByteAngle,
pitch: ByteAngle,
on_ground: bool,
}
}
def_struct! { def_struct! {
PlayerPositionAndLook 0x38 { PlayerPositionAndLook 0x38 {
position: Vec3<f64>, position: Vec3<f64>,
@ -991,6 +1018,13 @@ pub mod play {
} }
} }
def_struct! {
EntityHeadLook 0x3e {
entity_id: VarInt,
head_yaw: ByteAngle,
}
}
def_struct! { def_struct! {
MultiBlockChange 0x3f { MultiBlockChange 0x3f {
chunk_section_position: u64, chunk_section_position: u64,
@ -1032,6 +1066,13 @@ pub mod play {
} }
} }
def_struct! {
EntityVelocity 0x4f {
entity_id: VarInt,
velocity: Vec3<i16>,
}
}
def_struct! { def_struct! {
TimeUpdate 0x59 { TimeUpdate 0x59 {
/// The age of the world in 1/20ths of a second. /// The age of the world in 1/20ths of a second.
@ -1043,6 +1084,16 @@ pub mod play {
} }
} }
def_struct! {
EntityTeleport 0x62 {
entity_id: VarInt,
position: Vec3<f64>,
yaw: ByteAngle,
pitch: ByteAngle,
on_ground: bool,
}
}
macro_rules! def_client_play_packet_enum { macro_rules! def_client_play_packet_enum {
{ {
$($packet:ident),* $(,)? $($packet:ident),* $(,)?
@ -1115,14 +1166,20 @@ pub mod play {
KeepAliveClientbound, KeepAliveClientbound,
ChunkDataAndUpdateLight, ChunkDataAndUpdateLight,
JoinGame, JoinGame,
EntityPosition,
EntityPositionAndRotation,
EntityRotation,
PlayerPositionAndLook, PlayerPositionAndLook,
DestroyEntities, DestroyEntities,
EntityHeadLook,
MultiBlockChange, MultiBlockChange,
HeldItemChangeClientbound, HeldItemChangeClientbound,
UpdateViewPosition, UpdateViewPosition,
UpdateViewDistance, UpdateViewDistance,
SpawnPosition, SpawnPosition,
EntityMetadata, EntityMetadata,
EntityVelocity,
EntityTeleport,
TimeUpdate, TimeUpdate,
} }

View file

@ -368,7 +368,12 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
}); });
world.clients.par_iter_mut().for_each(|(_, mut client)| { world.clients.par_iter_mut().for_each(|(_, mut client)| {
client.update(&server, &world.entities, &world.chunks, world.dimension); client.update(
&server,
&world.entities,
&world.chunks,
&world.meta,
);
}); });
world.entities.update(); world.entities.update();

View file

@ -1,15 +1,11 @@
use std::collections::{HashMap, HashSet};
use std::iter::FusedIterator; use std::iter::FusedIterator;
use std::ops::Deref; use std::ops::Deref;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::ParallelIterator;
use crate::chunk::ChunkPos;
use crate::config::DimensionId; use crate::config::DimensionId;
use crate::slotmap::{Key, SlotMap}; use crate::slotmap::{Key, SlotMap};
use crate::{ use crate::{Chunks, ChunksMut, Clients, ClientsMut, Entities, EntitiesMut, Server};
Chunks, ChunksMut, Clients, ClientsMut, Entities, EntitiesMut, Entity, EntityId, Server,
};
pub struct Worlds { pub struct Worlds {
sm: SlotMap<World>, sm: SlotMap<World>,
@ -67,7 +63,10 @@ impl<'a> WorldsMut<'a> {
self.server.clone(), self.server.clone(),
(self.server.dimension(dim).height / 16) as u32, (self.server.dimension(dim).height / 16) as u32,
), ),
dimension: dim, meta: WorldMeta {
dimension: dim,
is_flat: false,
},
})) }))
} }
@ -123,7 +122,7 @@ pub(crate) struct World {
clients: Clients, clients: Clients,
entities: Entities, entities: Entities,
chunks: Chunks, chunks: Chunks,
dimension: DimensionId, meta: WorldMeta,
} }
/// A bag of immutable references to the components of a world. /// A bag of immutable references to the components of a world.
@ -131,7 +130,7 @@ pub struct WorldRef<'a> {
pub clients: &'a Clients, pub clients: &'a Clients,
pub entities: &'a Entities, pub entities: &'a Entities,
pub chunks: &'a Chunks, pub chunks: &'a Chunks,
pub dimension: DimensionId, pub meta: &'a WorldMeta,
} }
impl<'a> WorldRef<'a> { impl<'a> WorldRef<'a> {
@ -140,7 +139,7 @@ impl<'a> WorldRef<'a> {
clients: &w.clients, clients: &w.clients,
entities: &w.entities, entities: &w.entities,
chunks: &w.chunks, chunks: &w.chunks,
dimension: w.dimension, meta: &w.meta,
} }
} }
} }
@ -150,7 +149,7 @@ pub struct WorldMut<'a> {
pub clients: ClientsMut<'a>, pub clients: ClientsMut<'a>,
pub entities: EntitiesMut<'a>, pub entities: EntitiesMut<'a>,
pub chunks: ChunksMut<'a>, pub chunks: ChunksMut<'a>,
pub dimension: DimensionId, pub meta: WorldMetaMut<'a>,
} }
impl<'a> WorldMut<'a> { impl<'a> WorldMut<'a> {
@ -159,7 +158,7 @@ impl<'a> WorldMut<'a> {
clients: ClientsMut::new(&mut w.clients), clients: ClientsMut::new(&mut w.clients),
entities: EntitiesMut::new(&mut w.entities), entities: EntitiesMut::new(&mut w.entities),
chunks: ChunksMut::new(&mut w.chunks), chunks: ChunksMut::new(&mut w.chunks),
dimension: w.dimension, meta: WorldMetaMut(&mut w.meta),
} }
} }
@ -168,7 +167,38 @@ impl<'a> WorldMut<'a> {
clients: &self.clients, clients: &self.clients,
entities: &self.entities, entities: &self.entities,
chunks: &self.chunks, chunks: &self.chunks,
dimension: self.dimension, meta: &self.meta,
} }
} }
} }
pub struct WorldMeta {
dimension: DimensionId,
is_flat: bool,
}
impl WorldMeta {
pub fn dimension(&self) -> DimensionId {
self.dimension
}
pub fn is_flat(&self) -> bool {
self.is_flat
}
}
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;
}
}