mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Clean up client and fix names
This commit is contained in:
parent
6b5e795f81
commit
49d63a39c0
|
@ -44,11 +44,11 @@ pub fn build() -> anyhow::Result<TokenStream> {
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum Event {
|
pub enum EntityEvent {
|
||||||
#(#event_variants,)*
|
#(#event_variants,)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl EntityEvent {
|
||||||
pub(crate) fn status_or_animation(self) -> StatusOrAnimation {
|
pub(crate) fn status_or_animation(self) -> StatusOrAnimation {
|
||||||
match self {
|
match self {
|
||||||
#(#status_arms)*
|
#(#status_arms)*
|
||||||
|
|
|
@ -16,11 +16,13 @@ pub fn main() -> anyhow::Result<()> {
|
||||||
(block::build, "block.rs"),
|
(block::build, "block.rs"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
for (_, file_name) in generators {
|
||||||
|
println!("cargo:rerun-if-changed=extracted/{file_name}");
|
||||||
|
}
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").context("can't get OUT_DIR env var")?;
|
let out_dir = env::var_os("OUT_DIR").context("can't get OUT_DIR env var")?;
|
||||||
|
|
||||||
for (g, file_name) in generators {
|
for (g, file_name) in generators {
|
||||||
println!("cargo:rerun-if-changed=extracted/{file_name}");
|
|
||||||
|
|
||||||
let path = Path::new(&out_dir).join(file_name);
|
let path = Path::new(&out_dir).join(file_name);
|
||||||
let code = g()?.to_string();
|
let code = g()?.to_string();
|
||||||
fs::write(&path, &code)?;
|
fs::write(&path, &code)?;
|
||||||
|
|
|
@ -3,15 +3,15 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use valence::block::{BlockPos, BlockState};
|
use valence::block::{BlockPos, BlockState};
|
||||||
use valence::client::{ClientId, Event, GameMode, Hand, InteractWithEntityKind};
|
use valence::client::{Client, ClientEvent, ClientId, GameMode, Hand, InteractWithEntityKind};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::DimensionId;
|
use valence::dimension::DimensionId;
|
||||||
use valence::entity::data::Pose;
|
use valence::entity::types::Pose;
|
||||||
use valence::entity::{TrackedData, EntityId, EntityKind, Event as EntityEvent};
|
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::{async_trait, Ticks};
|
use valence::{async_trait, Ticks};
|
||||||
use vek::Vec3;
|
use vek::{Vec2, Vec3};
|
||||||
|
|
||||||
pub fn main() -> ShutdownResult {
|
pub fn main() -> ShutdownResult {
|
||||||
env_logger::Builder::new()
|
env_logger::Builder::new()
|
||||||
|
@ -119,7 +119,7 @@ impl Config for Game {
|
||||||
let current_tick = server.shared.current_tick();
|
let current_tick = server.shared.current_tick();
|
||||||
|
|
||||||
server.clients.retain(|client_id, client| {
|
server.clients.retain(|client_id, client| {
|
||||||
if client.created_tick() == current_tick {
|
if client.created_this_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
@ -131,6 +131,23 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (player_id, player) = match server.entities.create_with_uuid(
|
||||||
|
EntityKind::Player,
|
||||||
|
client.uuid(),
|
||||||
|
EntityState::default(),
|
||||||
|
) {
|
||||||
|
Some(e) => e,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
player.state.client = client_id;
|
||||||
|
|
||||||
|
client.state.player = player_id;
|
||||||
|
client.state.extra_knockback = true;
|
||||||
|
|
||||||
client.spawn(world_id);
|
client.spawn(world_id);
|
||||||
client.set_game_mode(GameMode::Survival);
|
client.set_game_mode(GameMode::Survival);
|
||||||
client.teleport(
|
client.teleport(
|
||||||
|
@ -152,17 +169,6 @@ impl Config for Game {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (player_id, player) = server
|
|
||||||
.entities
|
|
||||||
.create_with_uuid(EntityKind::Player, client.uuid(), EntityState::default())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
client.state.player = player_id;
|
|
||||||
client.state.extra_knockback = true;
|
|
||||||
|
|
||||||
player.state.client = client_id;
|
|
||||||
player.state.last_attack_time = 0;
|
|
||||||
|
|
||||||
client.send_message("Welcome to the arena.".italic());
|
client.send_message("Welcome to the arena.".italic());
|
||||||
if self.player_count.load(Ordering::SeqCst) <= 1 {
|
if self.player_count.load(Ordering::SeqCst) <= 1 {
|
||||||
client.send_message("Have another player join the game with you.".italic());
|
client.send_message("Have another player join the game with you.".italic());
|
||||||
|
@ -176,41 +182,6 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(event) = client.pop_event() {
|
|
||||||
match event {
|
|
||||||
Event::StartSprinting => {
|
|
||||||
client.state.extra_knockback = true;
|
|
||||||
}
|
|
||||||
Event::InteractWithEntity {
|
|
||||||
id,
|
|
||||||
kind: InteractWithEntityKind::Attack,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if let Some(target) = server.entities.get_mut(id) {
|
|
||||||
if !target.state.attacked
|
|
||||||
&& current_tick - target.state.last_attack_time >= 10
|
|
||||||
&& id != client.state.player
|
|
||||||
{
|
|
||||||
target.state.attacked = true;
|
|
||||||
target.state.attacker_pos = client.position();
|
|
||||||
target.state.extra_knockback = client.state.extra_knockback;
|
|
||||||
target.state.last_attack_time = current_tick;
|
|
||||||
|
|
||||||
client.state.extra_knockback = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::ArmSwing(hand) => {
|
|
||||||
let player = server.entities.get_mut(client.state.player).unwrap();
|
|
||||||
match hand {
|
|
||||||
Hand::Main => player.trigger_event(EntityEvent::SwingMainHand),
|
|
||||||
Hand::Off => player.trigger_event(EntityEvent::SwingOffHand),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if client.position().y <= 0.0 {
|
if client.position().y <= 0.0 {
|
||||||
client.teleport(
|
client.teleport(
|
||||||
[
|
[
|
||||||
|
@ -223,53 +194,189 @@ impl Config for Game {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let player = server.entities.get_mut(client.state.player).unwrap();
|
loop {
|
||||||
|
let player = server
|
||||||
|
.entities
|
||||||
|
.get_mut(client.state.player)
|
||||||
|
.expect("missing player entity");
|
||||||
|
|
||||||
player.set_world(client.world());
|
match client_event_boilerplate(client, player) {
|
||||||
player.set_position(client.position());
|
Some(ClientEvent::StartSprinting) => {
|
||||||
player.set_yaw(client.yaw());
|
client.state.extra_knockback = true;
|
||||||
player.set_head_yaw(client.yaw());
|
}
|
||||||
player.set_pitch(client.pitch());
|
Some(ClientEvent::InteractWithEntity {
|
||||||
player.set_on_ground(client.on_ground());
|
id,
|
||||||
|
kind: InteractWithEntityKind::Attack,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if let Some(target) = server.entities.get_mut(id) {
|
||||||
|
if !target.state.attacked
|
||||||
|
&& current_tick - target.state.last_attack_time >= 10
|
||||||
|
&& id != client.state.player
|
||||||
|
{
|
||||||
|
target.state.attacked = true;
|
||||||
|
target.state.attacker_pos = client.position();
|
||||||
|
target.state.extra_knockback = client.state.extra_knockback;
|
||||||
|
target.state.last_attack_time = current_tick;
|
||||||
|
|
||||||
if let TrackedData::Player(player) = player.view_mut() {
|
client.state.extra_knockback = false;
|
||||||
if client.is_sneaking() {
|
}
|
||||||
player.set_pose(Pose::Sneaking);
|
}
|
||||||
} else {
|
}
|
||||||
player.set_pose(Pose::Standing);
|
Some(_) => {}
|
||||||
|
None => break,
|
||||||
}
|
}
|
||||||
|
|
||||||
player.set_sprinting(client.is_sprinting());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
for (_, e) in server.entities.iter_mut() {
|
for (_, entity) in server.entities.iter_mut() {
|
||||||
if e.state.attacked {
|
if entity.state.attacked {
|
||||||
e.state.attacked = false;
|
entity.state.attacked = false;
|
||||||
let victim = server.clients.get_mut(e.state.client).unwrap();
|
if let Some(victim) = server.clients.get_mut(entity.state.client) {
|
||||||
|
let victim_pos = Vec2::new(victim.position().x, victim.position().z);
|
||||||
|
let attacker_pos =
|
||||||
|
Vec2::new(entity.state.attacker_pos.x, entity.state.attacker_pos.z);
|
||||||
|
|
||||||
let mut vel = (victim.position() - e.state.attacker_pos).normalized();
|
let dir = (victim_pos - attacker_pos).normalized();
|
||||||
|
|
||||||
let knockback_xz = if e.state.extra_knockback { 18.0 } else { 8.0 };
|
let knockback_xz = if entity.state.extra_knockback {
|
||||||
let knockback_y = if e.state.extra_knockback {
|
18.0
|
||||||
8.432
|
} else {
|
||||||
} else {
|
8.0
|
||||||
6.432
|
};
|
||||||
};
|
let knockback_y = if entity.state.extra_knockback {
|
||||||
|
8.432
|
||||||
|
} else {
|
||||||
|
6.432
|
||||||
|
};
|
||||||
|
|
||||||
vel.x *= knockback_xz;
|
let vel = Vec3::new(dir.x * knockback_xz, knockback_y, dir.y * knockback_xz);
|
||||||
vel.y = knockback_y;
|
victim.set_velocity(vel.as_());
|
||||||
vel.z *= knockback_xz;
|
|
||||||
|
|
||||||
victim.set_velocity(victim.velocity() / 2.0 + vel.as_());
|
entity.push_event(EntityEvent::DamageFromGenericSource);
|
||||||
|
entity.push_event(EntityEvent::Damage);
|
||||||
e.trigger_event(EntityEvent::DamageFromGenericSource);
|
victim.push_entity_event(EntityEvent::DamageFromGenericSource);
|
||||||
e.trigger_event(EntityEvent::Damage);
|
victim.push_entity_event(EntityEvent::Damage);
|
||||||
victim.trigger_entity_event(EntityEvent::DamageFromGenericSource);
|
}
|
||||||
victim.trigger_entity_event(EntityEvent::Damage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_event_boilerplate(
|
||||||
|
client: &mut Client<Game>,
|
||||||
|
entity: &mut Entity<Game>,
|
||||||
|
) -> Option<ClientEvent> {
|
||||||
|
let event = client.pop_event()?;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
ClientEvent::ChatMessage { .. } => {}
|
||||||
|
ClientEvent::SettingsChanged {
|
||||||
|
view_distance,
|
||||||
|
main_hand,
|
||||||
|
displayed_skin_parts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
client.set_view_distance(*view_distance);
|
||||||
|
|
||||||
|
let player = client.player_mut();
|
||||||
|
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::MovePosition {
|
||||||
|
position,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MovePositionAndRotation {
|
||||||
|
position,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveRotation {
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveOnGround { on_ground } => {
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveVehicle { .. } => {}
|
||||||
|
ClientEvent::StartSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Standing {
|
||||||
|
player.set_pose(Pose::Sneaking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Sneaking {
|
||||||
|
player.set_pose(Pose::Standing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartJumpWithHorse { .. } => {}
|
||||||
|
ClientEvent::StopJumpWithHorse => {}
|
||||||
|
ClientEvent::LeaveBed => {}
|
||||||
|
ClientEvent::OpenHorseInventory => {}
|
||||||
|
ClientEvent::StartFlyingWithElytra => {}
|
||||||
|
ClientEvent::ArmSwing(hand) => {
|
||||||
|
entity.push_event(match hand {
|
||||||
|
Hand::Main => EntityEvent::SwingMainHand,
|
||||||
|
Hand::Off => EntityEvent::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientEvent::InteractWithEntity { .. } => {}
|
||||||
|
ClientEvent::SteerBoat { .. } => {}
|
||||||
|
ClientEvent::Digging { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.set_world(client.world());
|
||||||
|
|
||||||
|
Some(event)
|
||||||
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ use num::Integer;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use valence::biome::Biome;
|
use valence::biome::Biome;
|
||||||
use valence::block::BlockState;
|
use valence::block::BlockState;
|
||||||
use valence::client::{Event, Hand};
|
use valence::client::{Client, ClientEvent, Hand};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::{Dimension, DimensionId};
|
use valence::dimension::{Dimension, DimensionId};
|
||||||
use valence::entity::data::Pose;
|
use valence::entity::types::Pose;
|
||||||
use valence::entity::{TrackedData, EntityId, EntityKind, Event as EntityEvent};
|
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::{async_trait, ident};
|
use valence::{async_trait, ident};
|
||||||
|
@ -42,12 +42,6 @@ struct ServerState {
|
||||||
board_buf: Box<[bool]>,
|
board_buf: Box<[bool]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ClientState {
|
|
||||||
/// The client's player entity.
|
|
||||||
player: EntityId,
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_PLAYERS: usize = 10;
|
const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
const SIZE_X: usize = 100;
|
const SIZE_X: usize = 100;
|
||||||
|
@ -57,7 +51,7 @@ const BOARD_Y: i32 = 50;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
type ChunkState = ();
|
type ChunkState = ();
|
||||||
type ClientState = ClientState;
|
type ClientState = EntityId;
|
||||||
type EntityState = ();
|
type EntityState = ();
|
||||||
type ServerState = ServerState;
|
type ServerState = ServerState;
|
||||||
type WorldState = ();
|
type WorldState = ();
|
||||||
|
@ -121,7 +115,7 @@ impl Config for Game {
|
||||||
];
|
];
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_this_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
@ -133,6 +127,17 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match server
|
||||||
|
.entities
|
||||||
|
.create_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||||
|
{
|
||||||
|
Some((id, _)) => client.state = id,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.spawn(world_id);
|
client.spawn(world_id);
|
||||||
client.teleport(spawn_pos, 0.0, 0.0);
|
client.teleport(spawn_pos, 0.0, 0.0);
|
||||||
|
|
||||||
|
@ -145,65 +150,35 @@ impl Config for Game {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
client.state.player = server
|
|
||||||
.entities
|
|
||||||
.create_with_uuid(EntityKind::Player, client.uuid(), ())
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
|
|
||||||
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
client.send_message("Welcome to Conway's game of life in Minecraft!".italic());
|
||||||
client.send_message("Hold the left mouse button to bring blocks to life.".italic());
|
client.send_message("Hold the left mouse button to bring blocks to life.".italic());
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
server.entities.delete(client.state.player);
|
server.entities.delete(client.state);
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let player = server.entities.get_mut(client.state.player).unwrap();
|
let player = server.entities.get_mut(client.state).unwrap();
|
||||||
|
|
||||||
if client.position().y <= 0.0 {
|
if client.position().y <= 0.0 {
|
||||||
client.teleport(spawn_pos, client.yaw(), client.pitch());
|
client.teleport(spawn_pos, client.yaw(), client.pitch());
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(event) = client.pop_event() {
|
while let Some(event) = client_event_boilerplate(client, player) {
|
||||||
match event {
|
if let ClientEvent::Digging { position, .. } = event {
|
||||||
Event::Digging { position, .. } => {
|
if (0..SIZE_X as i32).contains(&position.x)
|
||||||
if (0..SIZE_X as i32).contains(&position.x)
|
&& (0..SIZE_Z as i32).contains(&position.z)
|
||||||
&& (0..SIZE_Z as i32).contains(&position.z)
|
&& position.y == BOARD_Y
|
||||||
&& position.y == BOARD_Y
|
{
|
||||||
{
|
server.state.board[position.x as usize + position.z as usize * SIZE_X] =
|
||||||
server.state.board
|
true;
|
||||||
[position.x as usize + position.z as usize * SIZE_X] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Event::ArmSwing(hand) => match hand {
|
|
||||||
Hand::Main => player.trigger_event(EntityEvent::SwingMainHand),
|
|
||||||
Hand::Off => player.trigger_event(EntityEvent::SwingOffHand),
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player.set_world(client.world());
|
|
||||||
player.set_position(client.position());
|
|
||||||
player.set_yaw(client.yaw());
|
|
||||||
player.set_head_yaw(client.yaw());
|
|
||||||
player.set_pitch(client.pitch());
|
|
||||||
player.set_on_ground(client.on_ground());
|
|
||||||
|
|
||||||
if let TrackedData::Player(player) = player.view_mut() {
|
|
||||||
if client.is_sneaking() {
|
|
||||||
player.set_pose(Pose::Sneaking);
|
|
||||||
} else {
|
|
||||||
player.set_pose(Pose::Standing);
|
|
||||||
}
|
|
||||||
|
|
||||||
player.set_sprinting(client.is_sprinting());
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -269,3 +244,119 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_event_boilerplate(
|
||||||
|
client: &mut Client<Game>,
|
||||||
|
entity: &mut Entity<Game>,
|
||||||
|
) -> Option<ClientEvent> {
|
||||||
|
let event = client.pop_event()?;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
ClientEvent::ChatMessage { .. } => {}
|
||||||
|
ClientEvent::SettingsChanged {
|
||||||
|
view_distance,
|
||||||
|
main_hand,
|
||||||
|
displayed_skin_parts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
client.set_view_distance(*view_distance);
|
||||||
|
|
||||||
|
let player = client.player_mut();
|
||||||
|
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::MovePosition {
|
||||||
|
position,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MovePositionAndRotation {
|
||||||
|
position,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveRotation {
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveOnGround { on_ground } => {
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveVehicle { .. } => {}
|
||||||
|
ClientEvent::StartSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Standing {
|
||||||
|
player.set_pose(Pose::Sneaking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Sneaking {
|
||||||
|
player.set_pose(Pose::Standing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartJumpWithHorse { .. } => {}
|
||||||
|
ClientEvent::StopJumpWithHorse => {}
|
||||||
|
ClientEvent::LeaveBed => {}
|
||||||
|
ClientEvent::OpenHorseInventory => {}
|
||||||
|
ClientEvent::StartFlyingWithElytra => {}
|
||||||
|
ClientEvent::ArmSwing(hand) => {
|
||||||
|
entity.push_event(match hand {
|
||||||
|
Hand::Main => EntityEvent::SwingMainHand,
|
||||||
|
Hand::Off => EntityEvent::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientEvent::InteractWithEntity { .. } => {}
|
||||||
|
ClientEvent::SteerBoat { .. } => {}
|
||||||
|
ClientEvent::Digging { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.set_world(client.world());
|
||||||
|
|
||||||
|
Some(event)
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,11 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use valence::async_trait;
|
use valence::async_trait;
|
||||||
use valence::block::{BlockPos, BlockState};
|
use valence::block::{BlockPos, BlockState};
|
||||||
use valence::client::GameMode;
|
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::DimensionId;
|
use valence::dimension::DimensionId;
|
||||||
use valence::entity::{EntityId, EntityKind};
|
use valence::entity::types::Pose;
|
||||||
|
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::util::to_yaw_and_pitch;
|
use valence::util::to_yaw_and_pitch;
|
||||||
|
@ -43,7 +44,7 @@ const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -25);
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
type ChunkState = ();
|
type ChunkState = ();
|
||||||
type ClientState = ();
|
type ClientState = EntityId;
|
||||||
type EntityState = ();
|
type EntityState = ();
|
||||||
type ServerState = ServerState;
|
type ServerState = ServerState;
|
||||||
type WorldState = ();
|
type WorldState = ();
|
||||||
|
@ -92,10 +93,10 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, server: &mut Server<Self>) {
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().expect("missing world");
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_this_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
@ -107,6 +108,17 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match server
|
||||||
|
.entities
|
||||||
|
.create_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||||
|
{
|
||||||
|
Some((id, _)) => client.state = id,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.spawn(world_id);
|
client.spawn(world_id);
|
||||||
client.set_game_mode(GameMode::Creative);
|
client.set_game_mode(GameMode::Creative);
|
||||||
client.teleport(
|
client.teleport(
|
||||||
|
@ -132,9 +144,18 @@ impl Config for Game {
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
|
server.entities.delete(client.state);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let entity = server
|
||||||
|
.entities
|
||||||
|
.get_mut(client.state)
|
||||||
|
.expect("missing player entity");
|
||||||
|
|
||||||
|
while client_event_boilerplate(client, entity).is_some() {}
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,7 +174,7 @@ impl Config for Game {
|
||||||
.map(|c| c.1.position())
|
.map(|c| c.1.position())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// TODO: hardcoded eye pos.
|
// TODO: remove hardcoded eye pos.
|
||||||
let eye_pos = Vec3::new(player_pos.x, player_pos.y + 1.6, player_pos.z);
|
let eye_pos = Vec3::new(player_pos.x, player_pos.y + 1.6, player_pos.z);
|
||||||
|
|
||||||
for (cow_id, p) in server
|
for (cow_id, p) in server
|
||||||
|
@ -194,3 +215,119 @@ fn fibonacci_spiral(n: usize) -> impl Iterator<Item = Vec3<f64>> {
|
||||||
Vec3::new(theta.cos() * phi.sin(), theta.sin() * phi.sin(), phi.cos())
|
Vec3::new(theta.cos() * phi.sin(), theta.sin() * phi.sin(), phi.cos())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_event_boilerplate(
|
||||||
|
client: &mut Client<Game>,
|
||||||
|
entity: &mut Entity<Game>,
|
||||||
|
) -> Option<ClientEvent> {
|
||||||
|
let event = client.pop_event()?;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
ClientEvent::ChatMessage { .. } => {}
|
||||||
|
ClientEvent::SettingsChanged {
|
||||||
|
view_distance,
|
||||||
|
main_hand,
|
||||||
|
displayed_skin_parts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
client.set_view_distance(*view_distance);
|
||||||
|
|
||||||
|
let player = client.player_mut();
|
||||||
|
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::MovePosition {
|
||||||
|
position,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MovePositionAndRotation {
|
||||||
|
position,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveRotation {
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveOnGround { on_ground } => {
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveVehicle { .. } => {}
|
||||||
|
ClientEvent::StartSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Standing {
|
||||||
|
player.set_pose(Pose::Sneaking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Sneaking {
|
||||||
|
player.set_pose(Pose::Standing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartJumpWithHorse { .. } => {}
|
||||||
|
ClientEvent::StopJumpWithHorse => {}
|
||||||
|
ClientEvent::LeaveBed => {}
|
||||||
|
ClientEvent::OpenHorseInventory => {}
|
||||||
|
ClientEvent::StartFlyingWithElytra => {}
|
||||||
|
ClientEvent::ArmSwing(hand) => {
|
||||||
|
entity.push_event(match hand {
|
||||||
|
Hand::Main => EntityEvent::SwingMainHand,
|
||||||
|
Hand::Off => EntityEvent::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientEvent::InteractWithEntity { .. } => {}
|
||||||
|
ClientEvent::SteerBoat { .. } => {}
|
||||||
|
ClientEvent::Digging { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.set_world(client.world());
|
||||||
|
|
||||||
|
Some(event)
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use valence::async_trait;
|
use valence::async_trait;
|
||||||
use valence::block::{BlockPos, BlockState};
|
use valence::block::{BlockPos, BlockState};
|
||||||
use valence::client::GameMode;
|
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::DimensionId;
|
use valence::dimension::DimensionId;
|
||||||
use valence::entity::{TrackedData, EntityKind};
|
use valence::entity::types::Pose;
|
||||||
|
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::spatial_index::RaycastHit;
|
use valence::spatial_index::RaycastHit;
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
|
@ -41,7 +42,7 @@ const PLAYER_EYE_HEIGHT: f64 = 1.6;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
type ChunkState = ();
|
type ChunkState = ();
|
||||||
type ClientState = ();
|
type ClientState = EntityId;
|
||||||
/// `true` for entities that have been intersected with.
|
/// `true` for entities that have been intersected with.
|
||||||
type EntityState = bool;
|
type EntityState = bool;
|
||||||
type ServerState = ();
|
type ServerState = ();
|
||||||
|
@ -99,7 +100,7 @@ impl Config for Game {
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_this_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
@ -111,6 +112,17 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match server
|
||||||
|
.entities
|
||||||
|
.create_with_uuid(EntityKind::Player, client.uuid(), false)
|
||||||
|
{
|
||||||
|
Some((id, _)) => client.state = id,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.spawn(world_id);
|
client.spawn(world_id);
|
||||||
client.set_game_mode(GameMode::Creative);
|
client.set_game_mode(GameMode::Creative);
|
||||||
client.teleport(
|
client.teleport(
|
||||||
|
@ -142,6 +154,8 @@ impl Config for Game {
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
|
server.entities.delete(client.state);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,12 +176,16 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while client_event_boilerplate(client, server.entities.get_mut(client.state).unwrap())
|
||||||
|
.is_some()
|
||||||
|
{}
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
for (_, e) in server.entities.iter_mut() {
|
for (_, e) in server.entities.iter_mut() {
|
||||||
let intersected = e.state;
|
let intersected = e.state;
|
||||||
if let TrackedData::Sheep(sheep) = &mut e.view_mut() {
|
if let TrackedData::Sheep(sheep) = &mut e.data_mut() {
|
||||||
if intersected {
|
if intersected {
|
||||||
sheep.set_color(5);
|
sheep.set_color(5);
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,3 +196,119 @@ impl Config for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_event_boilerplate(
|
||||||
|
client: &mut Client<Game>,
|
||||||
|
entity: &mut Entity<Game>,
|
||||||
|
) -> Option<ClientEvent> {
|
||||||
|
let event = client.pop_event()?;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
ClientEvent::ChatMessage { .. } => {}
|
||||||
|
ClientEvent::SettingsChanged {
|
||||||
|
view_distance,
|
||||||
|
main_hand,
|
||||||
|
displayed_skin_parts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
client.set_view_distance(*view_distance);
|
||||||
|
|
||||||
|
let player = client.player_mut();
|
||||||
|
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::MovePosition {
|
||||||
|
position,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MovePositionAndRotation {
|
||||||
|
position,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveRotation {
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveOnGround { on_ground } => {
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveVehicle { .. } => {}
|
||||||
|
ClientEvent::StartSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Standing {
|
||||||
|
player.set_pose(Pose::Sneaking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Sneaking {
|
||||||
|
player.set_pose(Pose::Standing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartJumpWithHorse { .. } => {}
|
||||||
|
ClientEvent::StopJumpWithHorse => {}
|
||||||
|
ClientEvent::LeaveBed => {}
|
||||||
|
ClientEvent::OpenHorseInventory => {}
|
||||||
|
ClientEvent::StartFlyingWithElytra => {}
|
||||||
|
ClientEvent::ArmSwing(hand) => {
|
||||||
|
entity.push_event(match hand {
|
||||||
|
Hand::Main => EntityEvent::SwingMainHand,
|
||||||
|
Hand::Off => EntityEvent::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientEvent::InteractWithEntity { .. } => {}
|
||||||
|
ClientEvent::SteerBoat { .. } => {}
|
||||||
|
ClientEvent::Digging { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.set_world(client.world());
|
||||||
|
|
||||||
|
Some(event)
|
||||||
|
}
|
||||||
|
|
|
@ -8,9 +8,11 @@ use rayon::iter::ParallelIterator;
|
||||||
use valence::async_trait;
|
use valence::async_trait;
|
||||||
use valence::block::{BlockState, PropName, PropValue};
|
use valence::block::{BlockState, PropName, PropValue};
|
||||||
use valence::chunk::ChunkPos;
|
use valence::chunk::ChunkPos;
|
||||||
use valence::client::GameMode;
|
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||||
use valence::config::{Config, ServerListPing};
|
use valence::config::{Config, ServerListPing};
|
||||||
use valence::dimension::DimensionId;
|
use valence::dimension::DimensionId;
|
||||||
|
use valence::entity::types::Pose;
|
||||||
|
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
use valence::text::{Color, TextFormat};
|
use valence::text::{Color, TextFormat};
|
||||||
use valence::util::chunks_in_view_distance;
|
use valence::util::chunks_in_view_distance;
|
||||||
|
@ -51,7 +53,7 @@ const MAX_PLAYERS: usize = 10;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Config for Game {
|
impl Config for Game {
|
||||||
type ChunkState = ();
|
type ChunkState = ();
|
||||||
type ClientState = ();
|
type ClientState = EntityId;
|
||||||
type EntityState = ();
|
type EntityState = ();
|
||||||
type ServerState = ();
|
type ServerState = ();
|
||||||
type WorldState = ();
|
type WorldState = ();
|
||||||
|
@ -90,7 +92,7 @@ impl Config for Game {
|
||||||
let mut chunks_to_unload = HashSet::<_>::from_iter(world.chunks.iter().map(|t| t.0));
|
let mut chunks_to_unload = HashSet::<_>::from_iter(world.chunks.iter().map(|t| t.0));
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
server.clients.retain(|_, client| {
|
||||||
if client.created_tick() == server.shared.current_tick() {
|
if client.created_this_tick() {
|
||||||
if self
|
if self
|
||||||
.player_count
|
.player_count
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
@ -102,9 +104,19 @@ impl Config for Game {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match server
|
||||||
|
.entities
|
||||||
|
.create_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||||
|
{
|
||||||
|
Some((id, _)) => client.state = id,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.spawn(world_id);
|
client.spawn(world_id);
|
||||||
client.set_game_mode(GameMode::Creative);
|
client.set_game_mode(GameMode::Creative);
|
||||||
client.set_max_view_distance(32);
|
|
||||||
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
||||||
|
|
||||||
world.meta.player_list_mut().insert(
|
world.meta.player_list_mut().insert(
|
||||||
|
@ -117,16 +129,20 @@ impl Config for Game {
|
||||||
);
|
);
|
||||||
|
|
||||||
client.send_message("Welcome to the terrain example!".italic());
|
client.send_message("Welcome to the terrain example!".italic());
|
||||||
client
|
|
||||||
.send_message("Explore this infinite procedurally generated terrain.".italic());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if client.is_disconnected() {
|
if client.is_disconnected() {
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
world.meta.player_list_mut().remove(client.uuid());
|
world.meta.player_list_mut().remove(client.uuid());
|
||||||
|
server.entities.delete(client.state);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(entity) = server.entities.get_mut(client.state) {
|
||||||
|
while client_event_boilerplate(client, entity).is_some() {}
|
||||||
|
}
|
||||||
|
|
||||||
let dist = client.view_distance();
|
let dist = client.view_distance();
|
||||||
let p = client.position();
|
let p = client.position();
|
||||||
|
|
||||||
|
@ -321,3 +337,119 @@ fn fbm(noise: &SuperSimplex, p: [f64; 3], octaves: u32, lacunarity: f64, persist
|
||||||
fn noise01(noise: &SuperSimplex, xyz: [f64; 3]) -> f64 {
|
fn noise01(noise: &SuperSimplex, xyz: [f64; 3]) -> f64 {
|
||||||
(noise.get(xyz) + 1.0) / 2.0
|
(noise.get(xyz) + 1.0) / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_event_boilerplate(
|
||||||
|
client: &mut Client<Game>,
|
||||||
|
entity: &mut Entity<Game>,
|
||||||
|
) -> Option<ClientEvent> {
|
||||||
|
let event = client.pop_event()?;
|
||||||
|
|
||||||
|
match &event {
|
||||||
|
ClientEvent::ChatMessage { .. } => {}
|
||||||
|
ClientEvent::SettingsChanged {
|
||||||
|
view_distance,
|
||||||
|
main_hand,
|
||||||
|
displayed_skin_parts,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
client.set_view_distance(*view_distance);
|
||||||
|
|
||||||
|
let player = client.player_mut();
|
||||||
|
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_cape(displayed_skin_parts.cape());
|
||||||
|
player.set_jacket(displayed_skin_parts.jacket());
|
||||||
|
player.set_left_sleeve(displayed_skin_parts.left_sleeve());
|
||||||
|
player.set_right_sleeve(displayed_skin_parts.right_sleeve());
|
||||||
|
player.set_left_pants_leg(displayed_skin_parts.left_pants_leg());
|
||||||
|
player.set_right_pants_leg(displayed_skin_parts.right_pants_leg());
|
||||||
|
player.set_hat(displayed_skin_parts.hat());
|
||||||
|
player.set_main_arm(*main_hand as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::MovePosition {
|
||||||
|
position,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MovePositionAndRotation {
|
||||||
|
position,
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_position(*position);
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveRotation {
|
||||||
|
yaw,
|
||||||
|
pitch,
|
||||||
|
on_ground,
|
||||||
|
} => {
|
||||||
|
entity.set_yaw(*yaw);
|
||||||
|
entity.set_head_yaw(*yaw);
|
||||||
|
entity.set_pitch(*pitch);
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveOnGround { on_ground } => {
|
||||||
|
entity.set_on_ground(*on_ground);
|
||||||
|
}
|
||||||
|
ClientEvent::MoveVehicle { .. } => {}
|
||||||
|
ClientEvent::StartSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Standing {
|
||||||
|
player.set_pose(Pose::Sneaking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSneaking => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
if player.get_pose() == Pose::Sneaking {
|
||||||
|
player.set_pose(Pose::Standing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StopSprinting => {
|
||||||
|
if let TrackedData::Player(player) = entity.data_mut() {
|
||||||
|
player.set_sprinting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientEvent::StartJumpWithHorse { .. } => {}
|
||||||
|
ClientEvent::StopJumpWithHorse => {}
|
||||||
|
ClientEvent::LeaveBed => {}
|
||||||
|
ClientEvent::OpenHorseInventory => {}
|
||||||
|
ClientEvent::StartFlyingWithElytra => {}
|
||||||
|
ClientEvent::ArmSwing(hand) => {
|
||||||
|
entity.push_event(match hand {
|
||||||
|
Hand::Main => EntityEvent::SwingMainHand,
|
||||||
|
Hand::Off => EntityEvent::SwingOffHand,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ClientEvent::InteractWithEntity { .. } => {}
|
||||||
|
ClientEvent::SteerBoat { .. } => {}
|
||||||
|
ClientEvent::Digging { .. } => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.set_world(client.world());
|
||||||
|
|
||||||
|
Some(event)
|
||||||
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
use valence::protocol::codec::Decoder;
|
use valence::protocol::codec::Decoder;
|
||||||
use valence::protocol::packets::handshake::{Handshake, HandshakeNextState};
|
use valence::protocol::packets::c2s::handshake::{Handshake, HandshakeNextState};
|
||||||
use valence::protocol::packets::login::c2s::{EncryptionResponse, LoginStart};
|
use valence::protocol::packets::c2s::login::{EncryptionResponse, LoginStart};
|
||||||
use valence::protocol::packets::login::s2c::{LoginSuccess, S2cLoginPacket};
|
|
||||||
use valence::protocol::packets::c2s::play::C2sPlayPacket;
|
use valence::protocol::packets::c2s::play::C2sPlayPacket;
|
||||||
|
use valence::protocol::packets::c2s::status::{QueryPing, QueryRequest};
|
||||||
|
use valence::protocol::packets::s2c::login::{LoginSuccess, S2cLoginPacket};
|
||||||
use valence::protocol::packets::s2c::play::S2cPlayPacket;
|
use valence::protocol::packets::s2c::play::S2cPlayPacket;
|
||||||
use valence::protocol::packets::status::c2s::{QueryPing, QueryRequest};
|
use valence::protocol::packets::s2c::status::{QueryPong, QueryResponse};
|
||||||
use valence::protocol::packets::status::s2c::{QueryPong, QueryResponse};
|
|
||||||
use valence::protocol::packets::{DecodePacket, EncodePacket};
|
use valence::protocol::packets::{DecodePacket, EncodePacket};
|
||||||
use valence::protocol::{Encode, VarInt};
|
use valence::protocol::{Encode, VarInt};
|
||||||
|
|
||||||
|
|
439
src/client.rs
439
src/client.rs
|
@ -4,7 +4,7 @@ use std::collections::{HashSet, VecDeque};
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
pub use bitfield_struct::bitfield;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
use flume::{Receiver, Sender, TrySendError};
|
use flume::{Receiver, Sender, TrySendError};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
|
@ -16,10 +16,9 @@ use crate::block_pos::BlockPos;
|
||||||
use crate::chunk_pos::ChunkPos;
|
use crate::chunk_pos::ChunkPos;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::dimension::DimensionId;
|
use crate::dimension::DimensionId;
|
||||||
use crate::entity::kinds::Player;
|
use crate::entity::data::Player;
|
||||||
use crate::entity::{
|
use crate::entity::{
|
||||||
velocity_to_packet_units, Entities, EntityId, EntityKind, Event as EntityEvent,
|
velocity_to_packet_units, Entities, EntityEvent, EntityId, EntityKind, StatusOrAnimation,
|
||||||
StatusOrAnimation,
|
|
||||||
};
|
};
|
||||||
use crate::player_textures::SignedPlayerTextures;
|
use crate::player_textures::SignedPlayerTextures;
|
||||||
use crate::protocol_inner::packets::c2s::play::{
|
use crate::protocol_inner::packets::c2s::play::{
|
||||||
|
@ -42,9 +41,9 @@ use crate::slotmap::{Key, SlotMap};
|
||||||
use crate::text::Text;
|
use crate::text::Text;
|
||||||
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::world::{WorldId, Worlds};
|
use crate::world::{WorldId, Worlds};
|
||||||
use crate::{ident, Ticks, LIBRARY_NAMESPACE, STANDARD_TPS};
|
use crate::{ident, LIBRARY_NAMESPACE};
|
||||||
|
|
||||||
/// Contains the [`Event`] enum and related data types.
|
/// Contains the [`ClientEvent`] enum and related data types.
|
||||||
mod event;
|
mod event;
|
||||||
|
|
||||||
/// A container for all [`Client`]s on a [`Server`](crate::server::Server).
|
/// A container for all [`Client`]s on a [`Server`](crate::server::Server).
|
||||||
|
@ -62,8 +61,8 @@ impl<C: Config> Clients<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, client: Client<C>) -> (ClientId, &mut Client<C>) {
|
pub(crate) fn insert(&mut self, client: Client<C>) -> (ClientId, &mut Client<C>) {
|
||||||
let (id, client) = self.sm.insert(client);
|
let (k, client) = self.sm.insert(client);
|
||||||
(ClientId(id), client)
|
(ClientId(k), client)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a client from the server.
|
/// Removes a client from the server.
|
||||||
|
@ -74,7 +73,7 @@ impl<C: Config> Clients<C> {
|
||||||
self.sm.remove(client.0).is_some()
|
self.sm.remove(client.0).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes all clients from the server (as if by [`Self::delete`]) for
|
/// Deletes all clients from the server for
|
||||||
/// which `f` returns `true`.
|
/// which `f` returns `true`.
|
||||||
///
|
///
|
||||||
/// All clients are visited in an unspecified order.
|
/// All clients are visited in an unspecified order.
|
||||||
|
@ -146,7 +145,7 @@ impl ClientId {
|
||||||
|
|
||||||
/// Represents a remote connection to a client after successfully logging in.
|
/// Represents a remote connection to a client after successfully logging in.
|
||||||
///
|
///
|
||||||
/// Much like an [`Entity`], clients posess a location, rotation, and UUID.
|
/// Much like an [`Entity`], clients possess a location, rotation, and UUID.
|
||||||
/// However, clients are handled separately from entities and are partially
|
/// However, clients are handled separately from entities and are partially
|
||||||
/// managed by the library.
|
/// managed by the library.
|
||||||
///
|
///
|
||||||
|
@ -154,30 +153,28 @@ impl ClientId {
|
||||||
/// cannot break blocks, hurt entities, or see other clients. Interactions with
|
/// cannot break blocks, hurt entities, or see other clients. Interactions with
|
||||||
/// the server must be handled explicitly with [`Self::pop_event`].
|
/// the server must be handled explicitly with [`Self::pop_event`].
|
||||||
///
|
///
|
||||||
/// Additionally, clients posess [`Player`] entity data which is only visible to
|
/// Additionally, clients possess [`Player`] entity data which is only visible
|
||||||
/// themselves. This can be accessed with [`Self::player`] and
|
/// to themselves. This can be accessed with [`Self::player`] and
|
||||||
/// [`Self::player_mut`].
|
/// [`Self::player_mut`].
|
||||||
///
|
///
|
||||||
/// # The Difference Between a "Client" and a "Player"
|
/// # The Difference Between a "Client" and a "Player"
|
||||||
///
|
///
|
||||||
/// Normally in Minecraft, players and clients are one and the same. Players are
|
/// Normally in Minecraft, players and clients are one and the same. Players are
|
||||||
/// simply a special type of entity which is backed by a remote connection.
|
/// simply a subtype of the entity base class backed by a remote connection.
|
||||||
///
|
///
|
||||||
/// In Valence however, clients and players have been decoupled. This separation
|
/// In Valence however, clients and players are decoupled. This separation
|
||||||
/// was done primarily to enable multithreaded client updates.
|
/// allows for greater flexibility and parallelism.
|
||||||
pub struct Client<C: Config> {
|
pub struct Client<C: Config> {
|
||||||
/// Custom state.
|
/// Custom state.
|
||||||
pub state: C::ClientState,
|
pub state: C::ClientState,
|
||||||
/// Setting this to `None` disconnects the client.
|
/// Setting this to `None` disconnects the client.
|
||||||
send: SendOpt,
|
send: SendOpt,
|
||||||
recv: Receiver<C2sPlayPacket>,
|
recv: Receiver<C2sPlayPacket>,
|
||||||
/// The tick this client was created.
|
|
||||||
created_tick: Ticks,
|
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
username: String,
|
username: String,
|
||||||
textures: Option<SignedPlayerTextures>,
|
textures: Option<SignedPlayerTextures>,
|
||||||
world: WorldId,
|
world: WorldId,
|
||||||
new_position: Vec3<f64>,
|
position: Vec3<f64>,
|
||||||
old_position: Vec3<f64>,
|
old_position: Vec3<f64>,
|
||||||
/// Measured in m/s.
|
/// Measured in m/s.
|
||||||
velocity: Vec3<f32>,
|
velocity: Vec3<f32>,
|
||||||
|
@ -185,6 +182,7 @@ pub struct Client<C: Config> {
|
||||||
yaw: f32,
|
yaw: f32,
|
||||||
/// Measured in degrees
|
/// Measured in degrees
|
||||||
pitch: f32,
|
pitch: f32,
|
||||||
|
view_distance: u8,
|
||||||
/// Counts up as teleports are made.
|
/// Counts up as teleports are made.
|
||||||
teleport_id_counter: u32,
|
teleport_id_counter: u32,
|
||||||
/// The number of pending client teleports that have yet to receive a
|
/// The number of pending client teleports that have yet to receive a
|
||||||
|
@ -194,11 +192,9 @@ pub struct Client<C: Config> {
|
||||||
spawn_position: BlockPos,
|
spawn_position: BlockPos,
|
||||||
spawn_position_yaw: f32,
|
spawn_position_yaw: f32,
|
||||||
death_location: Option<(DimensionId, BlockPos)>,
|
death_location: Option<(DimensionId, BlockPos)>,
|
||||||
events: VecDeque<Event>,
|
events: VecDeque<ClientEvent>,
|
||||||
/// The ID of the last keepalive sent.
|
/// The ID of the last keepalive sent.
|
||||||
last_keepalive_id: i64,
|
last_keepalive_id: i64,
|
||||||
new_max_view_distance: u8,
|
|
||||||
old_max_view_distance: u8,
|
|
||||||
/// Entities that were visible to this client at the end of the last tick.
|
/// Entities that were visible to this client at the end of the last tick.
|
||||||
/// This is used to determine what entity create/destroy packets should be
|
/// This is used to determine what entity create/destroy packets should be
|
||||||
/// sent.
|
/// sent.
|
||||||
|
@ -212,21 +208,15 @@ pub struct Client<C: Config> {
|
||||||
msgs_to_send: Vec<Text>,
|
msgs_to_send: Vec<Text>,
|
||||||
attack_speed: f64,
|
attack_speed: f64,
|
||||||
movement_speed: f64,
|
movement_speed: f64,
|
||||||
flags: ClientFlags,
|
bits: ClientBits,
|
||||||
/// The data for the client's own player entity.
|
/// The data for the client's own player entity.
|
||||||
player_data: Player,
|
player_data: Player,
|
||||||
entity_events: Vec<EntityEvent>,
|
entity_events: Vec<EntityEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitfield(u16)]
|
#[bitfield(u16)]
|
||||||
pub(crate) struct ClientFlags {
|
struct ClientBits {
|
||||||
spawn: bool,
|
spawn: bool,
|
||||||
sneaking: bool,
|
|
||||||
sprinting: bool,
|
|
||||||
jumping_with_horse: bool,
|
|
||||||
on_ground: bool,
|
|
||||||
/// If any of position, yaw, or pitch were modified by the
|
|
||||||
/// user this tick.
|
|
||||||
teleported_this_tick: bool,
|
teleported_this_tick: bool,
|
||||||
/// If spawn_position or spawn_position_yaw were modified this tick.
|
/// If spawn_position or spawn_position_yaw were modified this tick.
|
||||||
modified_spawn_position: bool,
|
modified_spawn_position: bool,
|
||||||
|
@ -236,33 +226,34 @@ pub(crate) struct ClientFlags {
|
||||||
attack_speed_modified: bool,
|
attack_speed_modified: bool,
|
||||||
movement_speed_modified: bool,
|
movement_speed_modified: bool,
|
||||||
velocity_modified: bool,
|
velocity_modified: bool,
|
||||||
#[bits(4)]
|
created_this_tick: bool,
|
||||||
|
view_distance_modified: bool,
|
||||||
|
#[bits(6)]
|
||||||
_pad: u8,
|
_pad: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Config> Client<C> {
|
impl<C: Config> Client<C> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
packet_channels: C2sPacketChannels,
|
packet_channels: C2sPacketChannels,
|
||||||
server: &SharedServer<C>,
|
|
||||||
ncd: NewClientData,
|
ncd: NewClientData,
|
||||||
data: C::ClientState,
|
state: C::ClientState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (send, recv) = packet_channels;
|
let (send, recv) = packet_channels;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state: data,
|
state,
|
||||||
send: Some(send),
|
send: Some(send),
|
||||||
recv,
|
recv,
|
||||||
created_tick: server.current_tick(),
|
|
||||||
uuid: ncd.uuid,
|
uuid: ncd.uuid,
|
||||||
username: ncd.username,
|
username: ncd.username,
|
||||||
textures: ncd.textures,
|
textures: ncd.textures,
|
||||||
world: WorldId::default(),
|
world: WorldId::default(),
|
||||||
new_position: Vec3::default(),
|
position: Vec3::default(),
|
||||||
old_position: Vec3::default(),
|
old_position: Vec3::default(),
|
||||||
velocity: Vec3::default(),
|
velocity: Vec3::default(),
|
||||||
yaw: 0.0,
|
yaw: 0.0,
|
||||||
pitch: 0.0,
|
pitch: 0.0,
|
||||||
|
view_distance: 8,
|
||||||
teleport_id_counter: 0,
|
teleport_id_counter: 0,
|
||||||
pending_teleports: 0,
|
pending_teleports: 0,
|
||||||
spawn_position: BlockPos::default(),
|
spawn_position: BlockPos::default(),
|
||||||
|
@ -270,8 +261,6 @@ impl<C: Config> Client<C> {
|
||||||
death_location: None,
|
death_location: None,
|
||||||
events: VecDeque::new(),
|
events: VecDeque::new(),
|
||||||
last_keepalive_id: 0,
|
last_keepalive_id: 0,
|
||||||
new_max_view_distance: 16,
|
|
||||||
old_max_view_distance: 0,
|
|
||||||
loaded_entities: HashSet::new(),
|
loaded_entities: HashSet::new(),
|
||||||
loaded_chunks: HashSet::new(),
|
loaded_chunks: HashSet::new(),
|
||||||
new_game_mode: GameMode::Survival,
|
new_game_mode: GameMode::Survival,
|
||||||
|
@ -281,17 +270,18 @@ impl<C: Config> Client<C> {
|
||||||
msgs_to_send: Vec::new(),
|
msgs_to_send: Vec::new(),
|
||||||
attack_speed: 4.0,
|
attack_speed: 4.0,
|
||||||
movement_speed: 0.7,
|
movement_speed: 0.7,
|
||||||
flags: ClientFlags::new()
|
bits: ClientBits::new()
|
||||||
.with_modified_spawn_position(true)
|
.with_modified_spawn_position(true)
|
||||||
.with_got_keepalive(true),
|
.with_got_keepalive(true)
|
||||||
|
.with_created_this_tick(true),
|
||||||
player_data: Player::new(),
|
player_data: Player::new(),
|
||||||
entity_events: Vec::new(),
|
entity_events: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the tick that this client was created.
|
/// If the client joined the game this tick.
|
||||||
pub fn created_tick(&self) -> Ticks {
|
pub fn created_this_tick(&self) -> bool {
|
||||||
self.created_tick
|
self.bits.created_this_tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the client's UUID.
|
/// Gets the client's UUID.
|
||||||
|
@ -304,16 +294,6 @@ impl<C: Config> Client<C> {
|
||||||
&self.username
|
&self.username
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the sneaking state of this client.
|
|
||||||
pub fn is_sneaking(&self) -> bool {
|
|
||||||
self.flags.sneaking()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the sprinting state of this client.
|
|
||||||
pub fn is_sprinting(&self) -> bool {
|
|
||||||
self.flags.sprinting()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the player textures of this client. If the client does not have
|
/// Gets the player textures of this client. If the client does not have
|
||||||
/// a skin, then `None` is returned.
|
/// a skin, then `None` is returned.
|
||||||
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
||||||
|
@ -325,13 +305,14 @@ impl<C: Config> Client<C> {
|
||||||
self.world
|
self.world
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the world this client is located in.
|
/// Changes the world this client is located in and respawns the client.
|
||||||
|
/// This can be used to respawn the client after death.
|
||||||
///
|
///
|
||||||
/// The given [`WorldId`] must be valid. Otherwise, the client is
|
/// The given [`WorldId`] must be valid. Otherwise, the client is
|
||||||
/// disconnected.
|
/// disconnected.
|
||||||
pub fn spawn(&mut self, world: WorldId) {
|
pub fn spawn(&mut self, world: WorldId) {
|
||||||
self.world = world;
|
self.world = world;
|
||||||
self.flags.set_spawn(true);
|
self.bits.set_spawn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a system message to the player which is visible in the chat.
|
/// Sends a system message to the player which is visible in the chat.
|
||||||
|
@ -344,7 +325,7 @@ impl<C: Config> Client<C> {
|
||||||
/// Gets the absolute position of this client in the world it is located
|
/// Gets the absolute position of this client in the world it is located
|
||||||
/// in.
|
/// in.
|
||||||
pub fn position(&self) -> Vec3<f64> {
|
pub fn position(&self) -> Vec3<f64> {
|
||||||
self.new_position
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the position and rotation of this client in the world it is
|
/// Changes the position and rotation of this client in the world it is
|
||||||
|
@ -352,31 +333,14 @@ impl<C: Config> Client<C> {
|
||||||
///
|
///
|
||||||
/// If you want to change the client's world, use [`Self::spawn`].
|
/// If you want to change the client's world, use [`Self::spawn`].
|
||||||
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
||||||
self.new_position = pos.into();
|
self.position = pos.into();
|
||||||
self.yaw = yaw;
|
self.yaw = yaw;
|
||||||
self.pitch = pitch;
|
self.pitch = pitch;
|
||||||
self.velocity = Vec3::default();
|
|
||||||
|
|
||||||
if !self.flags.teleported_this_tick() {
|
self.bits.set_teleported_this_tick(true);
|
||||||
self.flags.set_teleported_this_tick(true);
|
|
||||||
|
|
||||||
self.pending_teleports = match self.pending_teleports.checked_add(1) {
|
|
||||||
Some(n) => n,
|
|
||||||
None => {
|
|
||||||
log::warn!("too many pending teleports for {}", self.username());
|
|
||||||
self.disconnect_no_reason();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.teleport_id_counter = self.teleport_id_counter.wrapping_add(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the velocity of this client in m/s.
|
/// Gets the most recently set velocity of this client in m/s.
|
||||||
///
|
|
||||||
/// The velocity of a client is derived from their current and previous
|
|
||||||
/// position.
|
|
||||||
pub fn velocity(&self) -> Vec3<f32> {
|
pub fn velocity(&self) -> Vec3<f32> {
|
||||||
self.velocity
|
self.velocity
|
||||||
}
|
}
|
||||||
|
@ -384,7 +348,7 @@ impl<C: Config> Client<C> {
|
||||||
/// Sets the client's velocity in m/s.
|
/// Sets the client's velocity in m/s.
|
||||||
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
|
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
|
||||||
self.velocity = velocity.into();
|
self.velocity = velocity.into();
|
||||||
self.flags.set_velocity_modified(true);
|
self.bits.set_velocity_modified(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets this client's yaw.
|
/// Gets this client's yaw.
|
||||||
|
@ -410,12 +374,13 @@ impl<C: Config> Client<C> {
|
||||||
if pos != self.spawn_position || yaw_degrees != self.spawn_position_yaw {
|
if pos != self.spawn_position || yaw_degrees != self.spawn_position_yaw {
|
||||||
self.spawn_position = pos;
|
self.spawn_position = pos;
|
||||||
self.spawn_position_yaw = yaw_degrees;
|
self.spawn_position_yaw = yaw_degrees;
|
||||||
self.flags.set_modified_spawn_position(true);
|
self.bits.set_modified_spawn_position(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the last death location of this client. The client will see
|
/// Gets the last death location of this client. The client will see
|
||||||
/// `minecraft:recovery_compass` items point at the returned position.
|
/// `minecraft:recovery_compass` items point at the returned position.
|
||||||
|
///
|
||||||
/// If the client's current dimension differs from the returned
|
/// If the client's current dimension differs from the returned
|
||||||
/// dimension or the location is `None` then the compass will spin
|
/// dimension or the location is `None` then the compass will spin
|
||||||
/// randomly.
|
/// randomly.
|
||||||
|
@ -482,7 +447,7 @@ impl<C: Config> Client<C> {
|
||||||
pub fn set_attack_speed(&mut self, speed: f64) {
|
pub fn set_attack_speed(&mut self, speed: f64) {
|
||||||
if self.attack_speed != speed {
|
if self.attack_speed != speed {
|
||||||
self.attack_speed = speed;
|
self.attack_speed = speed;
|
||||||
self.flags.set_attack_speed_modified(true);
|
self.bits.set_attack_speed_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,7 +460,7 @@ impl<C: Config> Client<C> {
|
||||||
pub fn set_movement_speed(&mut self, speed: f64) {
|
pub fn set_movement_speed(&mut self, speed: f64) {
|
||||||
if self.movement_speed != speed {
|
if self.movement_speed != speed {
|
||||||
self.movement_speed = speed;
|
self.movement_speed = speed;
|
||||||
self.flags.set_movement_speed_modified(true);
|
self.bits.set_movement_speed_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,11 +469,6 @@ impl<C: Config> Client<C> {
|
||||||
self.send_packet(ClearTitles { reset: true });
|
self.send_packet(ClearTitles { reset: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets if the client is on the ground, as determined by the client.
|
|
||||||
pub fn on_ground(&self) -> bool {
|
|
||||||
self.flags.on_ground()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets whether or not the client is connected to the server.
|
/// Gets whether or not the client is connected to the server.
|
||||||
///
|
///
|
||||||
/// A disconnected client object will never become reconnected. It is your
|
/// A disconnected client object will never become reconnected. It is your
|
||||||
|
@ -518,42 +478,46 @@ impl<C: Config> Client<C> {
|
||||||
self.send.is_none()
|
self.send.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn events(
|
||||||
|
&self,
|
||||||
|
) -> impl DoubleEndedIterator<Item = &ClientEvent> + ExactSizeIterator + FusedIterator + Clone + '_
|
||||||
|
{
|
||||||
|
self.events.iter()
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes an [`Event`] from the event queue.
|
/// Removes an [`Event`] from the event queue.
|
||||||
///
|
///
|
||||||
/// If there are no remaining events, `None` is returned.
|
/// If there are no remaining events, `None` is returned.
|
||||||
///
|
///
|
||||||
/// Any remaining client events are deleted at the end of the
|
/// Any remaining client events are deleted at the end of the
|
||||||
/// current tick.
|
/// current tick.
|
||||||
pub fn pop_event(&mut self) -> Option<Event> {
|
pub fn pop_event(&mut self) -> Option<ClientEvent> {
|
||||||
self.events.pop_front()
|
self.events.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_entity_event(&mut self, event: EntityEvent) {
|
pub fn push_entity_event(&mut self, event: EntityEvent) {
|
||||||
self.entity_events.push(event);
|
self.entity_events.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current view distance of this client measured in chunks.
|
/// The current view distance of this client measured in chunks. The client
|
||||||
pub fn view_distance(&self) -> u8 {
|
/// will not be able to see chunks and entities past this distance.
|
||||||
self.settings
|
|
||||||
.as_ref()
|
|
||||||
.map_or(2, |s| s.view_distance)
|
|
||||||
.min(self.max_view_distance())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the maximum view distance. The client will not be able to see
|
|
||||||
/// chunks and entities past this distance.
|
|
||||||
///
|
///
|
||||||
/// The value returned is measured in chunks.
|
/// The result is in `2..=32`.
|
||||||
pub fn max_view_distance(&self) -> u8 {
|
pub fn view_distance(&self) -> u8 {
|
||||||
self.new_max_view_distance
|
self.view_distance
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the maximum view distance. The client will not be able to see
|
/// Sets the view distance. The client will not be able to see chunks and
|
||||||
/// chunks and entities past this distance.
|
/// entities past this distance.
|
||||||
///
|
///
|
||||||
/// The new view distance is measured in chunks and is clamped to `2..=32`.
|
/// The new view distance is measured in chunks and is clamped to `2..=32`.
|
||||||
pub fn set_max_view_distance(&mut self, dist: u8) {
|
pub fn set_view_distance(&mut self, dist: u8) {
|
||||||
self.new_max_view_distance = dist.clamp(2, 32);
|
let dist = dist.clamp(2, 32);
|
||||||
|
|
||||||
|
if self.view_distance != dist {
|
||||||
|
self.view_distance = dist;
|
||||||
|
self.bits.set_view_distance_modified(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables hardcore mode. This changes the design of the client's hearts.
|
/// Enables hardcore mode. This changes the design of the client's hearts.
|
||||||
|
@ -561,12 +525,12 @@ impl<C: Config> Client<C> {
|
||||||
/// To have any visible effect, this function must be called on the same
|
/// To have any visible effect, this function must be called on the same
|
||||||
/// tick the client joins the server.
|
/// tick the client joins the server.
|
||||||
pub fn set_hardcore(&mut self, hardcore: bool) {
|
pub fn set_hardcore(&mut self, hardcore: bool) {
|
||||||
self.flags.set_hardcore(hardcore);
|
self.bits.set_hardcore(hardcore);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets if hardcore mode is enabled.
|
/// Gets if hardcore mode is enabled.
|
||||||
pub fn is_hardcore(&self) -> bool {
|
pub fn is_hardcore(&self) -> bool {
|
||||||
self.flags.hardcore()
|
self.bits.hardcore()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the client's current settings.
|
/// Gets the client's current settings.
|
||||||
|
@ -630,44 +594,6 @@ impl<C: Config> Client<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_serverbound_packet(&mut self, entities: &Entities<C>, pkt: C2sPlayPacket) {
|
fn handle_serverbound_packet(&mut self, entities: &Entities<C>, pkt: C2sPlayPacket) {
|
||||||
fn handle_movement_packet<C: Config>(
|
|
||||||
client: &mut Client<C>,
|
|
||||||
_vehicle: bool,
|
|
||||||
new_position: Vec3<f64>,
|
|
||||||
new_yaw: f32,
|
|
||||||
new_pitch: f32,
|
|
||||||
new_on_ground: bool,
|
|
||||||
) {
|
|
||||||
if client.pending_teleports == 0 {
|
|
||||||
// TODO: validate movement using swept AABB collision with the blocks.
|
|
||||||
// TODO: validate that the client is actually inside/outside the vehicle?
|
|
||||||
|
|
||||||
// Movement packets should be coming in at a rate of STANDARD_TPS.
|
|
||||||
let new_velocity = (new_position - client.new_position).as_() * STANDARD_TPS as f32;
|
|
||||||
|
|
||||||
let event = Event::Movement {
|
|
||||||
old_position: client.new_position,
|
|
||||||
old_velocity: client.velocity,
|
|
||||||
old_yaw: client.yaw,
|
|
||||||
old_pitch: client.pitch,
|
|
||||||
old_on_ground: client.flags.on_ground(),
|
|
||||||
new_position,
|
|
||||||
new_velocity,
|
|
||||||
new_yaw,
|
|
||||||
new_pitch,
|
|
||||||
new_on_ground,
|
|
||||||
};
|
|
||||||
|
|
||||||
client.new_position = new_position;
|
|
||||||
client.velocity = new_velocity;
|
|
||||||
client.yaw = new_yaw;
|
|
||||||
client.pitch = new_pitch;
|
|
||||||
client.flags.set_on_ground(new_on_ground);
|
|
||||||
|
|
||||||
client.events.push_back(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match pkt {
|
match pkt {
|
||||||
C2sPlayPacket::TeleportConfirm(p) => {
|
C2sPlayPacket::TeleportConfirm(p) => {
|
||||||
if self.pending_teleports == 0 {
|
if self.pending_teleports == 0 {
|
||||||
|
@ -694,14 +620,14 @@ impl<C: Config> Client<C> {
|
||||||
C2sPlayPacket::QueryBlockNbt(_) => {}
|
C2sPlayPacket::QueryBlockNbt(_) => {}
|
||||||
C2sPlayPacket::UpdateDifficulty(_) => {}
|
C2sPlayPacket::UpdateDifficulty(_) => {}
|
||||||
C2sPlayPacket::CommandExecution(_) => {}
|
C2sPlayPacket::CommandExecution(_) => {}
|
||||||
C2sPlayPacket::ChatMessage(p) => self.events.push_back(Event::ChatMessage {
|
C2sPlayPacket::ChatMessage(p) => self.events.push_back(ClientEvent::ChatMessage {
|
||||||
message: p.message.0,
|
message: p.message.0,
|
||||||
timestamp: Duration::from_millis(p.timestamp),
|
timestamp: Duration::from_millis(p.timestamp),
|
||||||
}),
|
}),
|
||||||
C2sPlayPacket::RequestChatPreview(_) => {}
|
C2sPlayPacket::RequestChatPreview(_) => {}
|
||||||
C2sPlayPacket::ClientStatus(_) => {}
|
C2sPlayPacket::ClientStatus(_) => {}
|
||||||
C2sPlayPacket::ClientSettings(p) => {
|
C2sPlayPacket::ClientSettings(p) => {
|
||||||
let old = self.settings.replace(Settings {
|
self.events.push_back(ClientEvent::SettingsChanged {
|
||||||
locale: p.locale.0,
|
locale: p.locale.0,
|
||||||
view_distance: p.view_distance.0,
|
view_distance: p.view_distance.0,
|
||||||
chat_mode: p.chat_mode,
|
chat_mode: p.chat_mode,
|
||||||
|
@ -709,9 +635,7 @@ impl<C: Config> Client<C> {
|
||||||
main_hand: p.main_hand,
|
main_hand: p.main_hand,
|
||||||
displayed_skin_parts: p.displayed_skin_parts,
|
displayed_skin_parts: p.displayed_skin_parts,
|
||||||
allow_server_listings: p.allow_server_listings,
|
allow_server_listings: p.allow_server_listings,
|
||||||
});
|
})
|
||||||
|
|
||||||
self.events.push_back(Event::SettingsChanged(old));
|
|
||||||
}
|
}
|
||||||
C2sPlayPacket::RequestCommandCompletion(_) => {}
|
C2sPlayPacket::RequestCommandCompletion(_) => {}
|
||||||
C2sPlayPacket::ButtonClick(_) => {}
|
C2sPlayPacket::ButtonClick(_) => {}
|
||||||
|
@ -725,7 +649,7 @@ impl<C: Config> Client<C> {
|
||||||
// TODO: verify that the client has line of sight to the targeted entity and
|
// TODO: verify that the client has line of sight to the targeted entity and
|
||||||
// that the distance is <=4 blocks.
|
// that the distance is <=4 blocks.
|
||||||
|
|
||||||
self.events.push_back(Event::InteractWithEntity {
|
self.events.push_back(ClientEvent::InteractWithEntity {
|
||||||
id,
|
id,
|
||||||
sneaking: p.sneaking,
|
sneaking: p.sneaking,
|
||||||
kind: match p.kind {
|
kind: match p.kind {
|
||||||
|
@ -741,7 +665,7 @@ impl<C: Config> Client<C> {
|
||||||
C2sPlayPacket::JigsawGenerate(_) => {}
|
C2sPlayPacket::JigsawGenerate(_) => {}
|
||||||
C2sPlayPacket::KeepAlive(p) => {
|
C2sPlayPacket::KeepAlive(p) => {
|
||||||
let last_keepalive_id = self.last_keepalive_id;
|
let last_keepalive_id = self.last_keepalive_id;
|
||||||
if self.flags.got_keepalive() {
|
if self.bits.got_keepalive() {
|
||||||
log::warn!("unexpected keepalive from player {}", self.username());
|
log::warn!("unexpected keepalive from player {}", self.username());
|
||||||
self.disconnect_no_reason();
|
self.disconnect_no_reason();
|
||||||
} else if p.id != last_keepalive_id {
|
} else if p.id != last_keepalive_id {
|
||||||
|
@ -753,39 +677,68 @@ impl<C: Config> Client<C> {
|
||||||
);
|
);
|
||||||
self.disconnect_no_reason();
|
self.disconnect_no_reason();
|
||||||
} else {
|
} else {
|
||||||
self.flags.set_got_keepalive(true);
|
self.bits.set_got_keepalive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::UpdateDifficultyLock(_) => {}
|
C2sPlayPacket::UpdateDifficultyLock(_) => {}
|
||||||
C2sPlayPacket::MovePlayerPosition(p) => {
|
C2sPlayPacket::MovePlayerPosition(p) => {
|
||||||
handle_movement_packet(self, false, p.position, self.yaw, self.pitch, p.on_ground)
|
if self.pending_teleports == 0 {
|
||||||
|
self.position = p.position;
|
||||||
|
|
||||||
|
self.events.push_back(ClientEvent::MovePosition {
|
||||||
|
position: p.position,
|
||||||
|
on_ground: p.on_ground,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::MovePlayerPositionAndRotation(p) => {
|
C2sPlayPacket::MovePlayerPositionAndRotation(p) => {
|
||||||
handle_movement_packet(self, false, p.position, p.yaw, p.pitch, p.on_ground)
|
if self.pending_teleports == 0 {
|
||||||
|
self.position = p.position;
|
||||||
|
self.yaw = p.yaw;
|
||||||
|
self.pitch = p.pitch;
|
||||||
|
|
||||||
|
self.events.push_back(ClientEvent::MovePositionAndRotation {
|
||||||
|
position: p.position,
|
||||||
|
yaw: p.yaw,
|
||||||
|
pitch: p.pitch,
|
||||||
|
on_ground: p.on_ground,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::MovePlayerRotation(p) => {
|
C2sPlayPacket::MovePlayerRotation(p) => {
|
||||||
handle_movement_packet(self, false, self.new_position, p.yaw, p.pitch, p.on_ground)
|
if self.pending_teleports == 0 {
|
||||||
|
self.yaw = p.yaw;
|
||||||
|
self.pitch = p.pitch;
|
||||||
|
|
||||||
|
self.events.push_back(ClientEvent::MoveRotation {
|
||||||
|
yaw: p.yaw,
|
||||||
|
pitch: p.pitch,
|
||||||
|
on_ground: p.on_ground,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
C2sPlayPacket::MovePlayerOnGround(p) => {
|
||||||
|
if self.pending_teleports == 0 {
|
||||||
|
self.events.push_back(ClientEvent::MoveOnGround {
|
||||||
|
on_ground: p.on_ground,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::MovePlayerOnGround(p) => handle_movement_packet(
|
|
||||||
self,
|
|
||||||
false,
|
|
||||||
self.new_position,
|
|
||||||
self.yaw,
|
|
||||||
self.pitch,
|
|
||||||
p.on_ground,
|
|
||||||
),
|
|
||||||
C2sPlayPacket::MoveVehicle(p) => {
|
C2sPlayPacket::MoveVehicle(p) => {
|
||||||
handle_movement_packet(
|
if self.pending_teleports == 0 {
|
||||||
self,
|
self.position = p.position;
|
||||||
true,
|
self.yaw = p.yaw;
|
||||||
p.position,
|
self.pitch = p.pitch;
|
||||||
p.yaw,
|
|
||||||
p.pitch,
|
self.events.push_back(ClientEvent::MoveVehicle {
|
||||||
self.flags.on_ground(),
|
position: p.position,
|
||||||
);
|
yaw: p.yaw,
|
||||||
|
pitch: p.pitch,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
C2sPlayPacket::BoatPaddleState(p) => {
|
C2sPlayPacket::BoatPaddleState(p) => {
|
||||||
self.events.push_back(Event::SteerBoat {
|
self.events.push_back(ClientEvent::SteerBoat {
|
||||||
left_paddle_turning: p.left_paddle_turning,
|
left_paddle_turning: p.left_paddle_turning,
|
||||||
right_paddle_turning: p.right_paddle_turning,
|
right_paddle_turning: p.right_paddle_turning,
|
||||||
});
|
});
|
||||||
|
@ -802,17 +755,17 @@ impl<C: Config> Client<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.events.push_back(match p.status {
|
self.events.push_back(match p.status {
|
||||||
DiggingStatus::StartedDigging => Event::Digging {
|
DiggingStatus::StartedDigging => ClientEvent::Digging {
|
||||||
status: event::DiggingStatus::Start,
|
status: event::DiggingStatus::Start,
|
||||||
position: p.location,
|
position: p.location,
|
||||||
face: p.face,
|
face: p.face,
|
||||||
},
|
},
|
||||||
DiggingStatus::CancelledDigging => Event::Digging {
|
DiggingStatus::CancelledDigging => ClientEvent::Digging {
|
||||||
status: event::DiggingStatus::Cancel,
|
status: event::DiggingStatus::Cancel,
|
||||||
position: p.location,
|
position: p.location,
|
||||||
face: p.face,
|
face: p.face,
|
||||||
},
|
},
|
||||||
DiggingStatus::FinishedDigging => Event::Digging {
|
DiggingStatus::FinishedDigging => ClientEvent::Digging {
|
||||||
status: event::DiggingStatus::Finish,
|
status: event::DiggingStatus::Finish,
|
||||||
position: p.location,
|
position: p.location,
|
||||||
face: p.face,
|
face: p.face,
|
||||||
|
@ -823,56 +776,19 @@ impl<C: Config> Client<C> {
|
||||||
DiggingStatus::SwapItemInHand => return,
|
DiggingStatus::SwapItemInHand => return,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
C2sPlayPacket::PlayerCommand(e) => {
|
C2sPlayPacket::PlayerCommand(c) => {
|
||||||
// TODO: validate:
|
self.events.push_back(match c.action_id {
|
||||||
// - Can't sprint and sneak at the same time
|
PlayerCommandId::StartSneaking => ClientEvent::StartSneaking,
|
||||||
// - Can't leave bed while not in a bed.
|
PlayerCommandId::StopSneaking => ClientEvent::StopSneaking,
|
||||||
// - Can't jump with a horse if not on a horse
|
PlayerCommandId::LeaveBed => ClientEvent::LeaveBed,
|
||||||
// - Can't open horse inventory if not on a horse.
|
PlayerCommandId::StartSprinting => ClientEvent::StartSprinting,
|
||||||
// - Can't fly with elytra if not wearing an elytra.
|
PlayerCommandId::StopSprinting => ClientEvent::StopSprinting,
|
||||||
// - Can't jump with horse while already jumping & vice versa?
|
PlayerCommandId::StartJumpWithHorse => ClientEvent::StartJumpWithHorse {
|
||||||
self.events.push_back(match e.action_id {
|
jump_boost: c.jump_boost.0 .0 as u8,
|
||||||
PlayerCommandId::StartSneaking => {
|
},
|
||||||
if self.flags.sneaking() {
|
PlayerCommandId::StopJumpWithHorse => ClientEvent::StopJumpWithHorse,
|
||||||
return;
|
PlayerCommandId::OpenHorseInventory => ClientEvent::OpenHorseInventory,
|
||||||
}
|
PlayerCommandId::StartFlyingWithElytra => ClientEvent::StartFlyingWithElytra,
|
||||||
self.flags.set_sneaking(true);
|
|
||||||
Event::StartSneaking
|
|
||||||
}
|
|
||||||
PlayerCommandId::StopSneaking => {
|
|
||||||
if !self.flags.sneaking() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.flags.set_sneaking(false);
|
|
||||||
Event::StopSneaking
|
|
||||||
}
|
|
||||||
PlayerCommandId::LeaveBed => Event::LeaveBed,
|
|
||||||
PlayerCommandId::StartSprinting => {
|
|
||||||
if self.flags.sprinting() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.flags.set_sprinting(true);
|
|
||||||
Event::StartSprinting
|
|
||||||
}
|
|
||||||
PlayerCommandId::StopSprinting => {
|
|
||||||
if !self.flags.sprinting() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.flags.set_sprinting(false);
|
|
||||||
Event::StopSprinting
|
|
||||||
}
|
|
||||||
PlayerCommandId::StartJumpWithHorse => {
|
|
||||||
self.flags.set_jumping_with_horse(true);
|
|
||||||
Event::StartJumpWithHorse {
|
|
||||||
jump_boost: e.jump_boost.0 .0 as u8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PlayerCommandId::StopJumpWithHorse => {
|
|
||||||
self.flags.set_jumping_with_horse(false);
|
|
||||||
Event::StopJumpWithHorse
|
|
||||||
}
|
|
||||||
PlayerCommandId::OpenHorseInventory => Event::OpenHorseInventory,
|
|
||||||
PlayerCommandId::StartFlyingWithElytra => Event::StartFlyingWithElytra,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
C2sPlayPacket::PlayerInput(_) => {}
|
C2sPlayPacket::PlayerInput(_) => {}
|
||||||
|
@ -891,7 +807,7 @@ impl<C: Config> Client<C> {
|
||||||
C2sPlayPacket::UpdateJigsaw(_) => {}
|
C2sPlayPacket::UpdateJigsaw(_) => {}
|
||||||
C2sPlayPacket::UpdateStructureBlock(_) => {}
|
C2sPlayPacket::UpdateStructureBlock(_) => {}
|
||||||
C2sPlayPacket::UpdateSign(_) => {}
|
C2sPlayPacket::UpdateSign(_) => {}
|
||||||
C2sPlayPacket::HandSwing(p) => self.events.push_back(Event::ArmSwing(p.hand)),
|
C2sPlayPacket::HandSwing(p) => self.events.push_back(ClientEvent::ArmSwing(p.hand)),
|
||||||
C2sPlayPacket::SpectatorTeleport(_) => {}
|
C2sPlayPacket::SpectatorTeleport(_) => {}
|
||||||
C2sPlayPacket::PlayerInteractBlock(_) => {}
|
C2sPlayPacket::PlayerInteractBlock(_) => {}
|
||||||
C2sPlayPacket::PlayerInteractItem(_) => {}
|
C2sPlayPacket::PlayerInteractItem(_) => {}
|
||||||
|
@ -926,7 +842,7 @@ impl<C: Config> Client<C> {
|
||||||
|
|
||||||
// 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 == current_tick {
|
if self.created_this_tick() {
|
||||||
world
|
world
|
||||||
.meta
|
.meta
|
||||||
.player_list()
|
.player_list()
|
||||||
|
@ -941,7 +857,7 @@ impl<C: Config> Client<C> {
|
||||||
|
|
||||||
self.send_packet(GameJoin {
|
self.send_packet(GameJoin {
|
||||||
entity_id: 0, // EntityId 0 is reserved for clients.
|
entity_id: 0, // EntityId 0 is reserved for clients.
|
||||||
is_hardcore: self.flags.hardcore(),
|
is_hardcore: self.bits.hardcore(),
|
||||||
gamemode: self.new_game_mode,
|
gamemode: self.new_game_mode,
|
||||||
previous_gamemode: self.old_game_mode,
|
previous_gamemode: self.old_game_mode,
|
||||||
dimension_names,
|
dimension_names,
|
||||||
|
@ -956,7 +872,7 @@ impl<C: Config> Client<C> {
|
||||||
),
|
),
|
||||||
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.view_distance() as i32)),
|
||||||
simulation_distance: VarInt(16),
|
simulation_distance: VarInt(16),
|
||||||
reduced_debug_info: false,
|
reduced_debug_info: false,
|
||||||
enable_respawn_screen: false,
|
enable_respawn_screen: false,
|
||||||
|
@ -969,14 +885,15 @@ impl<C: Config> Client<C> {
|
||||||
|
|
||||||
self.teleport(self.position(), self.yaw(), self.pitch());
|
self.teleport(self.position(), self.yaw(), self.pitch());
|
||||||
} else {
|
} else {
|
||||||
if self.flags.spawn() {
|
if self.bits.spawn() {
|
||||||
self.flags.set_spawn(false);
|
self.bits.set_spawn(false);
|
||||||
self.loaded_entities.clear();
|
self.loaded_entities.clear();
|
||||||
self.loaded_chunks.clear();
|
self.loaded_chunks.clear();
|
||||||
|
|
||||||
// TODO: clear player list.
|
// TODO: clear player list.
|
||||||
|
|
||||||
// Client bug workaround: send the client to a dummy dimension first.
|
// Client bug workaround: send the client to a dummy dimension first.
|
||||||
|
// TODO: is there actually a bug?
|
||||||
self.send_packet(PlayerRespawn {
|
self.send_packet(PlayerRespawn {
|
||||||
dimension_type_name: ident!("{LIBRARY_NAMESPACE}:dimension_type_0"),
|
dimension_type_name: ident!("{LIBRARY_NAMESPACE}:dimension_type_0"),
|
||||||
dimension_name: ident!("{LIBRARY_NAMESPACE}:dummy_dimension"),
|
dimension_name: ident!("{LIBRARY_NAMESPACE}:dummy_dimension"),
|
||||||
|
@ -1027,8 +944,8 @@ impl<C: Config> Client<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set player attributes
|
// Set player attributes
|
||||||
if self.flags.attack_speed_modified() {
|
if self.bits.attack_speed_modified() {
|
||||||
self.flags.set_attack_speed_modified(false);
|
self.bits.set_attack_speed_modified(false);
|
||||||
|
|
||||||
self.send_packet(EntityAttributes {
|
self.send_packet(EntityAttributes {
|
||||||
entity_id: VarInt(0),
|
entity_id: VarInt(0),
|
||||||
|
@ -1040,8 +957,8 @@ impl<C: Config> Client<C> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.flags.movement_speed_modified() {
|
if self.bits.movement_speed_modified() {
|
||||||
self.flags.set_movement_speed_modified(false);
|
self.bits.set_movement_speed_modified(false);
|
||||||
|
|
||||||
self.send_packet(EntityAttributes {
|
self.send_packet(EntityAttributes {
|
||||||
entity_id: VarInt(0),
|
entity_id: VarInt(0),
|
||||||
|
@ -1054,8 +971,8 @@ impl<C: Config> Client<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the players spawn position (compass position)
|
// Update the players spawn position (compass position)
|
||||||
if self.flags.modified_spawn_position() {
|
if self.bits.modified_spawn_position() {
|
||||||
self.flags.set_modified_spawn_position(false);
|
self.bits.set_modified_spawn_position(false);
|
||||||
|
|
||||||
self.send_packet(PlayerSpawnPosition {
|
self.send_packet(PlayerSpawnPosition {
|
||||||
location: self.spawn_position,
|
location: self.spawn_position,
|
||||||
|
@ -1063,23 +980,24 @@ impl<C: Config> Client<C> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update view distance fog on the client if necessary.
|
// Update view distance fog on the client.
|
||||||
if self.old_max_view_distance != self.new_max_view_distance {
|
if self.bits.view_distance_modified() {
|
||||||
self.old_max_view_distance = self.new_max_view_distance;
|
self.bits.set_view_distance_modified(false);
|
||||||
if self.created_tick != current_tick {
|
|
||||||
|
if !self.created_this_tick() {
|
||||||
self.send_packet(ChunkLoadDistance {
|
self.send_packet(ChunkLoadDistance {
|
||||||
view_distance: BoundedInt(VarInt(self.new_max_view_distance as i32)),
|
view_distance: BoundedInt(VarInt(self.view_distance() as i32)),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's time to send another keepalive.
|
// Check if it's time to send another keepalive.
|
||||||
if current_tick % (shared.tick_rate() * 8) == 0 {
|
if current_tick % (shared.tick_rate() * 8) == 0 {
|
||||||
if self.flags.got_keepalive() {
|
if self.bits.got_keepalive() {
|
||||||
let id = rand::random();
|
let id = rand::random();
|
||||||
self.send_packet(KeepAlive { id });
|
self.send_packet(KeepAlive { id });
|
||||||
self.last_keepalive_id = id;
|
self.last_keepalive_id = id;
|
||||||
self.flags.set_got_keepalive(false);
|
self.bits.set_got_keepalive(false);
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"player {} timed out (no keepalive response)",
|
"player {} timed out (no keepalive response)",
|
||||||
|
@ -1091,13 +1009,13 @@ impl<C: Config> Client<C> {
|
||||||
|
|
||||||
let view_dist = self.view_distance();
|
let view_dist = self.view_distance();
|
||||||
|
|
||||||
let center = ChunkPos::at(self.new_position.x, self.new_position.z);
|
let center = ChunkPos::at(self.position.x, self.position.z);
|
||||||
|
|
||||||
// Send the update view position packet if the client changes the chunk section
|
// Send the update view position packet if the client changes the chunk section
|
||||||
// they're in.
|
// they're in.
|
||||||
{
|
{
|
||||||
let old_section = self.old_position.map(|n| (n / 16.0).floor() as i32);
|
let old_section = self.old_position.map(|n| (n / 16.0).floor() as i32);
|
||||||
let new_section = self.new_position.map(|n| (n / 16.0).floor() as i32);
|
let new_section = self.position.map(|n| (n / 16.0).floor() as i32);
|
||||||
|
|
||||||
if old_section != new_section {
|
if old_section != new_section {
|
||||||
self.send_packet(ChunkRenderDistanceCenter {
|
self.send_packet(ChunkRenderDistanceCenter {
|
||||||
|
@ -1161,23 +1079,33 @@ impl<C: Config> Client<C> {
|
||||||
//
|
//
|
||||||
// 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.flags.teleported_this_tick() {
|
if self.bits.teleported_this_tick() {
|
||||||
self.flags.set_teleported_this_tick(false);
|
self.bits.set_teleported_this_tick(false);
|
||||||
|
|
||||||
self.send_packet(PlayerPositionLook {
|
self.send_packet(PlayerPositionLook {
|
||||||
position: self.new_position,
|
position: self.position,
|
||||||
yaw: self.yaw,
|
yaw: self.yaw,
|
||||||
pitch: self.pitch,
|
pitch: self.pitch,
|
||||||
flags: PlayerPositionLookFlags::new(false, false, false, false, false),
|
flags: PlayerPositionLookFlags::new(false, false, false, false, false),
|
||||||
teleport_id: VarInt((self.teleport_id_counter - 1) as i32),
|
teleport_id: VarInt(self.teleport_id_counter as i32),
|
||||||
dismount_vehicle: false,
|
dismount_vehicle: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.pending_teleports = self.pending_teleports.wrapping_add(1);
|
||||||
|
|
||||||
|
if self.pending_teleports == 0 {
|
||||||
|
log::warn!("too many pending teleports for {}", self.username());
|
||||||
|
self.disconnect_no_reason();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.teleport_id_counter = self.teleport_id_counter.wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set velocity. Do this after teleporting since teleporting sets velocity to
|
// Set velocity. Do this after teleporting since teleporting sets velocity to
|
||||||
// zero.
|
// zero.
|
||||||
if self.flags.velocity_modified() {
|
if self.bits.velocity_modified() {
|
||||||
self.flags.set_velocity_modified(false);
|
self.bits.set_velocity_modified(false);
|
||||||
|
|
||||||
self.send_packet(EntityVelocityUpdate {
|
self.send_packet(EntityVelocityUpdate {
|
||||||
entity_id: VarInt(0),
|
entity_id: VarInt(0),
|
||||||
|
@ -1203,14 +1131,14 @@ impl<C: Config> Client<C> {
|
||||||
self.loaded_entities.retain(|&id| {
|
self.loaded_entities.retain(|&id| {
|
||||||
if let Some(entity) = entities.get(id) {
|
if let Some(entity) = entities.get(id) {
|
||||||
debug_assert!(entity.kind() != EntityKind::Marker);
|
debug_assert!(entity.kind() != EntityKind::Marker);
|
||||||
if self.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 {
|
if self.position.distance(entity.position()) <= view_dist as f64 * 16.0 {
|
||||||
if let Some(meta) = entity.updated_tracked_data_packet(id) {
|
if let Some(meta) = entity.updated_tracked_data_packet(id) {
|
||||||
send_packet(&mut self.send, meta);
|
send_packet(&mut self.send, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
let position_delta = entity.position() - entity.old_position();
|
let position_delta = entity.position() - entity.old_position();
|
||||||
let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0;
|
let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0;
|
||||||
let flags = entity.flags();
|
let flags = entity.bits();
|
||||||
|
|
||||||
if entity.position() != entity.old_position()
|
if entity.position() != entity.old_position()
|
||||||
&& !needs_teleport
|
&& !needs_teleport
|
||||||
|
@ -1345,7 +1273,8 @@ impl<C: Config> Client<C> {
|
||||||
self.entity_events.clear();
|
self.entity_events.clear();
|
||||||
|
|
||||||
self.player_data.clear_modifications();
|
self.player_data.clear_modifications();
|
||||||
self.old_position = self.new_position;
|
self.old_position = self.position;
|
||||||
|
self.bits.set_created_this_tick(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub use crate::protocol_inner::packets::s2c::play::GameMode;
|
||||||
/// Client events can be obtained from
|
/// Client events can be obtained from
|
||||||
/// [`pop_event`](crate::client::Client::pop_event).
|
/// [`pop_event`](crate::client::Client::pop_event).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum ClientEvent {
|
||||||
/// A regular message was sent to the chat.
|
/// A regular message was sent to the chat.
|
||||||
ChatMessage {
|
ChatMessage {
|
||||||
/// The content of the message
|
/// The content of the message
|
||||||
|
@ -21,31 +21,44 @@ pub enum Event {
|
||||||
/// The time the message was sent.
|
/// The time the message was sent.
|
||||||
timestamp: Duration,
|
timestamp: Duration,
|
||||||
},
|
},
|
||||||
/// Settings were changed. The value in this variant is the _previous_
|
/// Settings were changed. This is always sent once after joining by the
|
||||||
/// client settings.
|
/// vanilla client.
|
||||||
SettingsChanged(Option<Settings>),
|
SettingsChanged {
|
||||||
/// The client moved.
|
/// e.g. en_US
|
||||||
Movement {
|
locale: String,
|
||||||
/// Absolute coordinates of the previous position.
|
/// The client side render distance, in chunks.
|
||||||
old_position: Vec3<f64>,
|
///
|
||||||
/// Previous velocity in m/s.
|
/// The value is always in `2..=32`.
|
||||||
old_velocity: Vec3<f32>,
|
view_distance: u8,
|
||||||
/// The previous yaw (in degrees).
|
chat_mode: ChatMode,
|
||||||
old_yaw: f32,
|
/// `true` if the client has chat colors enabled, `false` otherwise.
|
||||||
/// The previous pitch (in degrees).
|
chat_colors: bool,
|
||||||
old_pitch: f32,
|
main_hand: MainHand,
|
||||||
/// If the client was previously on the ground.
|
displayed_skin_parts: DisplayedSkinParts,
|
||||||
old_on_ground: bool,
|
allow_server_listings: bool,
|
||||||
/// Absolute coodinates of the new position.
|
},
|
||||||
new_position: Vec3<f64>,
|
MovePosition {
|
||||||
/// New velocity in m/s.
|
position: Vec3<f64>,
|
||||||
new_velocity: Vec3<f32>,
|
on_ground: bool,
|
||||||
/// The new yaw (in degrees).
|
},
|
||||||
new_yaw: f32,
|
MovePositionAndRotation {
|
||||||
/// The new pitch (in degrees).
|
position: Vec3<f64>,
|
||||||
new_pitch: f32,
|
yaw: f32,
|
||||||
/// If the client is now on the ground.
|
pitch: f32,
|
||||||
new_on_ground: bool,
|
on_ground: bool,
|
||||||
|
},
|
||||||
|
MoveRotation {
|
||||||
|
yaw: f32,
|
||||||
|
pitch: f32,
|
||||||
|
on_ground: bool,
|
||||||
|
},
|
||||||
|
MoveOnGround {
|
||||||
|
on_ground: bool,
|
||||||
|
},
|
||||||
|
MoveVehicle {
|
||||||
|
position: Vec3<f64>,
|
||||||
|
yaw: f32,
|
||||||
|
pitch: f32,
|
||||||
},
|
},
|
||||||
StartSneaking,
|
StartSneaking,
|
||||||
StopSneaking,
|
StopSneaking,
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub trait Config: 'static + Sized + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// Called once at startup to get the capacity of the buffer used to
|
/// Called once at startup to get the capacity of the buffer used to
|
||||||
/// hold incoming packets.
|
/// hold incoming packets.
|
||||||
///
|
///
|
||||||
/// A larger capcity reduces the chance of packet loss but increases
|
/// A larger capacity reduces the chance of packet loss but increases
|
||||||
/// potential memory usage.
|
/// potential memory usage.
|
||||||
///
|
///
|
||||||
/// # Default Implementation
|
/// # Default Implementation
|
||||||
|
@ -104,7 +104,7 @@ pub trait Config: 'static + Sized + Send + Sync + UnwindSafe + RefUnwindSafe {
|
||||||
/// Called once at startup to get the capacity of the buffer used to
|
/// Called once at startup to get the capacity of the buffer used to
|
||||||
/// hold outgoing packets.
|
/// hold outgoing packets.
|
||||||
///
|
///
|
||||||
/// A larger capcity reduces the chance of packet loss due to a full buffer
|
/// A larger capacity reduces the chance of packet loss due to a full buffer
|
||||||
/// but increases potential memory usage.
|
/// but increases potential memory usage.
|
||||||
///
|
///
|
||||||
/// # Default Implementation
|
/// # Default Implementation
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::iter::FusedIterator;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use bitfield_struct::bitfield;
|
use bitfield_struct::bitfield;
|
||||||
pub use kinds::{TrackedData, EntityKind};
|
pub use data::{EntityKind, TrackedData};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
@ -22,7 +22,7 @@ use crate::world::WorldId;
|
||||||
use crate::STANDARD_TPS;
|
use crate::STANDARD_TPS;
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod kinds;
|
pub mod types;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
|
include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ impl<C: Config> Entities<C> {
|
||||||
state: data,
|
state: data,
|
||||||
variants: TrackedData::new(kind),
|
variants: TrackedData::new(kind),
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
flags: EntityFlags(0),
|
bits: EntityBits::new(),
|
||||||
world: WorldId::NULL,
|
world: WorldId::NULL,
|
||||||
new_position: Vec3::default(),
|
new_position: Vec3::default(),
|
||||||
old_position: Vec3::default(),
|
old_position: Vec3::default(),
|
||||||
|
@ -203,9 +203,9 @@ impl<C: Config> Entities<C> {
|
||||||
e.variants.clear_modifications();
|
e.variants.clear_modifications();
|
||||||
e.events.clear();
|
e.events.clear();
|
||||||
|
|
||||||
e.flags.set_yaw_or_pitch_modified(false);
|
e.bits.set_yaw_or_pitch_modified(false);
|
||||||
e.flags.set_head_yaw_modified(false);
|
e.bits.set_head_yaw_modified(false);
|
||||||
e.flags.set_velocity_modified(false);
|
e.bits.set_velocity_modified(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,8 +244,8 @@ pub struct Entity<C: Config> {
|
||||||
/// Custom data.
|
/// Custom data.
|
||||||
pub state: C::EntityState,
|
pub state: C::EntityState,
|
||||||
variants: TrackedData,
|
variants: TrackedData,
|
||||||
flags: EntityFlags,
|
bits: EntityBits,
|
||||||
events: Vec<Event>,
|
events: Vec<EntityEvent>,
|
||||||
world: WorldId,
|
world: WorldId,
|
||||||
new_position: Vec3<f64>,
|
new_position: Vec3<f64>,
|
||||||
old_position: Vec3<f64>,
|
old_position: Vec3<f64>,
|
||||||
|
@ -257,7 +257,7 @@ pub struct Entity<C: Config> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitfield(u8)]
|
#[bitfield(u8)]
|
||||||
pub(crate) struct EntityFlags {
|
pub(crate) struct EntityBits {
|
||||||
pub yaw_or_pitch_modified: bool,
|
pub yaw_or_pitch_modified: bool,
|
||||||
pub head_yaw_modified: bool,
|
pub head_yaw_modified: bool,
|
||||||
pub velocity_modified: bool,
|
pub velocity_modified: bool,
|
||||||
|
@ -267,15 +267,15 @@ pub(crate) struct EntityFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Config> Entity<C> {
|
impl<C: Config> Entity<C> {
|
||||||
pub(crate) fn flags(&self) -> EntityFlags {
|
pub(crate) fn bits(&self) -> EntityBits {
|
||||||
self.flags
|
self.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> &TrackedData {
|
pub fn data(&self) -> &TrackedData {
|
||||||
&self.variants
|
&self.variants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_mut(&mut self) -> &mut TrackedData {
|
pub fn data_mut(&mut self) -> &mut TrackedData {
|
||||||
&mut self.variants
|
&mut self.variants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,11 +284,11 @@ impl<C: Config> Entity<C> {
|
||||||
self.variants.kind()
|
self.variants.kind()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_event(&mut self, event: Event) {
|
pub fn push_event(&mut self, event: EntityEvent) {
|
||||||
self.events.push(event);
|
self.events.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn events(&self) -> &[Event] {
|
pub(crate) fn events(&self) -> &[EntityEvent] {
|
||||||
&self.events
|
&self.events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ impl<C: Config> Entity<C> {
|
||||||
pub fn set_yaw(&mut self, yaw: f32) {
|
pub fn set_yaw(&mut self, yaw: f32) {
|
||||||
if self.yaw != yaw {
|
if self.yaw != yaw {
|
||||||
self.yaw = yaw;
|
self.yaw = yaw;
|
||||||
self.flags.set_yaw_or_pitch_modified(true);
|
self.bits.set_yaw_or_pitch_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ impl<C: Config> Entity<C> {
|
||||||
pub fn set_pitch(&mut self, pitch: f32) {
|
pub fn set_pitch(&mut self, pitch: f32) {
|
||||||
if self.pitch != pitch {
|
if self.pitch != pitch {
|
||||||
self.pitch = pitch;
|
self.pitch = pitch;
|
||||||
self.flags.set_yaw_or_pitch_modified(true);
|
self.bits.set_yaw_or_pitch_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ impl<C: Config> Entity<C> {
|
||||||
pub fn set_head_yaw(&mut self, head_yaw: f32) {
|
pub fn set_head_yaw(&mut self, head_yaw: f32) {
|
||||||
if self.head_yaw != head_yaw {
|
if self.head_yaw != head_yaw {
|
||||||
self.head_yaw = head_yaw;
|
self.head_yaw = head_yaw;
|
||||||
self.flags.set_head_yaw_modified(true);
|
self.bits.set_head_yaw_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,18 +378,18 @@ impl<C: Config> Entity<C> {
|
||||||
|
|
||||||
if self.velocity != new_vel {
|
if self.velocity != new_vel {
|
||||||
self.velocity = new_vel;
|
self.velocity = new_vel;
|
||||||
self.flags.set_velocity_modified(true);
|
self.bits.set_velocity_modified(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the value of the "on ground" flag.
|
/// Gets the value of the "on ground" flag.
|
||||||
pub fn on_ground(&self) -> bool {
|
pub fn on_ground(&self) -> bool {
|
||||||
self.flags.on_ground()
|
self.bits.on_ground()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value of the "on ground" flag.
|
/// Sets the value of the "on ground" flag.
|
||||||
pub fn set_on_ground(&mut self, on_ground: bool) {
|
pub fn set_on_ground(&mut self, on_ground: bool) {
|
||||||
self.flags.set_on_ground(on_ground);
|
self.bits.set_on_ground(on_ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the UUID of this entity.
|
/// Gets the UUID of this entity.
|
||||||
|
@ -405,7 +405,7 @@ impl<C: Config> Entity<C> {
|
||||||
/// The hitbox of an entity is determined by its position, entity type, and
|
/// The hitbox of an entity is determined by its position, entity type, and
|
||||||
/// other state specific to that type.
|
/// other state specific to that type.
|
||||||
///
|
///
|
||||||
/// [interact event]: crate::client::Event::InteractWithEntity
|
/// [interact event]: crate::client::EntityEvent::InteractWithEntity
|
||||||
pub fn hitbox(&self) -> Aabb<f64> {
|
pub fn hitbox(&self) -> Aabb<f64> {
|
||||||
let dims = match &self.variants {
|
let dims = match &self.variants {
|
||||||
TrackedData::Allay(_) => [0.6, 0.35, 0.6],
|
TrackedData::Allay(_) => [0.6, 0.35, 0.6],
|
||||||
|
|
|
@ -1,256 +1,11 @@
|
||||||
//! Primitive types used in getters and setters on entities.
|
//! Contains the [`TrackedData`] and the types for each variant.
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
#![allow(clippy::all, missing_docs, trivial_numeric_casts)]
|
||||||
|
|
||||||
use crate::protocol_inner::{Decode, Encode, VarInt};
|
use crate::block::{BlockPos, BlockState};
|
||||||
|
use crate::entity::types::*;
|
||||||
|
use crate::protocol_inner::{Encode, VarInt};
|
||||||
|
use crate::text::Text;
|
||||||
|
use crate::uuid::Uuid;
|
||||||
|
|
||||||
/// Represents an optional `u32` value excluding [`u32::MAX`].
|
include!(concat!(env!("OUT_DIR"), "/entity.rs"));
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub struct OptionalInt(u32);
|
|
||||||
|
|
||||||
impl OptionalInt {
|
|
||||||
/// Returns `None` iff `n` is Some(u32::MAX).
|
|
||||||
pub fn new(n: impl Into<Option<u32>>) -> Option<Self> {
|
|
||||||
match n.into() {
|
|
||||||
None => Some(Self(0)),
|
|
||||||
Some(u32::MAX) => None,
|
|
||||||
Some(n) => Some(Self(n + 1)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(self) -> Option<u32> {
|
|
||||||
self.0.checked_sub(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for OptionalInt {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(self.0 as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decode for OptionalInt {
|
|
||||||
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
|
|
||||||
Ok(Self(VarInt::decode(r)?.0 as u32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
pub struct EulerAngle {
|
|
||||||
pub pitch: f32,
|
|
||||||
pub yaw: f32,
|
|
||||||
pub roll: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EulerAngle {
|
|
||||||
pub fn new(pitch: f32, yaw: f32, roll: f32) -> Self {
|
|
||||||
Self { pitch, yaw, roll }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for EulerAngle {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
self.pitch.encode(w)?;
|
|
||||||
self.yaw.encode(w)?;
|
|
||||||
self.roll.encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
pub enum Facing {
|
|
||||||
Down,
|
|
||||||
Up,
|
|
||||||
North,
|
|
||||||
South,
|
|
||||||
West,
|
|
||||||
East,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for Facing {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
pub struct VillagerData {
|
|
||||||
pub kind: VillagerKind,
|
|
||||||
pub profession: VillagerProfession,
|
|
||||||
pub level: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VillagerData {
|
|
||||||
pub const fn new(kind: VillagerKind, profession: VillagerProfession, level: i32) -> Self {
|
|
||||||
Self {
|
|
||||||
kind,
|
|
||||||
profession,
|
|
||||||
level,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for VillagerData {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
kind: Default::default(),
|
|
||||||
profession: Default::default(),
|
|
||||||
level: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for VillagerData {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(self.kind as i32).encode(w)?;
|
|
||||||
VarInt(self.profession as i32).encode(w)?;
|
|
||||||
VarInt(self.level).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum VillagerKind {
|
|
||||||
Desert,
|
|
||||||
Jungle,
|
|
||||||
#[default]
|
|
||||||
Plains,
|
|
||||||
Savanna,
|
|
||||||
Snow,
|
|
||||||
Swamp,
|
|
||||||
Taiga,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum VillagerProfession {
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Armorer,
|
|
||||||
Butcher,
|
|
||||||
Cartographer,
|
|
||||||
Cleric,
|
|
||||||
Farmer,
|
|
||||||
Fisherman,
|
|
||||||
Fletcher,
|
|
||||||
Leatherworker,
|
|
||||||
Librarian,
|
|
||||||
Mason,
|
|
||||||
Nitwit,
|
|
||||||
Shepherd,
|
|
||||||
Toolsmith,
|
|
||||||
Weaponsmith,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum Pose {
|
|
||||||
#[default]
|
|
||||||
Standing,
|
|
||||||
FallFlying,
|
|
||||||
Sleeping,
|
|
||||||
Swimming,
|
|
||||||
SpinAttack,
|
|
||||||
Sneaking,
|
|
||||||
LongJumping,
|
|
||||||
Dying,
|
|
||||||
Croaking,
|
|
||||||
UsingTongue,
|
|
||||||
Roaring,
|
|
||||||
Sniffing,
|
|
||||||
Emerging,
|
|
||||||
Digging,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for Pose {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main hand of a player.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum MainHand {
|
|
||||||
Left,
|
|
||||||
#[default]
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for MainHand {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
(*self as u8).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum BoatKind {
|
|
||||||
#[default]
|
|
||||||
Oak,
|
|
||||||
Spruce,
|
|
||||||
Birch,
|
|
||||||
Jungle,
|
|
||||||
Acacia,
|
|
||||||
DarkOak,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for BoatKind {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum CatKind {
|
|
||||||
Tabby,
|
|
||||||
#[default]
|
|
||||||
Black,
|
|
||||||
Red,
|
|
||||||
Siamese,
|
|
||||||
BritishShorthair,
|
|
||||||
Calico,
|
|
||||||
Persian,
|
|
||||||
Ragdoll,
|
|
||||||
White,
|
|
||||||
Jellie,
|
|
||||||
AllBlack,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for CatKind {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum FrogKind {
|
|
||||||
#[default]
|
|
||||||
Temperate,
|
|
||||||
Warm,
|
|
||||||
Cold,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for FrogKind {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
|
||||||
pub enum PaintingKind {
|
|
||||||
#[default]
|
|
||||||
Kebab, // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for PaintingKind {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
pub enum Particle {
|
|
||||||
EntityEffect = 21,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encode for Particle {
|
|
||||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
||||||
VarInt(*self as i32).encode(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
//! Contains the [`TrackedData`] and the types for each variant.
|
|
||||||
|
|
||||||
#![allow(clippy::all, missing_docs, trivial_numeric_casts)]
|
|
||||||
|
|
||||||
use crate::block::{BlockPos, BlockState};
|
|
||||||
use crate::entity::data::*;
|
|
||||||
use crate::protocol_inner::{Encode, VarInt};
|
|
||||||
use crate::text::Text;
|
|
||||||
use crate::uuid::Uuid;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/entity.rs"));
|
|
256
src/entity/types.rs
Normal file
256
src/entity/types.rs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
//! Primitive types used in getters and setters on entities.
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use crate::protocol_inner::{Decode, Encode, VarInt};
|
||||||
|
|
||||||
|
/// Represents an optional `u32` value excluding [`u32::MAX`].
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub struct OptionalInt(u32);
|
||||||
|
|
||||||
|
impl OptionalInt {
|
||||||
|
/// Returns `None` iff `n` is Some(u32::MAX).
|
||||||
|
pub fn new(n: impl Into<Option<u32>>) -> Option<Self> {
|
||||||
|
match n.into() {
|
||||||
|
None => Some(Self(0)),
|
||||||
|
Some(u32::MAX) => None,
|
||||||
|
Some(n) => Some(Self(n + 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self) -> Option<u32> {
|
||||||
|
self.0.checked_sub(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for OptionalInt {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(self.0 as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for OptionalInt {
|
||||||
|
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
|
||||||
|
Ok(Self(VarInt::decode(r)?.0 as u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||||
|
pub struct EulerAngle {
|
||||||
|
pub pitch: f32,
|
||||||
|
pub yaw: f32,
|
||||||
|
pub roll: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EulerAngle {
|
||||||
|
pub fn new(pitch: f32, yaw: f32, roll: f32) -> Self {
|
||||||
|
Self { pitch, yaw, roll }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for EulerAngle {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
self.pitch.encode(w)?;
|
||||||
|
self.yaw.encode(w)?;
|
||||||
|
self.roll.encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub enum Facing {
|
||||||
|
Down,
|
||||||
|
Up,
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
West,
|
||||||
|
East,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Facing {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub struct VillagerData {
|
||||||
|
pub kind: VillagerKind,
|
||||||
|
pub profession: VillagerProfession,
|
||||||
|
pub level: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VillagerData {
|
||||||
|
pub const fn new(kind: VillagerKind, profession: VillagerProfession, level: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
profession,
|
||||||
|
level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VillagerData {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
kind: Default::default(),
|
||||||
|
profession: Default::default(),
|
||||||
|
level: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for VillagerData {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(self.kind as i32).encode(w)?;
|
||||||
|
VarInt(self.profession as i32).encode(w)?;
|
||||||
|
VarInt(self.level).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum VillagerKind {
|
||||||
|
Desert,
|
||||||
|
Jungle,
|
||||||
|
#[default]
|
||||||
|
Plains,
|
||||||
|
Savanna,
|
||||||
|
Snow,
|
||||||
|
Swamp,
|
||||||
|
Taiga,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum VillagerProfession {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Armorer,
|
||||||
|
Butcher,
|
||||||
|
Cartographer,
|
||||||
|
Cleric,
|
||||||
|
Farmer,
|
||||||
|
Fisherman,
|
||||||
|
Fletcher,
|
||||||
|
Leatherworker,
|
||||||
|
Librarian,
|
||||||
|
Mason,
|
||||||
|
Nitwit,
|
||||||
|
Shepherd,
|
||||||
|
Toolsmith,
|
||||||
|
Weaponsmith,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum Pose {
|
||||||
|
#[default]
|
||||||
|
Standing,
|
||||||
|
FallFlying,
|
||||||
|
Sleeping,
|
||||||
|
Swimming,
|
||||||
|
SpinAttack,
|
||||||
|
Sneaking,
|
||||||
|
LongJumping,
|
||||||
|
Dying,
|
||||||
|
Croaking,
|
||||||
|
UsingTongue,
|
||||||
|
Roaring,
|
||||||
|
Sniffing,
|
||||||
|
Emerging,
|
||||||
|
Digging,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Pose {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main hand of a player.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum MainArm {
|
||||||
|
Left,
|
||||||
|
#[default]
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for MainArm {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
(*self as u8).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum BoatKind {
|
||||||
|
#[default]
|
||||||
|
Oak,
|
||||||
|
Spruce,
|
||||||
|
Birch,
|
||||||
|
Jungle,
|
||||||
|
Acacia,
|
||||||
|
DarkOak,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for BoatKind {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum CatKind {
|
||||||
|
Tabby,
|
||||||
|
#[default]
|
||||||
|
Black,
|
||||||
|
Red,
|
||||||
|
Siamese,
|
||||||
|
BritishShorthair,
|
||||||
|
Calico,
|
||||||
|
Persian,
|
||||||
|
Ragdoll,
|
||||||
|
White,
|
||||||
|
Jellie,
|
||||||
|
AllBlack,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for CatKind {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum FrogKind {
|
||||||
|
#[default]
|
||||||
|
Temperate,
|
||||||
|
Warm,
|
||||||
|
Cold,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for FrogKind {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
|
pub enum PaintingKind {
|
||||||
|
#[default]
|
||||||
|
Kebab, // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for PaintingKind {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub enum Particle {
|
||||||
|
EntityEffect = 21,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Particle {
|
||||||
|
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||||
|
VarInt(*self as i32).encode(w)
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,7 @@ impl PlayerList {
|
||||||
game_mode,
|
game_mode,
|
||||||
ping,
|
ping,
|
||||||
display_name: display_name.into(),
|
display_name: display_name.into(),
|
||||||
flags: EntryFlags::new().with_created_this_tick(true),
|
bits: EntryBits::new().with_created_this_tick(true),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
e.set_game_mode(game_mode);
|
e.set_game_mode(game_mode);
|
||||||
|
@ -84,7 +84,7 @@ impl PlayerList {
|
||||||
game_mode,
|
game_mode,
|
||||||
ping,
|
ping,
|
||||||
display_name: display_name.into(),
|
display_name: display_name.into(),
|
||||||
flags: EntryFlags::new().with_created_this_tick(true),
|
bits: EntryBits::new().with_created_this_tick(true),
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ impl PlayerList {
|
||||||
let mut display_name = Vec::new();
|
let mut display_name = Vec::new();
|
||||||
|
|
||||||
for (&uuid, e) in self.entries.iter() {
|
for (&uuid, e) in self.entries.iter() {
|
||||||
if e.flags.created_this_tick() {
|
if e.bits.created_this_tick() {
|
||||||
let mut properties = Vec::new();
|
let mut properties = Vec::new();
|
||||||
if let Some(textures) = &e.textures {
|
if let Some(textures) = &e.textures {
|
||||||
properties.push(Property {
|
properties.push(Property {
|
||||||
|
@ -234,15 +234,15 @@ impl PlayerList {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.flags.modified_game_mode() {
|
if e.bits.modified_game_mode() {
|
||||||
game_mode.push((uuid, e.game_mode));
|
game_mode.push((uuid, e.game_mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.flags.modified_ping() {
|
if e.bits.modified_ping() {
|
||||||
ping.push((uuid, VarInt(e.ping)));
|
ping.push((uuid, VarInt(e.ping)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.flags.modified_display_name() {
|
if e.bits.modified_display_name() {
|
||||||
display_name.push((uuid, e.display_name.clone()));
|
display_name.push((uuid, e.display_name.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ impl PlayerList {
|
||||||
|
|
||||||
pub(crate) fn update(&mut self) {
|
pub(crate) fn update(&mut self) {
|
||||||
for e in self.entries.values_mut() {
|
for e in self.entries.values_mut() {
|
||||||
e.flags = EntryFlags(0);
|
e.bits = EntryBits::new();
|
||||||
}
|
}
|
||||||
self.removed.clear();
|
self.removed.clear();
|
||||||
self.modified_header_or_footer = false;
|
self.modified_header_or_footer = false;
|
||||||
|
@ -290,7 +290,17 @@ pub struct PlayerListEntry {
|
||||||
game_mode: GameMode,
|
game_mode: GameMode,
|
||||||
ping: i32,
|
ping: i32,
|
||||||
display_name: Option<Text>,
|
display_name: Option<Text>,
|
||||||
flags: EntryFlags,
|
bits: EntryBits,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield(u8)]
|
||||||
|
struct EntryBits {
|
||||||
|
created_this_tick: bool,
|
||||||
|
modified_game_mode: bool,
|
||||||
|
modified_ping: bool,
|
||||||
|
modified_display_name: bool,
|
||||||
|
#[bits(4)]
|
||||||
|
_pad: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerListEntry {
|
impl PlayerListEntry {
|
||||||
|
@ -313,7 +323,7 @@ impl PlayerListEntry {
|
||||||
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
||||||
if self.game_mode != game_mode {
|
if self.game_mode != game_mode {
|
||||||
self.game_mode = game_mode;
|
self.game_mode = game_mode;
|
||||||
self.flags.set_modified_game_mode(true);
|
self.bits.set_modified_game_mode(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +336,7 @@ impl PlayerListEntry {
|
||||||
pub fn set_ping(&mut self, ping: i32) {
|
pub fn set_ping(&mut self, ping: i32) {
|
||||||
if self.ping != ping {
|
if self.ping != ping {
|
||||||
self.ping = ping;
|
self.ping = ping;
|
||||||
self.flags.set_modified_ping(true);
|
self.bits.set_modified_ping(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,17 +350,7 @@ impl PlayerListEntry {
|
||||||
let display_name = display_name.into();
|
let display_name = display_name.into();
|
||||||
if self.display_name != display_name {
|
if self.display_name != display_name {
|
||||||
self.display_name = display_name;
|
self.display_name = display_name;
|
||||||
self.flags.set_modified_display_name(true);
|
self.bits.set_modified_display_name(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitfield(u8)]
|
|
||||||
struct EntryFlags {
|
|
||||||
created_this_tick: bool,
|
|
||||||
modified_game_mode: bool,
|
|
||||||
modified_ping: bool,
|
|
||||||
modified_display_name: bool,
|
|
||||||
#[bits(4)]
|
|
||||||
_pad: u8,
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ use paste::paste;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
// use {def_bitfield, def_enum, def_struct};
|
|
||||||
|
|
||||||
|
// use {def_bitfield, def_enum, def_struct};
|
||||||
use crate::block_pos::BlockPos;
|
use crate::block_pos::BlockPos;
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use crate::protocol_inner::{
|
use crate::protocol_inner::{
|
||||||
|
@ -121,8 +121,7 @@ macro_rules! def_struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
#[allow(unused)]
|
pub const PACKET_ID: i32 = $id;
|
||||||
const PACKET_ID: i32 = $id;
|
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
||||||
|
@ -231,8 +230,7 @@ macro_rules! def_enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
#[allow(unused)]
|
pub const PACKET_ID: i32 = $id;
|
||||||
const PACKET_ID: i32 = $id;
|
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -289,7 +287,7 @@ macro_rules! def_bitfield {
|
||||||
$(
|
$(
|
||||||
#[doc = "Gets the " $bit " bit on this bitfield.\n"]
|
#[doc = "Gets the " $bit " bit on this bitfield.\n"]
|
||||||
$(#[$bit_attrs])*
|
$(#[$bit_attrs])*
|
||||||
pub fn [<get_ $bit:snake>](self) -> bool {
|
pub fn $bit(self) -> bool {
|
||||||
self.0 & <$inner_ty>::one() << <$inner_ty>::from($offset) != <$inner_ty>::zero()
|
self.0 & <$inner_ty>::one() << <$inner_ty>::from($offset) != <$inner_ty>::zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +311,7 @@ macro_rules! def_bitfield {
|
||||||
let mut s = f.debug_struct(stringify!($name));
|
let mut s = f.debug_struct(stringify!($name));
|
||||||
paste! {
|
paste! {
|
||||||
$(
|
$(
|
||||||
s.field(stringify!($bit), &self. [<get_ $bit:snake>]());
|
s.field(stringify!($bit), &self. $bit());
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
s.finish()
|
s.finish()
|
||||||
|
|
|
@ -40,7 +40,9 @@ use crate::protocol_inner::packets::c2s::login::{
|
||||||
};
|
};
|
||||||
use crate::protocol_inner::packets::c2s::play::C2sPlayPacket;
|
use crate::protocol_inner::packets::c2s::play::C2sPlayPacket;
|
||||||
use crate::protocol_inner::packets::c2s::status::{QueryPing, QueryRequest};
|
use crate::protocol_inner::packets::c2s::status::{QueryPing, QueryRequest};
|
||||||
use crate::protocol_inner::packets::s2c::login::{EncryptionRequest, LoginCompression, LoginDisconnect, LoginSuccess};
|
use crate::protocol_inner::packets::s2c::login::{
|
||||||
|
EncryptionRequest, LoginCompression, LoginDisconnect, LoginSuccess,
|
||||||
|
};
|
||||||
use crate::protocol_inner::packets::s2c::play::S2cPlayPacket;
|
use crate::protocol_inner::packets::s2c::play::S2cPlayPacket;
|
||||||
use crate::protocol_inner::packets::s2c::status::{QueryPong, QueryResponse};
|
use crate::protocol_inner::packets::s2c::status::{QueryPong, QueryResponse};
|
||||||
use crate::protocol_inner::packets::Property;
|
use crate::protocol_inner::packets::Property;
|
||||||
|
@ -459,12 +461,7 @@ fn join_player<C: Config>(server: &mut Server<C>, msg: NewClientMessage) {
|
||||||
|
|
||||||
let _ = msg.reply.send(s2c_packet_channels);
|
let _ = msg.reply.send(s2c_packet_channels);
|
||||||
|
|
||||||
let client = Client::new(
|
let client = Client::new(c2s_packet_channels, msg.ncd, C::ClientState::default());
|
||||||
c2s_packet_channels,
|
|
||||||
&server.shared,
|
|
||||||
msg.ncd,
|
|
||||||
C::ClientState::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
server.clients.insert(client);
|
server.clients.insert(client);
|
||||||
}
|
}
|
||||||
|
@ -713,30 +710,28 @@ async fn handle_login<C: Config>(
|
||||||
c.enc.enable_compression(compression_threshold);
|
c.enc.enable_compression(compression_threshold);
|
||||||
c.dec.enable_compression(compression_threshold);
|
c.dec.enable_compression(compression_threshold);
|
||||||
|
|
||||||
let npd = NewClientData {
|
let ncd = NewClientData {
|
||||||
uuid,
|
uuid,
|
||||||
username,
|
username,
|
||||||
textures,
|
textures,
|
||||||
remote_addr,
|
remote_addr,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(reason) = server.0.cfg.login(server, &npd).await {
|
if let Err(reason) = server.0.cfg.login(server, &ncd).await {
|
||||||
log::info!("Disconnect at login: \"{reason}\"");
|
log::info!("Disconnect at login: \"{reason}\"");
|
||||||
c.enc
|
c.enc.write_packet(&LoginDisconnect { reason }).await?;
|
||||||
.write_packet(&LoginDisconnect { reason })
|
|
||||||
.await?;
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
c.enc
|
c.enc
|
||||||
.write_packet(&LoginSuccess {
|
.write_packet(&LoginSuccess {
|
||||||
uuid: npd.uuid,
|
uuid: ncd.uuid,
|
||||||
username: npd.username.clone().into(),
|
username: ncd.username.clone().into(),
|
||||||
properties: Vec::new(),
|
properties: Vec::new(),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Some(npd))
|
Ok(Some(ncd))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_play<C: Config>(
|
async fn handle_play<C: Config>(
|
||||||
|
|
Loading…
Reference in a new issue