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! {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Event {
|
||||
pub enum EntityEvent {
|
||||
#(#event_variants,)*
|
||||
}
|
||||
|
||||
impl Event {
|
||||
impl EntityEvent {
|
||||
pub(crate) fn status_or_animation(self) -> StatusOrAnimation {
|
||||
match self {
|
||||
#(#status_arms)*
|
||||
|
|
|
@ -16,11 +16,13 @@ pub fn main() -> anyhow::Result<()> {
|
|||
(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")?;
|
||||
|
||||
for (g, file_name) in generators {
|
||||
println!("cargo:rerun-if-changed=extracted/{file_name}");
|
||||
|
||||
let path = Path::new(&out_dir).join(file_name);
|
||||
let code = g()?.to_string();
|
||||
fs::write(&path, &code)?;
|
||||
|
|
|
@ -3,15 +3,15 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
|
||||
use log::LevelFilter;
|
||||
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::dimension::DimensionId;
|
||||
use valence::entity::data::Pose;
|
||||
use valence::entity::{TrackedData, EntityId, EntityKind, Event as EntityEvent};
|
||||
use valence::entity::types::Pose;
|
||||
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||
use valence::text::{Color, TextFormat};
|
||||
use valence::{async_trait, Ticks};
|
||||
use vek::Vec3;
|
||||
use vek::{Vec2, Vec3};
|
||||
|
||||
pub fn main() -> ShutdownResult {
|
||||
env_logger::Builder::new()
|
||||
|
@ -119,7 +119,7 @@ impl Config for Game {
|
|||
let current_tick = server.shared.current_tick();
|
||||
|
||||
server.clients.retain(|client_id, client| {
|
||||
if client.created_tick() == current_tick {
|
||||
if client.created_this_tick() {
|
||||
if self
|
||||
.player_count
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||
|
@ -131,6 +131,23 @@ impl Config for Game {
|
|||
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.set_game_mode(GameMode::Survival);
|
||||
client.teleport(
|
||||
|
@ -152,17 +169,6 @@ impl Config for Game {
|
|||
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());
|
||||
if self.player_count.load(Ordering::SeqCst) <= 1 {
|
||||
client.send_message("Have another player join the game with you.".italic());
|
||||
|
@ -176,41 +182,6 @@ impl Config for Game {
|
|||
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 {
|
||||
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());
|
||||
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());
|
||||
match client_event_boilerplate(client, player) {
|
||||
Some(ClientEvent::StartSprinting) => {
|
||||
client.state.extra_knockback = true;
|
||||
}
|
||||
Some(ClientEvent::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;
|
||||
|
||||
if let TrackedData::Player(player) = player.view_mut() {
|
||||
if client.is_sneaking() {
|
||||
player.set_pose(Pose::Sneaking);
|
||||
} else {
|
||||
player.set_pose(Pose::Standing);
|
||||
client.state.extra_knockback = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {}
|
||||
None => break,
|
||||
}
|
||||
|
||||
player.set_sprinting(client.is_sprinting());
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
for (_, e) in server.entities.iter_mut() {
|
||||
if e.state.attacked {
|
||||
e.state.attacked = false;
|
||||
let victim = server.clients.get_mut(e.state.client).unwrap();
|
||||
for (_, entity) in server.entities.iter_mut() {
|
||||
if entity.state.attacked {
|
||||
entity.state.attacked = false;
|
||||
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_y = if e.state.extra_knockback {
|
||||
8.432
|
||||
} else {
|
||||
6.432
|
||||
};
|
||||
let knockback_xz = if entity.state.extra_knockback {
|
||||
18.0
|
||||
} else {
|
||||
8.0
|
||||
};
|
||||
let knockback_y = if entity.state.extra_knockback {
|
||||
8.432
|
||||
} else {
|
||||
6.432
|
||||
};
|
||||
|
||||
vel.x *= knockback_xz;
|
||||
vel.y = knockback_y;
|
||||
vel.z *= knockback_xz;
|
||||
let vel = Vec3::new(dir.x * knockback_xz, knockback_y, dir.y * knockback_xz);
|
||||
victim.set_velocity(vel.as_());
|
||||
|
||||
victim.set_velocity(victim.velocity() / 2.0 + vel.as_());
|
||||
|
||||
e.trigger_event(EntityEvent::DamageFromGenericSource);
|
||||
e.trigger_event(EntityEvent::Damage);
|
||||
victim.trigger_entity_event(EntityEvent::DamageFromGenericSource);
|
||||
victim.trigger_entity_event(EntityEvent::Damage);
|
||||
entity.push_event(EntityEvent::DamageFromGenericSource);
|
||||
entity.push_event(EntityEvent::Damage);
|
||||
victim.push_entity_event(EntityEvent::DamageFromGenericSource);
|
||||
victim.push_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 valence::biome::Biome;
|
||||
use valence::block::BlockState;
|
||||
use valence::client::{Event, Hand};
|
||||
use valence::client::{Client, ClientEvent, Hand};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
use valence::dimension::{Dimension, DimensionId};
|
||||
use valence::entity::data::Pose;
|
||||
use valence::entity::{TrackedData, EntityId, EntityKind, Event as EntityEvent};
|
||||
use valence::entity::types::Pose;
|
||||
use valence::entity::{Entity, EntityEvent, EntityId, EntityKind, TrackedData};
|
||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||
use valence::text::{Color, TextFormat};
|
||||
use valence::{async_trait, ident};
|
||||
|
@ -42,12 +42,6 @@ struct ServerState {
|
|||
board_buf: Box<[bool]>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClientState {
|
||||
/// The client's player entity.
|
||||
player: EntityId,
|
||||
}
|
||||
|
||||
const MAX_PLAYERS: usize = 10;
|
||||
|
||||
const SIZE_X: usize = 100;
|
||||
|
@ -57,7 +51,7 @@ const BOARD_Y: i32 = 50;
|
|||
#[async_trait]
|
||||
impl Config for Game {
|
||||
type ChunkState = ();
|
||||
type ClientState = ClientState;
|
||||
type ClientState = EntityId;
|
||||
type EntityState = ();
|
||||
type ServerState = ServerState;
|
||||
type WorldState = ();
|
||||
|
@ -121,7 +115,7 @@ impl Config for Game {
|
|||
];
|
||||
|
||||
server.clients.retain(|_, client| {
|
||||
if client.created_tick() == server.shared.current_tick() {
|
||||
if client.created_this_tick() {
|
||||
if self
|
||||
.player_count
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||
|
@ -133,6 +127,17 @@ impl Config for Game {
|
|||
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.teleport(spawn_pos, 0.0, 0.0);
|
||||
|
||||
|
@ -145,65 +150,35 @@ impl Config for Game {
|
|||
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("Hold the left mouse button to bring blocks to life.".italic());
|
||||
}
|
||||
|
||||
if client.is_disconnected() {
|
||||
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());
|
||||
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 {
|
||||
client.teleport(spawn_pos, client.yaw(), client.pitch());
|
||||
}
|
||||
|
||||
while let Some(event) = client.pop_event() {
|
||||
match event {
|
||||
Event::Digging { position, .. } => {
|
||||
if (0..SIZE_X as i32).contains(&position.x)
|
||||
&& (0..SIZE_Z as i32).contains(&position.z)
|
||||
&& position.y == BOARD_Y
|
||||
{
|
||||
server.state.board
|
||||
[position.x as usize + position.z as usize * SIZE_X] = true;
|
||||
}
|
||||
while let Some(event) = client_event_boilerplate(client, player) {
|
||||
if let ClientEvent::Digging { position, .. } = event {
|
||||
if (0..SIZE_X as i32).contains(&position.x)
|
||||
&& (0..SIZE_Z as i32).contains(&position.z)
|
||||
&& position.y == BOARD_Y
|
||||
{
|
||||
server.state.board[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
|
||||
});
|
||||
|
||||
|
@ -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 valence::async_trait;
|
||||
use valence::block::{BlockPos, BlockState};
|
||||
use valence::client::GameMode;
|
||||
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
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::text::{Color, TextFormat};
|
||||
use valence::util::to_yaw_and_pitch;
|
||||
|
@ -43,7 +44,7 @@ const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -25);
|
|||
#[async_trait]
|
||||
impl Config for Game {
|
||||
type ChunkState = ();
|
||||
type ClientState = ();
|
||||
type ClientState = EntityId;
|
||||
type EntityState = ();
|
||||
type ServerState = ServerState;
|
||||
type WorldState = ();
|
||||
|
@ -92,10 +93,10 @@ impl Config for Game {
|
|||
}
|
||||
|
||||
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| {
|
||||
if client.created_tick() == server.shared.current_tick() {
|
||||
if client.created_this_tick() {
|
||||
if self
|
||||
.player_count
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||
|
@ -107,6 +108,17 @@ impl Config for Game {
|
|||
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.set_game_mode(GameMode::Creative);
|
||||
client.teleport(
|
||||
|
@ -132,9 +144,18 @@ impl Config for Game {
|
|||
if client.is_disconnected() {
|
||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||
world.meta.player_list_mut().remove(client.uuid());
|
||||
server.entities.delete(client.state);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let entity = server
|
||||
.entities
|
||||
.get_mut(client.state)
|
||||
.expect("missing player entity");
|
||||
|
||||
while client_event_boilerplate(client, entity).is_some() {}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
|
@ -153,7 +174,7 @@ impl Config for Game {
|
|||
.map(|c| c.1.position())
|
||||
.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);
|
||||
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
||||
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 valence::async_trait;
|
||||
use valence::block::{BlockPos, BlockState};
|
||||
use valence::client::GameMode;
|
||||
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
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::spatial_index::RaycastHit;
|
||||
use valence::text::{Color, TextFormat};
|
||||
|
@ -41,7 +42,7 @@ const PLAYER_EYE_HEIGHT: f64 = 1.6;
|
|||
#[async_trait]
|
||||
impl Config for Game {
|
||||
type ChunkState = ();
|
||||
type ClientState = ();
|
||||
type ClientState = EntityId;
|
||||
/// `true` for entities that have been intersected with.
|
||||
type EntityState = bool;
|
||||
type ServerState = ();
|
||||
|
@ -99,7 +100,7 @@ impl Config for Game {
|
|||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||
|
||||
server.clients.retain(|_, client| {
|
||||
if client.created_tick() == server.shared.current_tick() {
|
||||
if client.created_this_tick() {
|
||||
if self
|
||||
.player_count
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||
|
@ -111,6 +112,17 @@ impl Config for Game {
|
|||
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.set_game_mode(GameMode::Creative);
|
||||
client.teleport(
|
||||
|
@ -142,6 +154,8 @@ impl Config for Game {
|
|||
if client.is_disconnected() {
|
||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||
world.meta.player_list_mut().remove(client.uuid());
|
||||
server.entities.delete(client.state);
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
for (_, e) in server.entities.iter_mut() {
|
||||
let intersected = e.state;
|
||||
if let TrackedData::Sheep(sheep) = &mut e.view_mut() {
|
||||
if let TrackedData::Sheep(sheep) = &mut e.data_mut() {
|
||||
if intersected {
|
||||
sheep.set_color(5);
|
||||
} 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::block::{BlockState, PropName, PropValue};
|
||||
use valence::chunk::ChunkPos;
|
||||
use valence::client::GameMode;
|
||||
use valence::client::{Client, ClientEvent, GameMode, Hand};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
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::text::{Color, TextFormat};
|
||||
use valence::util::chunks_in_view_distance;
|
||||
|
@ -51,7 +53,7 @@ const MAX_PLAYERS: usize = 10;
|
|||
#[async_trait]
|
||||
impl Config for Game {
|
||||
type ChunkState = ();
|
||||
type ClientState = ();
|
||||
type ClientState = EntityId;
|
||||
type EntityState = ();
|
||||
type ServerState = ();
|
||||
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));
|
||||
|
||||
server.clients.retain(|_, client| {
|
||||
if client.created_tick() == server.shared.current_tick() {
|
||||
if client.created_this_tick() {
|
||||
if self
|
||||
.player_count
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||
|
@ -102,9 +104,19 @@ impl Config for Game {
|
|||
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.set_game_mode(GameMode::Creative);
|
||||
client.set_max_view_distance(32);
|
||||
client.teleport([0.0, 200.0, 0.0], 0.0, 0.0);
|
||||
|
||||
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("Explore this infinite procedurally generated terrain.".italic());
|
||||
}
|
||||
|
||||
if client.is_disconnected() {
|
||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||
world.meta.player_list_mut().remove(client.uuid());
|
||||
server.entities.delete(client.state);
|
||||
|
||||
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 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 {
|
||||
(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::sync::Semaphore;
|
||||
use valence::protocol::codec::Decoder;
|
||||
use valence::protocol::packets::handshake::{Handshake, HandshakeNextState};
|
||||
use valence::protocol::packets::login::c2s::{EncryptionResponse, LoginStart};
|
||||
use valence::protocol::packets::login::s2c::{LoginSuccess, S2cLoginPacket};
|
||||
use valence::protocol::packets::c2s::handshake::{Handshake, HandshakeNextState};
|
||||
use valence::protocol::packets::c2s::login::{EncryptionResponse, LoginStart};
|
||||
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::status::c2s::{QueryPing, QueryRequest};
|
||||
use valence::protocol::packets::status::s2c::{QueryPong, QueryResponse};
|
||||
use valence::protocol::packets::s2c::status::{QueryPong, QueryResponse};
|
||||
use valence::protocol::packets::{DecodePacket, EncodePacket};
|
||||
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::time::Duration;
|
||||
|
||||
use bitfield_struct::bitfield;
|
||||
pub use bitfield_struct::bitfield;
|
||||
pub use event::*;
|
||||
use flume::{Receiver, Sender, TrySendError};
|
||||
use rayon::iter::ParallelIterator;
|
||||
|
@ -16,10 +16,9 @@ use crate::block_pos::BlockPos;
|
|||
use crate::chunk_pos::ChunkPos;
|
||||
use crate::config::Config;
|
||||
use crate::dimension::DimensionId;
|
||||
use crate::entity::kinds::Player;
|
||||
use crate::entity::data::Player;
|
||||
use crate::entity::{
|
||||
velocity_to_packet_units, Entities, EntityId, EntityKind, Event as EntityEvent,
|
||||
StatusOrAnimation,
|
||||
velocity_to_packet_units, Entities, EntityEvent, EntityId, EntityKind, StatusOrAnimation,
|
||||
};
|
||||
use crate::player_textures::SignedPlayerTextures;
|
||||
use crate::protocol_inner::packets::c2s::play::{
|
||||
|
@ -42,9 +41,9 @@ use crate::slotmap::{Key, SlotMap};
|
|||
use crate::text::Text;
|
||||
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
|
||||
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;
|
||||
|
||||
/// 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>) {
|
||||
let (id, client) = self.sm.insert(client);
|
||||
(ClientId(id), client)
|
||||
let (k, client) = self.sm.insert(client);
|
||||
(ClientId(k), client)
|
||||
}
|
||||
|
||||
/// Removes a client from the server.
|
||||
|
@ -74,7 +73,7 @@ impl<C: Config> Clients<C> {
|
|||
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`.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// managed by the library.
|
||||
///
|
||||
|
@ -154,30 +153,28 @@ impl ClientId {
|
|||
/// cannot break blocks, hurt entities, or see other clients. Interactions with
|
||||
/// the server must be handled explicitly with [`Self::pop_event`].
|
||||
///
|
||||
/// Additionally, clients posess [`Player`] entity data which is only visible to
|
||||
/// themselves. This can be accessed with [`Self::player`] and
|
||||
/// Additionally, clients possess [`Player`] entity data which is only visible
|
||||
/// to themselves. This can be accessed with [`Self::player`] and
|
||||
/// [`Self::player_mut`].
|
||||
///
|
||||
/// # The Difference Between a "Client" and a "Player"
|
||||
///
|
||||
/// 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
|
||||
/// was done primarily to enable multithreaded client updates.
|
||||
/// In Valence however, clients and players are decoupled. This separation
|
||||
/// allows for greater flexibility and parallelism.
|
||||
pub struct Client<C: Config> {
|
||||
/// Custom state.
|
||||
pub state: C::ClientState,
|
||||
/// Setting this to `None` disconnects the client.
|
||||
send: SendOpt,
|
||||
recv: Receiver<C2sPlayPacket>,
|
||||
/// The tick this client was created.
|
||||
created_tick: Ticks,
|
||||
uuid: Uuid,
|
||||
username: String,
|
||||
textures: Option<SignedPlayerTextures>,
|
||||
world: WorldId,
|
||||
new_position: Vec3<f64>,
|
||||
position: Vec3<f64>,
|
||||
old_position: Vec3<f64>,
|
||||
/// Measured in m/s.
|
||||
velocity: Vec3<f32>,
|
||||
|
@ -185,6 +182,7 @@ pub struct Client<C: Config> {
|
|||
yaw: f32,
|
||||
/// Measured in degrees
|
||||
pitch: f32,
|
||||
view_distance: u8,
|
||||
/// Counts up as teleports are made.
|
||||
teleport_id_counter: u32,
|
||||
/// 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_yaw: f32,
|
||||
death_location: Option<(DimensionId, BlockPos)>,
|
||||
events: VecDeque<Event>,
|
||||
events: VecDeque<ClientEvent>,
|
||||
/// The ID of the last keepalive sent.
|
||||
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.
|
||||
/// This is used to determine what entity create/destroy packets should be
|
||||
/// sent.
|
||||
|
@ -212,21 +208,15 @@ pub struct Client<C: Config> {
|
|||
msgs_to_send: Vec<Text>,
|
||||
attack_speed: f64,
|
||||
movement_speed: f64,
|
||||
flags: ClientFlags,
|
||||
bits: ClientBits,
|
||||
/// The data for the client's own player entity.
|
||||
player_data: Player,
|
||||
entity_events: Vec<EntityEvent>,
|
||||
}
|
||||
|
||||
#[bitfield(u16)]
|
||||
pub(crate) struct ClientFlags {
|
||||
struct ClientBits {
|
||||
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,
|
||||
/// If spawn_position or spawn_position_yaw were modified this tick.
|
||||
modified_spawn_position: bool,
|
||||
|
@ -236,33 +226,34 @@ pub(crate) struct ClientFlags {
|
|||
attack_speed_modified: bool,
|
||||
movement_speed_modified: bool,
|
||||
velocity_modified: bool,
|
||||
#[bits(4)]
|
||||
created_this_tick: bool,
|
||||
view_distance_modified: bool,
|
||||
#[bits(6)]
|
||||
_pad: u8,
|
||||
}
|
||||
|
||||
impl<C: Config> Client<C> {
|
||||
pub(crate) fn new(
|
||||
packet_channels: C2sPacketChannels,
|
||||
server: &SharedServer<C>,
|
||||
ncd: NewClientData,
|
||||
data: C::ClientState,
|
||||
state: C::ClientState,
|
||||
) -> Self {
|
||||
let (send, recv) = packet_channels;
|
||||
|
||||
Self {
|
||||
state: data,
|
||||
state,
|
||||
send: Some(send),
|
||||
recv,
|
||||
created_tick: server.current_tick(),
|
||||
uuid: ncd.uuid,
|
||||
username: ncd.username,
|
||||
textures: ncd.textures,
|
||||
world: WorldId::default(),
|
||||
new_position: Vec3::default(),
|
||||
position: Vec3::default(),
|
||||
old_position: Vec3::default(),
|
||||
velocity: Vec3::default(),
|
||||
yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
view_distance: 8,
|
||||
teleport_id_counter: 0,
|
||||
pending_teleports: 0,
|
||||
spawn_position: BlockPos::default(),
|
||||
|
@ -270,8 +261,6 @@ impl<C: Config> Client<C> {
|
|||
death_location: None,
|
||||
events: VecDeque::new(),
|
||||
last_keepalive_id: 0,
|
||||
new_max_view_distance: 16,
|
||||
old_max_view_distance: 0,
|
||||
loaded_entities: HashSet::new(),
|
||||
loaded_chunks: HashSet::new(),
|
||||
new_game_mode: GameMode::Survival,
|
||||
|
@ -281,17 +270,18 @@ impl<C: Config> Client<C> {
|
|||
msgs_to_send: Vec::new(),
|
||||
attack_speed: 4.0,
|
||||
movement_speed: 0.7,
|
||||
flags: ClientFlags::new()
|
||||
bits: ClientBits::new()
|
||||
.with_modified_spawn_position(true)
|
||||
.with_got_keepalive(true),
|
||||
.with_got_keepalive(true)
|
||||
.with_created_this_tick(true),
|
||||
player_data: Player::new(),
|
||||
entity_events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the tick that this client was created.
|
||||
pub fn created_tick(&self) -> Ticks {
|
||||
self.created_tick
|
||||
/// If the client joined the game this tick.
|
||||
pub fn created_this_tick(&self) -> bool {
|
||||
self.bits.created_this_tick()
|
||||
}
|
||||
|
||||
/// Gets the client's UUID.
|
||||
|
@ -304,16 +294,6 @@ impl<C: Config> Client<C> {
|
|||
&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
|
||||
/// a skin, then `None` is returned.
|
||||
pub fn textures(&self) -> Option<&SignedPlayerTextures> {
|
||||
|
@ -325,13 +305,14 @@ impl<C: Config> Client<C> {
|
|||
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
|
||||
/// disconnected.
|
||||
pub fn spawn(&mut self, world: WorldId) {
|
||||
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.
|
||||
|
@ -344,7 +325,7 @@ impl<C: Config> Client<C> {
|
|||
/// Gets the absolute position of this client in the world it is located
|
||||
/// in.
|
||||
pub fn position(&self) -> Vec3<f64> {
|
||||
self.new_position
|
||||
self.position
|
||||
}
|
||||
|
||||
/// 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`].
|
||||
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.pitch = pitch;
|
||||
self.velocity = Vec3::default();
|
||||
|
||||
if !self.flags.teleported_this_tick() {
|
||||
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);
|
||||
}
|
||||
self.bits.set_teleported_this_tick(true);
|
||||
}
|
||||
|
||||
/// Gets the velocity of this client in m/s.
|
||||
///
|
||||
/// The velocity of a client is derived from their current and previous
|
||||
/// position.
|
||||
/// Gets the most recently set velocity of this client in m/s.
|
||||
pub fn velocity(&self) -> Vec3<f32> {
|
||||
self.velocity
|
||||
}
|
||||
|
@ -384,7 +348,7 @@ impl<C: Config> Client<C> {
|
|||
/// Sets the client's velocity in m/s.
|
||||
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
|
||||
self.velocity = velocity.into();
|
||||
self.flags.set_velocity_modified(true);
|
||||
self.bits.set_velocity_modified(true);
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
self.spawn_position = pos;
|
||||
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
|
||||
/// `minecraft:recovery_compass` items point at the returned position.
|
||||
///
|
||||
/// If the client's current dimension differs from the returned
|
||||
/// dimension or the location is `None` then the compass will spin
|
||||
/// randomly.
|
||||
|
@ -482,7 +447,7 @@ impl<C: Config> Client<C> {
|
|||
pub fn set_attack_speed(&mut self, speed: f64) {
|
||||
if 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) {
|
||||
if 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 });
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// A disconnected client object will never become reconnected. It is your
|
||||
|
@ -518,42 +478,46 @@ impl<C: Config> Client<C> {
|
|||
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.
|
||||
///
|
||||
/// If there are no remaining events, `None` is returned.
|
||||
///
|
||||
/// Any remaining client events are deleted at the end of the
|
||||
/// current tick.
|
||||
pub fn pop_event(&mut self) -> Option<Event> {
|
||||
pub fn pop_event(&mut self) -> Option<ClientEvent> {
|
||||
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);
|
||||
}
|
||||
|
||||
/// The current view distance of this client measured in chunks.
|
||||
pub fn view_distance(&self) -> u8 {
|
||||
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 current view distance of this client measured in chunks. The client
|
||||
/// will not be able to see chunks and entities past this distance.
|
||||
///
|
||||
/// The value returned is measured in chunks.
|
||||
pub fn max_view_distance(&self) -> u8 {
|
||||
self.new_max_view_distance
|
||||
/// The result is in `2..=32`.
|
||||
pub fn view_distance(&self) -> u8 {
|
||||
self.view_distance
|
||||
}
|
||||
|
||||
/// Sets the maximum view distance. The client will not be able to see
|
||||
/// chunks and entities past this distance.
|
||||
/// Sets the view distance. The client will not be able to see chunks and
|
||||
/// entities past this distance.
|
||||
///
|
||||
/// The new view distance is measured in chunks and is clamped to `2..=32`.
|
||||
pub fn set_max_view_distance(&mut self, dist: u8) {
|
||||
self.new_max_view_distance = dist.clamp(2, 32);
|
||||
pub fn set_view_distance(&mut self, dist: u8) {
|
||||
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.
|
||||
|
@ -561,12 +525,12 @@ impl<C: Config> Client<C> {
|
|||
/// To have any visible effect, this function must be called on the same
|
||||
/// tick the client joins the server.
|
||||
pub fn set_hardcore(&mut self, hardcore: bool) {
|
||||
self.flags.set_hardcore(hardcore);
|
||||
self.bits.set_hardcore(hardcore);
|
||||
}
|
||||
|
||||
/// Gets if hardcore mode is enabled.
|
||||
pub fn is_hardcore(&self) -> bool {
|
||||
self.flags.hardcore()
|
||||
self.bits.hardcore()
|
||||
}
|
||||
|
||||
/// 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_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 {
|
||||
C2sPlayPacket::TeleportConfirm(p) => {
|
||||
if self.pending_teleports == 0 {
|
||||
|
@ -694,14 +620,14 @@ impl<C: Config> Client<C> {
|
|||
C2sPlayPacket::QueryBlockNbt(_) => {}
|
||||
C2sPlayPacket::UpdateDifficulty(_) => {}
|
||||
C2sPlayPacket::CommandExecution(_) => {}
|
||||
C2sPlayPacket::ChatMessage(p) => self.events.push_back(Event::ChatMessage {
|
||||
C2sPlayPacket::ChatMessage(p) => self.events.push_back(ClientEvent::ChatMessage {
|
||||
message: p.message.0,
|
||||
timestamp: Duration::from_millis(p.timestamp),
|
||||
}),
|
||||
C2sPlayPacket::RequestChatPreview(_) => {}
|
||||
C2sPlayPacket::ClientStatus(_) => {}
|
||||
C2sPlayPacket::ClientSettings(p) => {
|
||||
let old = self.settings.replace(Settings {
|
||||
self.events.push_back(ClientEvent::SettingsChanged {
|
||||
locale: p.locale.0,
|
||||
view_distance: p.view_distance.0,
|
||||
chat_mode: p.chat_mode,
|
||||
|
@ -709,9 +635,7 @@ impl<C: Config> Client<C> {
|
|||
main_hand: p.main_hand,
|
||||
displayed_skin_parts: p.displayed_skin_parts,
|
||||
allow_server_listings: p.allow_server_listings,
|
||||
});
|
||||
|
||||
self.events.push_back(Event::SettingsChanged(old));
|
||||
})
|
||||
}
|
||||
C2sPlayPacket::RequestCommandCompletion(_) => {}
|
||||
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
|
||||
// that the distance is <=4 blocks.
|
||||
|
||||
self.events.push_back(Event::InteractWithEntity {
|
||||
self.events.push_back(ClientEvent::InteractWithEntity {
|
||||
id,
|
||||
sneaking: p.sneaking,
|
||||
kind: match p.kind {
|
||||
|
@ -741,7 +665,7 @@ impl<C: Config> Client<C> {
|
|||
C2sPlayPacket::JigsawGenerate(_) => {}
|
||||
C2sPlayPacket::KeepAlive(p) => {
|
||||
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());
|
||||
self.disconnect_no_reason();
|
||||
} else if p.id != last_keepalive_id {
|
||||
|
@ -753,39 +677,68 @@ impl<C: Config> Client<C> {
|
|||
);
|
||||
self.disconnect_no_reason();
|
||||
} else {
|
||||
self.flags.set_got_keepalive(true);
|
||||
self.bits.set_got_keepalive(true);
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::UpdateDifficultyLock(_) => {}
|
||||
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) => {
|
||||
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) => {
|
||||
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) => {
|
||||
handle_movement_packet(
|
||||
self,
|
||||
true,
|
||||
p.position,
|
||||
p.yaw,
|
||||
p.pitch,
|
||||
self.flags.on_ground(),
|
||||
);
|
||||
if self.pending_teleports == 0 {
|
||||
self.position = p.position;
|
||||
self.yaw = p.yaw;
|
||||
self.pitch = p.pitch;
|
||||
|
||||
self.events.push_back(ClientEvent::MoveVehicle {
|
||||
position: p.position,
|
||||
yaw: p.yaw,
|
||||
pitch: p.pitch,
|
||||
});
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::BoatPaddleState(p) => {
|
||||
self.events.push_back(Event::SteerBoat {
|
||||
self.events.push_back(ClientEvent::SteerBoat {
|
||||
left_paddle_turning: p.left_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 {
|
||||
DiggingStatus::StartedDigging => Event::Digging {
|
||||
DiggingStatus::StartedDigging => ClientEvent::Digging {
|
||||
status: event::DiggingStatus::Start,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
},
|
||||
DiggingStatus::CancelledDigging => Event::Digging {
|
||||
DiggingStatus::CancelledDigging => ClientEvent::Digging {
|
||||
status: event::DiggingStatus::Cancel,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
},
|
||||
DiggingStatus::FinishedDigging => Event::Digging {
|
||||
DiggingStatus::FinishedDigging => ClientEvent::Digging {
|
||||
status: event::DiggingStatus::Finish,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
|
@ -823,56 +776,19 @@ impl<C: Config> Client<C> {
|
|||
DiggingStatus::SwapItemInHand => return,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::PlayerCommand(e) => {
|
||||
// TODO: validate:
|
||||
// - Can't sprint and sneak at the same time
|
||||
// - Can't leave bed while not in a bed.
|
||||
// - Can't jump with a horse if not on a horse
|
||||
// - Can't open horse inventory if not on a horse.
|
||||
// - Can't fly with elytra if not wearing an elytra.
|
||||
// - Can't jump with horse while already jumping & vice versa?
|
||||
self.events.push_back(match e.action_id {
|
||||
PlayerCommandId::StartSneaking => {
|
||||
if self.flags.sneaking() {
|
||||
return;
|
||||
}
|
||||
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::PlayerCommand(c) => {
|
||||
self.events.push_back(match c.action_id {
|
||||
PlayerCommandId::StartSneaking => ClientEvent::StartSneaking,
|
||||
PlayerCommandId::StopSneaking => ClientEvent::StopSneaking,
|
||||
PlayerCommandId::LeaveBed => ClientEvent::LeaveBed,
|
||||
PlayerCommandId::StartSprinting => ClientEvent::StartSprinting,
|
||||
PlayerCommandId::StopSprinting => ClientEvent::StopSprinting,
|
||||
PlayerCommandId::StartJumpWithHorse => ClientEvent::StartJumpWithHorse {
|
||||
jump_boost: c.jump_boost.0 .0 as u8,
|
||||
},
|
||||
PlayerCommandId::StopJumpWithHorse => ClientEvent::StopJumpWithHorse,
|
||||
PlayerCommandId::OpenHorseInventory => ClientEvent::OpenHorseInventory,
|
||||
PlayerCommandId::StartFlyingWithElytra => ClientEvent::StartFlyingWithElytra,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::PlayerInput(_) => {}
|
||||
|
@ -891,7 +807,7 @@ impl<C: Config> Client<C> {
|
|||
C2sPlayPacket::UpdateJigsaw(_) => {}
|
||||
C2sPlayPacket::UpdateStructureBlock(_) => {}
|
||||
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::PlayerInteractBlock(_) => {}
|
||||
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
|
||||
// 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
|
||||
.meta
|
||||
.player_list()
|
||||
|
@ -941,7 +857,7 @@ impl<C: Config> Client<C> {
|
|||
|
||||
self.send_packet(GameJoin {
|
||||
entity_id: 0, // EntityId 0 is reserved for clients.
|
||||
is_hardcore: self.flags.hardcore(),
|
||||
is_hardcore: self.bits.hardcore(),
|
||||
gamemode: self.new_game_mode,
|
||||
previous_gamemode: self.old_game_mode,
|
||||
dimension_names,
|
||||
|
@ -956,7 +872,7 @@ impl<C: Config> Client<C> {
|
|||
),
|
||||
hashed_seed: 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),
|
||||
reduced_debug_info: false,
|
||||
enable_respawn_screen: false,
|
||||
|
@ -969,14 +885,15 @@ impl<C: Config> Client<C> {
|
|||
|
||||
self.teleport(self.position(), self.yaw(), self.pitch());
|
||||
} else {
|
||||
if self.flags.spawn() {
|
||||
self.flags.set_spawn(false);
|
||||
if self.bits.spawn() {
|
||||
self.bits.set_spawn(false);
|
||||
self.loaded_entities.clear();
|
||||
self.loaded_chunks.clear();
|
||||
|
||||
// TODO: clear player list.
|
||||
|
||||
// Client bug workaround: send the client to a dummy dimension first.
|
||||
// TODO: is there actually a bug?
|
||||
self.send_packet(PlayerRespawn {
|
||||
dimension_type_name: ident!("{LIBRARY_NAMESPACE}:dimension_type_0"),
|
||||
dimension_name: ident!("{LIBRARY_NAMESPACE}:dummy_dimension"),
|
||||
|
@ -1027,8 +944,8 @@ impl<C: Config> Client<C> {
|
|||
}
|
||||
|
||||
// Set player attributes
|
||||
if self.flags.attack_speed_modified() {
|
||||
self.flags.set_attack_speed_modified(false);
|
||||
if self.bits.attack_speed_modified() {
|
||||
self.bits.set_attack_speed_modified(false);
|
||||
|
||||
self.send_packet(EntityAttributes {
|
||||
entity_id: VarInt(0),
|
||||
|
@ -1040,8 +957,8 @@ impl<C: Config> Client<C> {
|
|||
});
|
||||
}
|
||||
|
||||
if self.flags.movement_speed_modified() {
|
||||
self.flags.set_movement_speed_modified(false);
|
||||
if self.bits.movement_speed_modified() {
|
||||
self.bits.set_movement_speed_modified(false);
|
||||
|
||||
self.send_packet(EntityAttributes {
|
||||
entity_id: VarInt(0),
|
||||
|
@ -1054,8 +971,8 @@ impl<C: Config> Client<C> {
|
|||
}
|
||||
|
||||
// Update the players spawn position (compass position)
|
||||
if self.flags.modified_spawn_position() {
|
||||
self.flags.set_modified_spawn_position(false);
|
||||
if self.bits.modified_spawn_position() {
|
||||
self.bits.set_modified_spawn_position(false);
|
||||
|
||||
self.send_packet(PlayerSpawnPosition {
|
||||
location: self.spawn_position,
|
||||
|
@ -1063,23 +980,24 @@ impl<C: Config> Client<C> {
|
|||
})
|
||||
}
|
||||
|
||||
// Update view distance fog on the client if necessary.
|
||||
if self.old_max_view_distance != self.new_max_view_distance {
|
||||
self.old_max_view_distance = self.new_max_view_distance;
|
||||
if self.created_tick != current_tick {
|
||||
// Update view distance fog on the client.
|
||||
if self.bits.view_distance_modified() {
|
||||
self.bits.set_view_distance_modified(false);
|
||||
|
||||
if !self.created_this_tick() {
|
||||
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.
|
||||
if current_tick % (shared.tick_rate() * 8) == 0 {
|
||||
if self.flags.got_keepalive() {
|
||||
if self.bits.got_keepalive() {
|
||||
let id = rand::random();
|
||||
self.send_packet(KeepAlive { id });
|
||||
self.last_keepalive_id = id;
|
||||
self.flags.set_got_keepalive(false);
|
||||
self.bits.set_got_keepalive(false);
|
||||
} else {
|
||||
log::warn!(
|
||||
"player {} timed out (no keepalive response)",
|
||||
|
@ -1091,13 +1009,13 @@ impl<C: Config> Client<C> {
|
|||
|
||||
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
|
||||
// they're in.
|
||||
{
|
||||
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 {
|
||||
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"
|
||||
// screen is closed at the appropriate time.
|
||||
if self.flags.teleported_this_tick() {
|
||||
self.flags.set_teleported_this_tick(false);
|
||||
if self.bits.teleported_this_tick() {
|
||||
self.bits.set_teleported_this_tick(false);
|
||||
|
||||
self.send_packet(PlayerPositionLook {
|
||||
position: self.new_position,
|
||||
position: self.position,
|
||||
yaw: self.yaw,
|
||||
pitch: self.pitch,
|
||||
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,
|
||||
});
|
||||
|
||||
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
|
||||
// zero.
|
||||
if self.flags.velocity_modified() {
|
||||
self.flags.set_velocity_modified(false);
|
||||
if self.bits.velocity_modified() {
|
||||
self.bits.set_velocity_modified(false);
|
||||
|
||||
self.send_packet(EntityVelocityUpdate {
|
||||
entity_id: VarInt(0),
|
||||
|
@ -1203,14 +1131,14 @@ impl<C: Config> Client<C> {
|
|||
self.loaded_entities.retain(|&id| {
|
||||
if let Some(entity) = entities.get(id) {
|
||||
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) {
|
||||
send_packet(&mut self.send, meta);
|
||||
}
|
||||
|
||||
let position_delta = entity.position() - entity.old_position();
|
||||
let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0;
|
||||
let flags = entity.flags();
|
||||
let flags = entity.bits();
|
||||
|
||||
if entity.position() != entity.old_position()
|
||||
&& !needs_teleport
|
||||
|
@ -1345,7 +1273,8 @@ impl<C: Config> Client<C> {
|
|||
self.entity_events.clear();
|
||||
|
||||
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
|
||||
/// [`pop_event`](crate::client::Client::pop_event).
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
pub enum ClientEvent {
|
||||
/// A regular message was sent to the chat.
|
||||
ChatMessage {
|
||||
/// The content of the message
|
||||
|
@ -21,31 +21,44 @@ pub enum Event {
|
|||
/// The time the message was sent.
|
||||
timestamp: Duration,
|
||||
},
|
||||
/// Settings were changed. The value in this variant is the _previous_
|
||||
/// client settings.
|
||||
SettingsChanged(Option<Settings>),
|
||||
/// The client moved.
|
||||
Movement {
|
||||
/// Absolute coordinates of the previous position.
|
||||
old_position: Vec3<f64>,
|
||||
/// Previous velocity in m/s.
|
||||
old_velocity: Vec3<f32>,
|
||||
/// The previous yaw (in degrees).
|
||||
old_yaw: f32,
|
||||
/// The previous pitch (in degrees).
|
||||
old_pitch: f32,
|
||||
/// If the client was previously on the ground.
|
||||
old_on_ground: bool,
|
||||
/// Absolute coodinates of the new position.
|
||||
new_position: Vec3<f64>,
|
||||
/// New velocity in m/s.
|
||||
new_velocity: Vec3<f32>,
|
||||
/// The new yaw (in degrees).
|
||||
new_yaw: f32,
|
||||
/// The new pitch (in degrees).
|
||||
new_pitch: f32,
|
||||
/// If the client is now on the ground.
|
||||
new_on_ground: bool,
|
||||
/// Settings were changed. This is always sent once after joining by the
|
||||
/// vanilla client.
|
||||
SettingsChanged {
|
||||
/// e.g. en_US
|
||||
locale: String,
|
||||
/// The client side render distance, in chunks.
|
||||
///
|
||||
/// The value is always in `2..=32`.
|
||||
view_distance: u8,
|
||||
chat_mode: ChatMode,
|
||||
/// `true` if the client has chat colors enabled, `false` otherwise.
|
||||
chat_colors: bool,
|
||||
main_hand: MainHand,
|
||||
displayed_skin_parts: DisplayedSkinParts,
|
||||
allow_server_listings: bool,
|
||||
},
|
||||
MovePosition {
|
||||
position: Vec3<f64>,
|
||||
on_ground: bool,
|
||||
},
|
||||
MovePositionAndRotation {
|
||||
position: Vec3<f64>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
on_ground: bool,
|
||||
},
|
||||
MoveRotation {
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
on_ground: bool,
|
||||
},
|
||||
MoveOnGround {
|
||||
on_ground: bool,
|
||||
},
|
||||
MoveVehicle {
|
||||
position: Vec3<f64>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
},
|
||||
StartSneaking,
|
||||
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
|
||||
/// 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.
|
||||
///
|
||||
/// # 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
|
||||
/// 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.
|
||||
///
|
||||
/// # Default Implementation
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::iter::FusedIterator;
|
|||
use std::num::NonZeroU32;
|
||||
|
||||
use bitfield_struct::bitfield;
|
||||
pub use kinds::{TrackedData, EntityKind};
|
||||
pub use data::{EntityKind, TrackedData};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use uuid::Uuid;
|
||||
use vek::{Aabb, Vec3};
|
||||
|
@ -22,7 +22,7 @@ use crate::world::WorldId;
|
|||
use crate::STANDARD_TPS;
|
||||
|
||||
pub mod data;
|
||||
pub mod kinds;
|
||||
pub mod types;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/entity_event.rs"));
|
||||
|
||||
|
@ -76,7 +76,7 @@ impl<C: Config> Entities<C> {
|
|||
state: data,
|
||||
variants: TrackedData::new(kind),
|
||||
events: Vec::new(),
|
||||
flags: EntityFlags(0),
|
||||
bits: EntityBits::new(),
|
||||
world: WorldId::NULL,
|
||||
new_position: Vec3::default(),
|
||||
old_position: Vec3::default(),
|
||||
|
@ -203,9 +203,9 @@ impl<C: Config> Entities<C> {
|
|||
e.variants.clear_modifications();
|
||||
e.events.clear();
|
||||
|
||||
e.flags.set_yaw_or_pitch_modified(false);
|
||||
e.flags.set_head_yaw_modified(false);
|
||||
e.flags.set_velocity_modified(false);
|
||||
e.bits.set_yaw_or_pitch_modified(false);
|
||||
e.bits.set_head_yaw_modified(false);
|
||||
e.bits.set_velocity_modified(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,8 +244,8 @@ pub struct Entity<C: Config> {
|
|||
/// Custom data.
|
||||
pub state: C::EntityState,
|
||||
variants: TrackedData,
|
||||
flags: EntityFlags,
|
||||
events: Vec<Event>,
|
||||
bits: EntityBits,
|
||||
events: Vec<EntityEvent>,
|
||||
world: WorldId,
|
||||
new_position: Vec3<f64>,
|
||||
old_position: Vec3<f64>,
|
||||
|
@ -257,7 +257,7 @@ pub struct Entity<C: Config> {
|
|||
}
|
||||
|
||||
#[bitfield(u8)]
|
||||
pub(crate) struct EntityFlags {
|
||||
pub(crate) struct EntityBits {
|
||||
pub yaw_or_pitch_modified: bool,
|
||||
pub head_yaw_modified: bool,
|
||||
pub velocity_modified: bool,
|
||||
|
@ -267,15 +267,15 @@ pub(crate) struct EntityFlags {
|
|||
}
|
||||
|
||||
impl<C: Config> Entity<C> {
|
||||
pub(crate) fn flags(&self) -> EntityFlags {
|
||||
self.flags
|
||||
pub(crate) fn bits(&self) -> EntityBits {
|
||||
self.bits
|
||||
}
|
||||
|
||||
pub fn view(&self) -> &TrackedData {
|
||||
pub fn data(&self) -> &TrackedData {
|
||||
&self.variants
|
||||
}
|
||||
|
||||
pub fn view_mut(&mut self) -> &mut TrackedData {
|
||||
pub fn data_mut(&mut self) -> &mut TrackedData {
|
||||
&mut self.variants
|
||||
}
|
||||
|
||||
|
@ -284,11 +284,11 @@ impl<C: Config> Entity<C> {
|
|||
self.variants.kind()
|
||||
}
|
||||
|
||||
pub fn trigger_event(&mut self, event: Event) {
|
||||
pub fn push_event(&mut self, event: EntityEvent) {
|
||||
self.events.push(event);
|
||||
}
|
||||
|
||||
pub(crate) fn events(&self) -> &[Event] {
|
||||
pub(crate) fn events(&self) -> &[EntityEvent] {
|
||||
&self.events
|
||||
}
|
||||
|
||||
|
@ -337,7 +337,7 @@ impl<C: Config> Entity<C> {
|
|||
pub fn set_yaw(&mut self, yaw: f32) {
|
||||
if 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) {
|
||||
if 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) {
|
||||
if 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 {
|
||||
self.velocity = new_vel;
|
||||
self.flags.set_velocity_modified(true);
|
||||
self.bits.set_velocity_modified(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the value of the "on ground" flag.
|
||||
pub fn on_ground(&self) -> bool {
|
||||
self.flags.on_ground()
|
||||
self.bits.on_ground()
|
||||
}
|
||||
|
||||
/// Sets the value of the "on ground" flag.
|
||||
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.
|
||||
|
@ -405,7 +405,7 @@ impl<C: Config> Entity<C> {
|
|||
/// The hitbox of an entity is determined by its position, entity type, and
|
||||
/// other state specific to that type.
|
||||
///
|
||||
/// [interact event]: crate::client::Event::InteractWithEntity
|
||||
/// [interact event]: crate::client::EntityEvent::InteractWithEntity
|
||||
pub fn hitbox(&self) -> Aabb<f64> {
|
||||
let dims = match &self.variants {
|
||||
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`].
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/entity.rs"));
|
||||
|
|
|
@ -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,
|
||||
ping,
|
||||
display_name: display_name.into(),
|
||||
flags: EntryFlags::new().with_created_this_tick(true),
|
||||
bits: EntryBits::new().with_created_this_tick(true),
|
||||
});
|
||||
} else {
|
||||
e.set_game_mode(game_mode);
|
||||
|
@ -84,7 +84,7 @@ impl PlayerList {
|
|||
game_mode,
|
||||
ping,
|
||||
display_name: display_name.into(),
|
||||
flags: EntryFlags::new().with_created_this_tick(true),
|
||||
bits: EntryBits::new().with_created_this_tick(true),
|
||||
});
|
||||
true
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ impl PlayerList {
|
|||
let mut display_name = Vec::new();
|
||||
|
||||
for (&uuid, e) in self.entries.iter() {
|
||||
if e.flags.created_this_tick() {
|
||||
if e.bits.created_this_tick() {
|
||||
let mut properties = Vec::new();
|
||||
if let Some(textures) = &e.textures {
|
||||
properties.push(Property {
|
||||
|
@ -234,15 +234,15 @@ impl PlayerList {
|
|||
continue;
|
||||
}
|
||||
|
||||
if e.flags.modified_game_mode() {
|
||||
if e.bits.modified_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)));
|
||||
}
|
||||
|
||||
if e.flags.modified_display_name() {
|
||||
if e.bits.modified_display_name() {
|
||||
display_name.push((uuid, e.display_name.clone()));
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ impl PlayerList {
|
|||
|
||||
pub(crate) fn update(&mut self) {
|
||||
for e in self.entries.values_mut() {
|
||||
e.flags = EntryFlags(0);
|
||||
e.bits = EntryBits::new();
|
||||
}
|
||||
self.removed.clear();
|
||||
self.modified_header_or_footer = false;
|
||||
|
@ -290,7 +290,17 @@ pub struct PlayerListEntry {
|
|||
game_mode: GameMode,
|
||||
ping: i32,
|
||||
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 {
|
||||
|
@ -313,7 +323,7 @@ impl PlayerListEntry {
|
|||
pub fn set_game_mode(&mut self, game_mode: GameMode) {
|
||||
if self.game_mode != game_mode {
|
||||
self.game_mode = game_mode;
|
||||
self.flags.set_modified_game_mode(true);
|
||||
self.bits.set_modified_game_mode(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +336,7 @@ impl PlayerListEntry {
|
|||
pub fn set_ping(&mut self, ping: i32) {
|
||||
if 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();
|
||||
if 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 uuid::Uuid;
|
||||
use vek::Vec3;
|
||||
// use {def_bitfield, def_enum, def_struct};
|
||||
|
||||
// use {def_bitfield, def_enum, def_struct};
|
||||
use crate::block_pos::BlockPos;
|
||||
use crate::ident::Ident;
|
||||
use crate::protocol_inner::{
|
||||
|
@ -121,8 +121,7 @@ macro_rules! def_struct {
|
|||
}
|
||||
|
||||
impl $name {
|
||||
#[allow(unused)]
|
||||
const PACKET_ID: i32 = $id;
|
||||
pub const PACKET_ID: i32 = $id;
|
||||
}
|
||||
)*
|
||||
|
||||
|
@ -231,8 +230,7 @@ macro_rules! def_enum {
|
|||
}
|
||||
|
||||
impl $name {
|
||||
#[allow(unused)]
|
||||
const PACKET_ID: i32 = $id;
|
||||
pub const PACKET_ID: i32 = $id;
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
@ -289,7 +287,7 @@ macro_rules! def_bitfield {
|
|||
$(
|
||||
#[doc = "Gets the " $bit " bit on this bitfield.\n"]
|
||||
$(#[$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()
|
||||
}
|
||||
|
||||
|
@ -313,7 +311,7 @@ macro_rules! def_bitfield {
|
|||
let mut s = f.debug_struct(stringify!($name));
|
||||
paste! {
|
||||
$(
|
||||
s.field(stringify!($bit), &self. [<get_ $bit:snake>]());
|
||||
s.field(stringify!($bit), &self. $bit());
|
||||
)*
|
||||
}
|
||||
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::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::status::{QueryPong, QueryResponse};
|
||||
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 client = Client::new(
|
||||
c2s_packet_channels,
|
||||
&server.shared,
|
||||
msg.ncd,
|
||||
C::ClientState::default(),
|
||||
);
|
||||
let client = Client::new(c2s_packet_channels, msg.ncd, C::ClientState::default());
|
||||
|
||||
server.clients.insert(client);
|
||||
}
|
||||
|
@ -713,30 +710,28 @@ async fn handle_login<C: Config>(
|
|||
c.enc.enable_compression(compression_threshold);
|
||||
c.dec.enable_compression(compression_threshold);
|
||||
|
||||
let npd = NewClientData {
|
||||
let ncd = NewClientData {
|
||||
uuid,
|
||||
username,
|
||||
textures,
|
||||
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}\"");
|
||||
c.enc
|
||||
.write_packet(&LoginDisconnect { reason })
|
||||
.await?;
|
||||
c.enc.write_packet(&LoginDisconnect { reason }).await?;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
c.enc
|
||||
.write_packet(&LoginSuccess {
|
||||
uuid: npd.uuid,
|
||||
username: npd.username.clone().into(),
|
||||
uuid: ncd.uuid,
|
||||
username: ncd.username.clone().into(),
|
||||
properties: Vec::new(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(Some(npd))
|
||||
Ok(Some(ncd))
|
||||
}
|
||||
|
||||
async fn handle_play<C: Config>(
|
||||
|
|
Loading…
Reference in a new issue