mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
Get entity movement working
This commit is contained in:
parent
8da32d0b8b
commit
1570c95ac8
|
@ -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) {
|
119
examples/cow_sphere.rs
Normal file
119
examples/cow_sphere.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -418,10 +418,11 @@ fn encode_paletted_container(
|
|||
let palette_idx = palette
|
||||
.iter()
|
||||
.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.encode(w)?;
|
||||
}
|
||||
|
|
114
src/client.rs
114
src/client.rs
|
@ -8,25 +8,29 @@ use uuid::Uuid;
|
|||
use vek::Vec3;
|
||||
|
||||
use crate::block_pos::BlockPos;
|
||||
use crate::byte_angle::ByteAngle;
|
||||
use crate::config::{
|
||||
Biome, BiomeGrassColorModifier, BiomePrecipitation, Dimension, DimensionEffects, DimensionId,
|
||||
};
|
||||
use crate::entity::EntityType;
|
||||
use crate::entity::{velocity_to_packet_units, EntityType};
|
||||
pub use crate::packets::play::GameMode;
|
||||
use crate::packets::play::{
|
||||
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
|
||||
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState,
|
||||
ChangeGameStateReason, ClientPlayPacket, DestroyEntities, DimensionCodec, DimensionType,
|
||||
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, JoinGame, KeepAliveClientbound,
|
||||
PlayerPositionAndLook, PlayerPositionAndLookFlags, ServerPlayPacket, SpawnPosition,
|
||||
UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
|
||||
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame,
|
||||
KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, ServerPlayPacket,
|
||||
SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||
};
|
||||
use crate::protocol::{BoundedInt, Nbt};
|
||||
use crate::server::ServerPacketChannels;
|
||||
use crate::slotmap::{Key, SlotMap};
|
||||
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
|
||||
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 {
|
||||
sm: SlotMap<Client>,
|
||||
|
@ -328,7 +332,7 @@ impl<'a> ClientMut<'a> {
|
|||
server: &Server,
|
||||
entities: &Entities,
|
||||
chunks: &Chunks,
|
||||
dimension_id: DimensionId,
|
||||
meta: &WorldMeta,
|
||||
) {
|
||||
self.0.events.clear();
|
||||
|
||||
|
@ -347,11 +351,13 @@ impl<'a> ClientMut<'a> {
|
|||
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
|
||||
// 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 {
|
||||
entity_id: 0, // EntityId 0 is reserved for clients.
|
||||
is_hardcore: false, // TODO
|
||||
|
@ -363,7 +369,7 @@ impl<'a> ClientMut<'a> {
|
|||
.collect(),
|
||||
dimension_codec: Nbt(make_dimension_codec(server)),
|
||||
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,
|
||||
max_players: VarInt(0),
|
||||
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.
|
||||
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 != server.current_tick() {
|
||||
if self.0.created_tick != current_tick {
|
||||
self.send_packet(UpdateViewDistance {
|
||||
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.
|
||||
if server.current_tick() % (server.tick_rate() * 8) == 0 {
|
||||
if current_tick % (server.tick_rate() * 8) == 0 {
|
||||
if self.0.got_keepalive {
|
||||
let id = rand::random();
|
||||
self.send_packet(KeepAliveClientbound { id });
|
||||
|
@ -451,7 +457,7 @@ impl<'a> ClientMut<'a> {
|
|||
|
||||
if let Some(chunk) = chunks.get(pos) {
|
||||
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) {
|
||||
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"
|
||||
// screen is closed at the appropriate time.
|
||||
|
||||
if self.0.teleported_this_tick {
|
||||
self.0.teleported_this_tick = false;
|
||||
|
||||
|
@ -504,10 +509,90 @@ impl<'a> ClientMut<'a> {
|
|||
// longer visible.
|
||||
self.0.loaded_entities.retain(|&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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -523,6 +608,7 @@ impl<'a> ClientMut<'a> {
|
|||
}
|
||||
|
||||
// Spawn new entities within the view distance.
|
||||
// TODO: use BVH
|
||||
for (id, entity) in entities.iter() {
|
||||
if self.position().distance(entity.position()) <= view_dist as f64 * 16.0
|
||||
&& entity.typ() != EntityType::Marker
|
||||
|
|
|
@ -162,7 +162,10 @@ impl<'a> EntitiesMut<'a> {
|
|||
for (_, e) in self.iter_mut() {
|
||||
e.0.old_position = e.new_position;
|
||||
e.0.meta.clear_modifications();
|
||||
|
||||
let on_ground = e.0.flags.on_ground();
|
||||
e.0.flags = EntityFlags(0);
|
||||
e.0.flags.set_on_ground(on_ground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,11 +207,12 @@ impl<'a> Deref for EntityMut<'a> {
|
|||
/// modified.
|
||||
#[bitfield(u8)]
|
||||
pub(crate) struct EntityFlags {
|
||||
meta_modified: bool,
|
||||
yaw_or_pitch_modified: bool,
|
||||
head_yaw_modified: bool,
|
||||
head_pitch_modified: bool,
|
||||
velocity_modified: bool,
|
||||
/// When the type of this entity changes.
|
||||
pub type_modified: bool,
|
||||
pub yaw_or_pitch_modified: bool,
|
||||
pub head_yaw_modified: bool,
|
||||
pub velocity_modified: bool,
|
||||
pub on_ground: bool,
|
||||
#[bits(3)]
|
||||
_pad: u8,
|
||||
}
|
||||
|
@ -254,16 +258,15 @@ impl Entity {
|
|||
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.
|
||||
pub fn velocity(&self) -> Vec3<f32> {
|
||||
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
|
||||
/// spawned.
|
||||
///
|
||||
|
@ -361,7 +364,7 @@ impl Entity {
|
|||
position: self.new_position,
|
||||
yaw: ByteAngle::from_degrees(self.yaw),
|
||||
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),
|
||||
}))
|
||||
}
|
||||
|
@ -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.
|
||||
(vel * 400.0).as_()
|
||||
}
|
||||
|
@ -528,8 +531,8 @@ impl<'a> EntityMut<'a> {
|
|||
/// 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_meta_modified(true);
|
||||
// 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.
|
||||
|
@ -539,43 +542,40 @@ impl<'a> EntityMut<'a> {
|
|||
|
||||
/// Sets the yaw of this entity (in degrees).
|
||||
pub fn set_yaw(&mut self, yaw: f32) {
|
||||
self.0.yaw = yaw;
|
||||
if ByteAngle::from_degrees(self.yaw) != ByteAngle::from_degrees(yaw) {
|
||||
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) {
|
||||
self.0.pitch = pitch;
|
||||
if ByteAngle::from_degrees(self.pitch) != ByteAngle::from_degrees(pitch) {
|
||||
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) {
|
||||
self.0.head_yaw = head_yaw;
|
||||
if ByteAngle::from_degrees(self.head_yaw) != ByteAngle::from_degrees(head_yaw) {
|
||||
if self.0.head_yaw != head_yaw {
|
||||
self.0.head_yaw = head_yaw;
|
||||
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>>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_on_ground(&mut self, on_ground: bool) {
|
||||
self.0.flags.set_on_ground(on_ground);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum EntitySpawnPacket {
|
||||
|
|
|
@ -36,7 +36,7 @@ pub use identifier::Identifier;
|
|||
pub use server::{start_server, NewClientData, Server, ShutdownResult};
|
||||
pub use text::{Text, TextFormat};
|
||||
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};
|
||||
|
||||
/// The Minecraft protocol version that this library targets.
|
||||
|
|
|
@ -13,7 +13,7 @@ use num::{One, Zero};
|
|||
use paste::paste;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use vek::{Vec2, Vec3};
|
||||
use vek::Vec3;
|
||||
|
||||
use crate::block_pos::BlockPos;
|
||||
use crate::byte_angle::ByteAngle;
|
||||
|
@ -469,7 +469,7 @@ pub mod play {
|
|||
position: Vec3<f64>,
|
||||
yaw: ByteAngle,
|
||||
pitch: ByteAngle,
|
||||
head_pitch: ByteAngle,
|
||||
head_yaw: ByteAngle,
|
||||
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! {
|
||||
PlayerPositionAndLook 0x38 {
|
||||
position: Vec3<f64>,
|
||||
|
@ -991,6 +1018,13 @@ pub mod play {
|
|||
}
|
||||
}
|
||||
|
||||
def_struct! {
|
||||
EntityHeadLook 0x3e {
|
||||
entity_id: VarInt,
|
||||
head_yaw: ByteAngle,
|
||||
}
|
||||
}
|
||||
|
||||
def_struct! {
|
||||
MultiBlockChange 0x3f {
|
||||
chunk_section_position: u64,
|
||||
|
@ -1032,6 +1066,13 @@ pub mod play {
|
|||
}
|
||||
}
|
||||
|
||||
def_struct! {
|
||||
EntityVelocity 0x4f {
|
||||
entity_id: VarInt,
|
||||
velocity: Vec3<i16>,
|
||||
}
|
||||
}
|
||||
|
||||
def_struct! {
|
||||
TimeUpdate 0x59 {
|
||||
/// 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 {
|
||||
{
|
||||
$($packet:ident),* $(,)?
|
||||
|
@ -1115,14 +1166,20 @@ pub mod play {
|
|||
KeepAliveClientbound,
|
||||
ChunkDataAndUpdateLight,
|
||||
JoinGame,
|
||||
EntityPosition,
|
||||
EntityPositionAndRotation,
|
||||
EntityRotation,
|
||||
PlayerPositionAndLook,
|
||||
DestroyEntities,
|
||||
EntityHeadLook,
|
||||
MultiBlockChange,
|
||||
HeldItemChangeClientbound,
|
||||
UpdateViewPosition,
|
||||
UpdateViewDistance,
|
||||
SpawnPosition,
|
||||
EntityMetadata,
|
||||
EntityVelocity,
|
||||
EntityTeleport,
|
||||
TimeUpdate,
|
||||
}
|
||||
|
||||
|
|
|
@ -368,7 +368,12 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
|||
});
|
||||
|
||||
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();
|
||||
|
|
56
src/world.rs
56
src/world.rs
|
@ -1,15 +1,11 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::iter::FusedIterator;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use rayon::iter::ParallelIterator;
|
||||
|
||||
use crate::chunk::ChunkPos;
|
||||
use crate::config::DimensionId;
|
||||
use crate::slotmap::{Key, SlotMap};
|
||||
use crate::{
|
||||
Chunks, ChunksMut, Clients, ClientsMut, Entities, EntitiesMut, Entity, EntityId, Server,
|
||||
};
|
||||
use crate::{Chunks, ChunksMut, Clients, ClientsMut, Entities, EntitiesMut, Server};
|
||||
|
||||
pub struct Worlds {
|
||||
sm: SlotMap<World>,
|
||||
|
@ -67,7 +63,10 @@ impl<'a> WorldsMut<'a> {
|
|||
self.server.clone(),
|
||||
(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,
|
||||
entities: Entities,
|
||||
chunks: Chunks,
|
||||
dimension: DimensionId,
|
||||
meta: WorldMeta,
|
||||
}
|
||||
|
||||
/// A bag of immutable references to the components of a world.
|
||||
|
@ -131,7 +130,7 @@ pub struct WorldRef<'a> {
|
|||
pub clients: &'a Clients,
|
||||
pub entities: &'a Entities,
|
||||
pub chunks: &'a Chunks,
|
||||
pub dimension: DimensionId,
|
||||
pub meta: &'a WorldMeta,
|
||||
}
|
||||
|
||||
impl<'a> WorldRef<'a> {
|
||||
|
@ -140,7 +139,7 @@ impl<'a> WorldRef<'a> {
|
|||
clients: &w.clients,
|
||||
entities: &w.entities,
|
||||
chunks: &w.chunks,
|
||||
dimension: w.dimension,
|
||||
meta: &w.meta,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +149,7 @@ pub struct WorldMut<'a> {
|
|||
pub clients: ClientsMut<'a>,
|
||||
pub entities: EntitiesMut<'a>,
|
||||
pub chunks: ChunksMut<'a>,
|
||||
pub dimension: DimensionId,
|
||||
pub meta: WorldMetaMut<'a>,
|
||||
}
|
||||
|
||||
impl<'a> WorldMut<'a> {
|
||||
|
@ -159,7 +158,7 @@ impl<'a> WorldMut<'a> {
|
|||
clients: ClientsMut::new(&mut w.clients),
|
||||
entities: EntitiesMut::new(&mut w.entities),
|
||||
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,
|
||||
entities: &self.entities,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue