mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
parent
dcd4a2b5ed
commit
9ec1df1978
417
examples/entity_raycast.rs
Normal file
417
examples/entity_raycast.rs
Normal file
|
@ -0,0 +1,417 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use log::LevelFilter;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use valence::async_trait;
|
||||||
|
use valence::block::{BlockPos, BlockState};
|
||||||
|
use valence::chunk::UnloadedChunk;
|
||||||
|
use valence::client::{handle_event_default, GameMode};
|
||||||
|
use valence::config::{Config, ServerListPing};
|
||||||
|
use valence::dimension::DimensionId;
|
||||||
|
use valence::entity::types::{Facing, PaintingKind, Pose};
|
||||||
|
use valence::entity::{EntityId, EntityKind, TrackedData};
|
||||||
|
use valence::player_list::PlayerListId;
|
||||||
|
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||||
|
use valence::spatial_index::RaycastHit;
|
||||||
|
use valence::text::{Color, TextFormat};
|
||||||
|
use valence::util::from_yaw_and_pitch;
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
|
pub fn main() -> ShutdownResult {
|
||||||
|
env_logger::Builder::new()
|
||||||
|
.filter_module("valence", LevelFilter::Trace)
|
||||||
|
.parse_default_env()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
valence::start_server(
|
||||||
|
Game {
|
||||||
|
player_count: AtomicUsize::new(0),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
player_count: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ClientState {
|
||||||
|
player: EntityId,
|
||||||
|
shulker_bullet: EntityId,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_PLAYERS: usize = 10;
|
||||||
|
|
||||||
|
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -5);
|
||||||
|
|
||||||
|
const PLAYER_EYE_HEIGHT: f64 = 1.62;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// const PLAYER_SNEAKING_EYE_HEIGHT: f64 = 1.495;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Config for Game {
|
||||||
|
type ServerState = Option<PlayerListId>;
|
||||||
|
type ClientState = ClientState;
|
||||||
|
type EntityState = ();
|
||||||
|
type WorldState = ();
|
||||||
|
type ChunkState = ();
|
||||||
|
type PlayerListState = ();
|
||||||
|
|
||||||
|
fn max_connections(&self) -> usize {
|
||||||
|
// We want status pings to be successful even if the server is full.
|
||||||
|
MAX_PLAYERS + 64
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn server_list_ping(
|
||||||
|
&self,
|
||||||
|
_server: &SharedServer<Self>,
|
||||||
|
_remote_addr: SocketAddr,
|
||||||
|
_protocol_version: i32,
|
||||||
|
) -> ServerListPing {
|
||||||
|
ServerListPing::Respond {
|
||||||
|
online_players: self.player_count.load(Ordering::SeqCst) as i32,
|
||||||
|
max_players: MAX_PLAYERS as i32,
|
||||||
|
player_sample: Default::default(),
|
||||||
|
description: "Hello Valence!".color(Color::AQUA),
|
||||||
|
favicon_png: Some(include_bytes!("../assets/logo-64x64.png").as_slice().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&self, server: &mut Server<Self>) {
|
||||||
|
let (world_id, world) = server.worlds.insert(DimensionId::default(), ());
|
||||||
|
let (player_list_id, player_list) = server.player_lists.insert(());
|
||||||
|
server.state = Some(player_list_id);
|
||||||
|
|
||||||
|
let size = 5;
|
||||||
|
for z in -size..size {
|
||||||
|
for x in -size..size {
|
||||||
|
world.chunks.insert([x, z], UnloadedChunk::default(), ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK);
|
||||||
|
|
||||||
|
// ==== Item Frames ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::ItemFrame, ());
|
||||||
|
if let TrackedData::ItemFrame(i) = e.data_mut() {
|
||||||
|
i.set_rotation(Facing::North as i32);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([2.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::ItemFrame, ());
|
||||||
|
if let TrackedData::ItemFrame(i) = e.data_mut() {
|
||||||
|
i.set_rotation(Facing::East as i32);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([3.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::GlowItemFrame, ());
|
||||||
|
if let TrackedData::GlowItemFrame(i) = e.data_mut() {
|
||||||
|
i.set_rotation(Facing::South as i32);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([4.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::GlowItemFrame, ());
|
||||||
|
if let TrackedData::GlowItemFrame(i) = e.data_mut() {
|
||||||
|
i.set_rotation(Facing::West as i32);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([5.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
// ==== Paintings ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Painting, ());
|
||||||
|
if let TrackedData::Painting(p) = e.data_mut() {
|
||||||
|
p.set_variant(PaintingKind::Pigscene);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_yaw(180.0);
|
||||||
|
e.set_position([0.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Painting, ());
|
||||||
|
if let TrackedData::Painting(p) = e.data_mut() {
|
||||||
|
p.set_variant(PaintingKind::DonkeyKong);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_yaw(90.0);
|
||||||
|
e.set_position([-4.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Painting, ());
|
||||||
|
if let TrackedData::Painting(p) = e.data_mut() {
|
||||||
|
p.set_variant(PaintingKind::Void);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-6.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Painting, ());
|
||||||
|
if let TrackedData::Painting(p) = e.data_mut() {
|
||||||
|
p.set_variant(PaintingKind::Aztec);
|
||||||
|
}
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-7.0, 102.0, 0.0]);
|
||||||
|
|
||||||
|
// ==== Shulkers ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Shulker, ());
|
||||||
|
if let TrackedData::Shulker(s) = e.data_mut() {
|
||||||
|
s.set_peek_amount(100);
|
||||||
|
s.set_attached_face(Facing::West);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-4.0, 102.0, -8.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Shulker, ());
|
||||||
|
if let TrackedData::Shulker(s) = e.data_mut() {
|
||||||
|
s.set_peek_amount(75);
|
||||||
|
s.set_attached_face(Facing::Up);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-1.0, 102.0, -8.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Shulker, ());
|
||||||
|
if let TrackedData::Shulker(s) = e.data_mut() {
|
||||||
|
s.set_peek_amount(50);
|
||||||
|
s.set_attached_face(Facing::Down);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([2.0, 102.0, -8.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Shulker, ());
|
||||||
|
if let TrackedData::Shulker(s) = e.data_mut() {
|
||||||
|
s.set_peek_amount(25);
|
||||||
|
s.set_attached_face(Facing::East);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([5.0, 102.0, -8.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Shulker, ());
|
||||||
|
if let TrackedData::Shulker(s) = e.data_mut() {
|
||||||
|
s.set_peek_amount(0);
|
||||||
|
s.set_attached_face(Facing::North);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([8.0, 102.0, -8.0]);
|
||||||
|
|
||||||
|
// ==== Slimes ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Slime, ());
|
||||||
|
if let TrackedData::Slime(s) = e.data_mut() {
|
||||||
|
s.set_slime_size(30);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_yaw(180.0);
|
||||||
|
e.set_head_yaw(180.0);
|
||||||
|
e.set_position([12.0, 102.0, 10.0]);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::MagmaCube, ());
|
||||||
|
if let TrackedData::MagmaCube(m) = e.data_mut() {
|
||||||
|
m.set_slime_size(30);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_yaw(180.0);
|
||||||
|
e.set_head_yaw(180.0);
|
||||||
|
e.set_position([-12.0, 102.0, 10.0]);
|
||||||
|
|
||||||
|
// ==== Sheep ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Sheep, ());
|
||||||
|
if let TrackedData::Sheep(s) = e.data_mut() {
|
||||||
|
s.set_color(6);
|
||||||
|
s.set_child(true);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-5.0, 101.0, -4.5]);
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_head_yaw(270.0);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Sheep, ());
|
||||||
|
if let TrackedData::Sheep(s) = e.data_mut() {
|
||||||
|
s.set_color(6);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([5.0, 101.0, -4.5]);
|
||||||
|
e.set_yaw(90.0);
|
||||||
|
e.set_head_yaw(90.0);
|
||||||
|
|
||||||
|
// ==== Players ==== //
|
||||||
|
let player_poses = [
|
||||||
|
Pose::Standing,
|
||||||
|
Pose::Sneaking,
|
||||||
|
Pose::FallFlying,
|
||||||
|
Pose::Sleeping,
|
||||||
|
Pose::Swimming,
|
||||||
|
Pose::SpinAttack,
|
||||||
|
Pose::Dying,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (i, pose) in player_poses.into_iter().enumerate() {
|
||||||
|
player_list.insert(
|
||||||
|
Uuid::from_u128(i as u128),
|
||||||
|
format!("fake_player_{i}"),
|
||||||
|
None,
|
||||||
|
GameMode::Survival,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (_, e) = server
|
||||||
|
.entities
|
||||||
|
.insert_with_uuid(EntityKind::Player, Uuid::from_u128(i as u128), ())
|
||||||
|
.unwrap();
|
||||||
|
if let TrackedData::Player(p) = e.data_mut() {
|
||||||
|
p.set_pose(pose);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-3.0 + i as f64 * 2.0, 104.0, -9.0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== Warden ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Warden, ());
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-7.0, 102.0, -4.5]);
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_head_yaw(270.0);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Warden, ());
|
||||||
|
if let TrackedData::Warden(w) = e.data_mut() {
|
||||||
|
w.set_pose(Pose::Emerging);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([-7.0, 102.0, -6.5]);
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_head_yaw(270.0);
|
||||||
|
|
||||||
|
// ==== Goat ==== //
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Goat, ());
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([5.0, 103.0, -4.5]);
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_head_yaw(90.0);
|
||||||
|
|
||||||
|
let (_, e) = server.entities.insert(EntityKind::Goat, ());
|
||||||
|
if let TrackedData::Goat(g) = e.data_mut() {
|
||||||
|
g.set_pose(Pose::LongJumping);
|
||||||
|
}
|
||||||
|
e.set_world(world_id);
|
||||||
|
e.set_position([5.0, 103.0, -3.5]);
|
||||||
|
e.set_yaw(270.0);
|
||||||
|
e.set_head_yaw(90.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, server: &mut Server<Self>) {
|
||||||
|
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
||||||
|
|
||||||
|
server.clients.retain(|_, client| {
|
||||||
|
if client.created_this_tick() {
|
||||||
|
if self
|
||||||
|
.player_count
|
||||||
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
||||||
|
(count < MAX_PLAYERS).then_some(count + 1)
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
client.disconnect("The server is full!".color(Color::RED));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match server
|
||||||
|
.entities
|
||||||
|
.insert_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||||
|
{
|
||||||
|
Some((id, _)) => client.state.player = id,
|
||||||
|
None => {
|
||||||
|
client.disconnect("Conflicting UUID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.spawn(world_id);
|
||||||
|
client.set_flat(true);
|
||||||
|
client.set_game_mode(GameMode::Creative);
|
||||||
|
client.teleport(
|
||||||
|
[
|
||||||
|
SPAWN_POS.x as f64 + 0.5,
|
||||||
|
SPAWN_POS.y as f64 + 1.0,
|
||||||
|
SPAWN_POS.z as f64 + 0.5,
|
||||||
|
],
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
client.set_player_list(server.state.clone());
|
||||||
|
|
||||||
|
if let Some(id) = &server.state {
|
||||||
|
server.player_lists.get_mut(id).insert(
|
||||||
|
client.uuid(),
|
||||||
|
client.username(),
|
||||||
|
client.textures().cloned(),
|
||||||
|
client.game_mode(),
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.send_message(
|
||||||
|
"Press ".italic()
|
||||||
|
+ "F3 + B".italic().color(Color::AQUA)
|
||||||
|
+ " to show hitboxes.".italic(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.is_disconnected() {
|
||||||
|
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
||||||
|
if let Some(id) = &server.state {
|
||||||
|
server.player_lists.get_mut(id).remove(client.uuid());
|
||||||
|
}
|
||||||
|
server.entities.remove(client.state.player);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let client_pos = client.position();
|
||||||
|
|
||||||
|
let origin = Vec3::new(client_pos.x, client_pos.y + PLAYER_EYE_HEIGHT, client_pos.z);
|
||||||
|
let direction = from_yaw_and_pitch(client.yaw() as f64, client.pitch() as f64);
|
||||||
|
let not_self_or_bullet = |hit: &RaycastHit| {
|
||||||
|
hit.entity != client.state.player && hit.entity != client.state.shulker_bullet
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(hit) = world
|
||||||
|
.spatial_index
|
||||||
|
.raycast(origin, direction, not_self_or_bullet)
|
||||||
|
{
|
||||||
|
let bullet =
|
||||||
|
if let Some(bullet) = server.entities.get_mut(client.state.shulker_bullet) {
|
||||||
|
bullet
|
||||||
|
} else {
|
||||||
|
let (id, bullet) = server.entities.insert(EntityKind::ShulkerBullet, ());
|
||||||
|
client.state.shulker_bullet = id;
|
||||||
|
bullet.set_world(world_id);
|
||||||
|
bullet
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut hit_pos = origin + direction * hit.near;
|
||||||
|
let hitbox = bullet.hitbox();
|
||||||
|
|
||||||
|
hit_pos.y -= (hitbox.max.y - hitbox.min.y) / 2.0;
|
||||||
|
|
||||||
|
bullet.set_position(hit_pos);
|
||||||
|
|
||||||
|
client.set_action_bar("Intersection".color(Color::GREEN));
|
||||||
|
} else {
|
||||||
|
server.entities.remove(client.state.shulker_bullet);
|
||||||
|
client.set_action_bar("No Intersection".color(Color::RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
while handle_event_default(
|
||||||
|
client,
|
||||||
|
server.entities.get_mut(client.state.player).unwrap(),
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{}
|
||||||
|
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,206 +0,0 @@
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use log::LevelFilter;
|
|
||||||
use valence::async_trait;
|
|
||||||
use valence::block::{BlockPos, BlockState};
|
|
||||||
use valence::chunk::UnloadedChunk;
|
|
||||||
use valence::client::{handle_event_default, GameMode};
|
|
||||||
use valence::config::{Config, ServerListPing};
|
|
||||||
use valence::dimension::DimensionId;
|
|
||||||
use valence::entity::{EntityId, EntityKind, TrackedData};
|
|
||||||
use valence::player_list::PlayerListId;
|
|
||||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
|
||||||
use valence::spatial_index::RaycastHit;
|
|
||||||
use valence::text::{Color, TextFormat};
|
|
||||||
use valence::util::from_yaw_and_pitch;
|
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
pub fn main() -> ShutdownResult {
|
|
||||||
env_logger::Builder::new()
|
|
||||||
.filter_module("valence", LevelFilter::Trace)
|
|
||||||
.parse_default_env()
|
|
||||||
.init();
|
|
||||||
|
|
||||||
valence::start_server(
|
|
||||||
Game {
|
|
||||||
player_count: AtomicUsize::new(0),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Game {
|
|
||||||
player_count: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_PLAYERS: usize = 10;
|
|
||||||
|
|
||||||
const SPAWN_POS: BlockPos = BlockPos::new(0, 100, -5);
|
|
||||||
|
|
||||||
const PLAYER_EYE_HEIGHT: f64 = 1.62;
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// const PLAYER_SNEAKING_EYE_HEIGHT: f64 = 1.495;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Config for Game {
|
|
||||||
type ServerState = Option<PlayerListId>;
|
|
||||||
type ClientState = EntityId;
|
|
||||||
/// `true` for entities that have been intersected with.
|
|
||||||
type EntityState = bool;
|
|
||||||
type WorldState = ();
|
|
||||||
type ChunkState = ();
|
|
||||||
type PlayerListState = ();
|
|
||||||
|
|
||||||
fn max_connections(&self) -> usize {
|
|
||||||
// We want status pings to be successful even if the server is full.
|
|
||||||
MAX_PLAYERS + 64
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn server_list_ping(
|
|
||||||
&self,
|
|
||||||
_server: &SharedServer<Self>,
|
|
||||||
_remote_addr: SocketAddr,
|
|
||||||
_protocol_version: i32,
|
|
||||||
) -> ServerListPing {
|
|
||||||
ServerListPing::Respond {
|
|
||||||
online_players: self.player_count.load(Ordering::SeqCst) as i32,
|
|
||||||
max_players: MAX_PLAYERS as i32,
|
|
||||||
player_sample: Default::default(),
|
|
||||||
description: "Hello Valence!".color(Color::AQUA),
|
|
||||||
favicon_png: Some(include_bytes!("../assets/logo-64x64.png").as_slice().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&self, server: &mut Server<Self>) {
|
|
||||||
let (world_id, world) = server.worlds.insert(DimensionId::default(), ());
|
|
||||||
server.state = Some(server.player_lists.insert(()).0);
|
|
||||||
|
|
||||||
let size = 5;
|
|
||||||
for z in -size..size {
|
|
||||||
for x in -size..size {
|
|
||||||
world.chunks.insert([x, z], UnloadedChunk::default(), ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
world.chunks.set_block_state(SPAWN_POS, BlockState::BEDROCK);
|
|
||||||
|
|
||||||
const SHEEP_COUNT: usize = 10;
|
|
||||||
for i in 0..SHEEP_COUNT {
|
|
||||||
let offset = (i as f64 - (SHEEP_COUNT - 1) as f64 / 2.0) * 1.25;
|
|
||||||
|
|
||||||
let (_, sheep) = server.entities.insert(EntityKind::Sheep, false);
|
|
||||||
sheep.set_world(world_id);
|
|
||||||
sheep.set_position([offset + 0.5, SPAWN_POS.y as f64 + 1.0, 0.0]);
|
|
||||||
sheep.set_yaw(180.0);
|
|
||||||
sheep.set_head_yaw(180.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&self, server: &mut Server<Self>) {
|
|
||||||
let (world_id, world) = server.worlds.iter_mut().next().unwrap();
|
|
||||||
|
|
||||||
server.clients.retain(|_, client| {
|
|
||||||
if client.created_this_tick() {
|
|
||||||
if self
|
|
||||||
.player_count
|
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
|
|
||||||
(count < MAX_PLAYERS).then_some(count + 1)
|
|
||||||
})
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
client.disconnect("The server is full!".color(Color::RED));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match server
|
|
||||||
.entities
|
|
||||||
.insert_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_flat(true);
|
|
||||||
client.set_game_mode(GameMode::Creative);
|
|
||||||
client.teleport(
|
|
||||||
[
|
|
||||||
SPAWN_POS.x as f64 + 0.5,
|
|
||||||
SPAWN_POS.y as f64 + 1.0,
|
|
||||||
SPAWN_POS.z as f64 + 0.5,
|
|
||||||
],
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
);
|
|
||||||
client.set_player_list(server.state.clone());
|
|
||||||
|
|
||||||
if let Some(id) = &server.state {
|
|
||||||
server.player_lists.get_mut(id).insert(
|
|
||||||
client.uuid(),
|
|
||||||
client.username(),
|
|
||||||
client.textures().cloned(),
|
|
||||||
client.game_mode(),
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
client.send_message(
|
|
||||||
"Look at a sheep to change its ".italic()
|
|
||||||
+ "color".italic().color(Color::GREEN)
|
|
||||||
+ ".",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if client.is_disconnected() {
|
|
||||||
self.player_count.fetch_sub(1, Ordering::SeqCst);
|
|
||||||
if let Some(id) = &server.state {
|
|
||||||
server.player_lists.get_mut(id).remove(client.uuid());
|
|
||||||
}
|
|
||||||
server.entities.remove(client.state);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let client_pos = client.position();
|
|
||||||
|
|
||||||
let origin = Vec3::new(client_pos.x, client_pos.y + PLAYER_EYE_HEIGHT, client_pos.z);
|
|
||||||
let direction = from_yaw_and_pitch(client.yaw() as f64, client.pitch() as f64);
|
|
||||||
let only_sheep = |hit: &RaycastHit| {
|
|
||||||
server
|
|
||||||
.entities
|
|
||||||
.get(hit.entity)
|
|
||||||
.map_or(false, |e| e.kind() == EntityKind::Sheep)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(hit) = world.spatial_index.raycast(origin, direction, only_sheep) {
|
|
||||||
if let Some(e) = server.entities.get_mut(hit.entity) {
|
|
||||||
e.state = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while handle_event_default(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.data_mut() {
|
|
||||||
if intersected {
|
|
||||||
sheep.set_color(5);
|
|
||||||
} else {
|
|
||||||
sheep.set_color(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.state = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
262
src/entity.rs
262
src/entity.rs
|
@ -12,6 +12,7 @@ use uuid::Uuid;
|
||||||
use vek::{Aabb, Vec3};
|
use vek::{Aabb, Vec3};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::entity::types::{Facing, PaintingKind, Pose};
|
||||||
use crate::protocol::packets::s2c::play::{
|
use crate::protocol::packets::s2c::play::{
|
||||||
S2cPlayPacket, SetEntityMetadata, SpawnEntity, SpawnExperienceOrb, SpawnPlayer,
|
S2cPlayPacket, SetEntityMetadata, SpawnEntity, SpawnExperienceOrb, SpawnPlayer,
|
||||||
};
|
};
|
||||||
|
@ -422,12 +423,49 @@ impl<C: Config> Entity<C> {
|
||||||
///
|
///
|
||||||
/// [interact event]: crate::client::ClientEvent::InteractWithEntity
|
/// [interact event]: crate::client::ClientEvent::InteractWithEntity
|
||||||
pub fn hitbox(&self) -> Aabb<f64> {
|
pub fn hitbox(&self) -> Aabb<f64> {
|
||||||
let dims = match &self.variants {
|
fn baby(is_baby: bool, adult_hitbox: [f64; 3]) -> [f64; 3] {
|
||||||
|
if is_baby {
|
||||||
|
adult_hitbox.map(|a| a / 2.0)
|
||||||
|
} else {
|
||||||
|
adult_hitbox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn item_frame(pos: Vec3<f64>, rotation: i32) -> Aabb<f64> {
|
||||||
|
let mut center_pos = pos + 0.5;
|
||||||
|
|
||||||
|
match rotation {
|
||||||
|
0 => center_pos.y += 0.46875,
|
||||||
|
1 => center_pos.y -= 0.46875,
|
||||||
|
2 => center_pos.z += 0.46875,
|
||||||
|
3 => center_pos.z -= 0.46875,
|
||||||
|
4 => center_pos.x += 0.46875,
|
||||||
|
5 => center_pos.x -= 0.46875,
|
||||||
|
_ => center_pos.y -= 0.46875,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bounds = Vec3::from(match rotation {
|
||||||
|
0 | 1 => [0.75, 0.0625, 0.75],
|
||||||
|
2 | 3 => [0.75, 0.75, 0.0625],
|
||||||
|
4 | 5 => [0.0625, 0.75, 0.75],
|
||||||
|
_ => [0.75, 0.0625, 0.75],
|
||||||
|
});
|
||||||
|
|
||||||
|
Aabb {
|
||||||
|
min: center_pos - bounds / 2.0,
|
||||||
|
max: center_pos + bounds / 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dimensions = match &self.variants {
|
||||||
TrackedData::Allay(_) => [0.6, 0.35, 0.6],
|
TrackedData::Allay(_) => [0.6, 0.35, 0.6],
|
||||||
TrackedData::ChestBoat(_) => [1.375, 0.5625, 1.375],
|
TrackedData::ChestBoat(_) => [1.375, 0.5625, 1.375],
|
||||||
TrackedData::Frog(_) => [0.5, 0.5, 0.5],
|
TrackedData::Frog(_) => [0.5, 0.5, 0.5],
|
||||||
TrackedData::Tadpole(_) => [0.4, 0.3, 0.4],
|
TrackedData::Tadpole(_) => [0.4, 0.3, 0.4],
|
||||||
TrackedData::Warden(_) => [0.9, 2.9, 0.9],
|
TrackedData::Warden(e) => match e.get_pose() {
|
||||||
|
Pose::Emerging | Pose::Digging => [0.9, 1.0, 0.9],
|
||||||
|
_ => [0.9, 2.9, 0.9],
|
||||||
|
},
|
||||||
TrackedData::AreaEffectCloud(e) => [
|
TrackedData::AreaEffectCloud(e) => [
|
||||||
e.get_radius() as f64 * 2.0,
|
e.get_radius() as f64 * 2.0,
|
||||||
0.5,
|
0.5,
|
||||||
|
@ -445,19 +483,19 @@ impl<C: Config> Entity<C> {
|
||||||
TrackedData::Arrow(_) => [0.5, 0.5, 0.5],
|
TrackedData::Arrow(_) => [0.5, 0.5, 0.5],
|
||||||
TrackedData::Axolotl(_) => [1.3, 0.6, 1.3],
|
TrackedData::Axolotl(_) => [1.3, 0.6, 1.3],
|
||||||
TrackedData::Bat(_) => [0.5, 0.9, 0.5],
|
TrackedData::Bat(_) => [0.5, 0.9, 0.5],
|
||||||
TrackedData::Bee(_) => [0.7, 0.6, 0.7], // TODO: baby size?
|
TrackedData::Bee(e) => baby(e.get_child(), [0.7, 0.6, 0.7]),
|
||||||
TrackedData::Blaze(_) => [0.6, 1.8, 0.6],
|
TrackedData::Blaze(_) => [0.6, 1.8, 0.6],
|
||||||
TrackedData::Boat(_) => [1.375, 0.5625, 1.375],
|
TrackedData::Boat(_) => [1.375, 0.5625, 1.375],
|
||||||
TrackedData::Cat(_) => [0.6, 0.7, 0.6],
|
TrackedData::Cat(_) => [0.6, 0.7, 0.6],
|
||||||
TrackedData::CaveSpider(_) => [0.7, 0.5, 0.7],
|
TrackedData::CaveSpider(_) => [0.7, 0.5, 0.7],
|
||||||
TrackedData::Chicken(_) => [0.4, 0.7, 0.4], // TODO: baby size?
|
TrackedData::Chicken(e) => baby(e.get_child(), [0.4, 0.7, 0.4]),
|
||||||
TrackedData::Cod(_) => [0.5, 0.3, 0.5],
|
TrackedData::Cod(_) => [0.5, 0.3, 0.5],
|
||||||
TrackedData::Cow(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
TrackedData::Cow(e) => baby(e.get_child(), [0.9, 1.4, 0.9]),
|
||||||
TrackedData::Creeper(_) => [0.6, 1.7, 0.6],
|
TrackedData::Creeper(_) => [0.6, 1.7, 0.6],
|
||||||
TrackedData::Dolphin(_) => [0.9, 0.6, 0.9],
|
TrackedData::Dolphin(_) => [0.9, 0.6, 0.9],
|
||||||
TrackedData::Donkey(_) => [1.5, 1.39648, 1.5], // TODO: baby size?
|
TrackedData::Donkey(e) => baby(e.get_child(), [1.5, 1.39648, 1.5]),
|
||||||
TrackedData::DragonFireball(_) => [1.0, 1.0, 1.0],
|
TrackedData::DragonFireball(_) => [1.0, 1.0, 1.0],
|
||||||
TrackedData::Drowned(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::Drowned(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::ElderGuardian(_) => [1.9975, 1.9975, 1.9975],
|
TrackedData::ElderGuardian(_) => [1.9975, 1.9975, 1.9975],
|
||||||
TrackedData::EndCrystal(_) => [2.0, 2.0, 2.0],
|
TrackedData::EndCrystal(_) => [2.0, 2.0, 2.0],
|
||||||
TrackedData::EnderDragon(_) => [16.0, 8.0, 16.0],
|
TrackedData::EnderDragon(_) => [16.0, 8.0, 16.0],
|
||||||
|
@ -469,27 +507,35 @@ impl<C: Config> Entity<C> {
|
||||||
TrackedData::EyeOfEnder(_) => [0.25, 0.25, 0.25],
|
TrackedData::EyeOfEnder(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::FallingBlock(_) => [0.98, 0.98, 0.98],
|
TrackedData::FallingBlock(_) => [0.98, 0.98, 0.98],
|
||||||
TrackedData::FireworkRocket(_) => [0.25, 0.25, 0.25],
|
TrackedData::FireworkRocket(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::Fox(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
TrackedData::Fox(e) => baby(e.get_child(), [0.6, 0.7, 0.6]),
|
||||||
TrackedData::Ghast(_) => [4.0, 4.0, 4.0],
|
TrackedData::Ghast(_) => [4.0, 4.0, 4.0],
|
||||||
TrackedData::Giant(_) => [3.6, 12.0, 3.6],
|
TrackedData::Giant(_) => [3.6, 12.0, 3.6],
|
||||||
TrackedData::GlowItemFrame(_) => todo!("account for rotation"),
|
TrackedData::GlowItemFrame(e) => {
|
||||||
|
return item_frame(self.new_position, e.get_rotation())
|
||||||
|
}
|
||||||
TrackedData::GlowSquid(_) => [0.8, 0.8, 0.8],
|
TrackedData::GlowSquid(_) => [0.8, 0.8, 0.8],
|
||||||
TrackedData::Goat(_) => [1.3, 0.9, 1.3], // TODO: baby size?
|
TrackedData::Goat(e) => {
|
||||||
|
if e.get_pose() == Pose::LongJumping {
|
||||||
|
baby(e.get_child(), [0.63, 0.91, 0.63])
|
||||||
|
} else {
|
||||||
|
baby(e.get_child(), [0.9, 1.3, 0.9])
|
||||||
|
}
|
||||||
|
}
|
||||||
TrackedData::Guardian(_) => [0.85, 0.85, 0.85],
|
TrackedData::Guardian(_) => [0.85, 0.85, 0.85],
|
||||||
TrackedData::Hoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
TrackedData::Hoglin(e) => baby(e.get_child(), [1.39648, 1.4, 1.39648]),
|
||||||
TrackedData::Horse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
TrackedData::Horse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
TrackedData::Husk(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::Husk(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::Illusioner(_) => [0.6, 1.95, 0.6],
|
TrackedData::Illusioner(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::IronGolem(_) => [1.4, 2.7, 1.4],
|
TrackedData::IronGolem(_) => [1.4, 2.7, 1.4],
|
||||||
TrackedData::Item(_) => [0.25, 0.25, 0.25],
|
TrackedData::Item(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::ItemFrame(_) => todo!("account for rotation"),
|
TrackedData::ItemFrame(e) => return item_frame(self.new_position, e.get_rotation()),
|
||||||
TrackedData::Fireball(_) => [1.0, 1.0, 1.0],
|
TrackedData::Fireball(_) => [1.0, 1.0, 1.0],
|
||||||
TrackedData::LeashKnot(_) => [0.375, 0.5, 0.375],
|
TrackedData::LeashKnot(_) => [0.375, 0.5, 0.375],
|
||||||
TrackedData::Lightning(_) => [0.0, 0.0, 0.0],
|
TrackedData::Lightning(_) => [0.0, 0.0, 0.0],
|
||||||
TrackedData::Llama(_) => [0.9, 1.87, 0.9], // TODO: baby size?
|
TrackedData::Llama(e) => baby(e.get_child(), [0.9, 1.87, 0.9]),
|
||||||
TrackedData::LlamaSpit(_) => [0.25, 0.25, 0.25],
|
TrackedData::LlamaSpit(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::MagmaCube(e) => {
|
TrackedData::MagmaCube(e) => {
|
||||||
let s = e.get_slime_size() as f64 * 0.51000005;
|
let s = 0.5202 * e.get_slime_size() as f64;
|
||||||
[s, s, s]
|
[s, s, s]
|
||||||
}
|
}
|
||||||
TrackedData::Marker(_) => [0.0, 0.0, 0.0],
|
TrackedData::Marker(_) => [0.0, 0.0, 0.0],
|
||||||
|
@ -500,31 +546,111 @@ impl<C: Config> Entity<C> {
|
||||||
TrackedData::HopperMinecart(_) => [0.98, 0.7, 0.98],
|
TrackedData::HopperMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
TrackedData::SpawnerMinecart(_) => [0.98, 0.7, 0.98],
|
TrackedData::SpawnerMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
TrackedData::TntMinecart(_) => [0.98, 0.7, 0.98],
|
TrackedData::TntMinecart(_) => [0.98, 0.7, 0.98],
|
||||||
TrackedData::Mule(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
TrackedData::Mule(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
TrackedData::Mooshroom(_) => [0.9, 1.4, 0.9], // TODO: baby size?
|
TrackedData::Mooshroom(e) => baby(e.get_child(), [0.9, 1.4, 0.9]),
|
||||||
TrackedData::Ocelot(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
TrackedData::Ocelot(e) => baby(e.get_child(), [0.6, 0.7, 0.6]),
|
||||||
TrackedData::Painting(_) => todo!("account for rotation and type"),
|
TrackedData::Painting(e) => {
|
||||||
TrackedData::Panda(_) => [0.6, 0.7, 0.6], // TODO: baby size?
|
let bounds: Vec3<u32> = match e.get_variant() {
|
||||||
|
PaintingKind::Kebab => [1, 1, 1],
|
||||||
|
PaintingKind::Aztec => [1, 1, 1],
|
||||||
|
PaintingKind::Alban => [1, 1, 1],
|
||||||
|
PaintingKind::Aztec2 => [1, 1, 1],
|
||||||
|
PaintingKind::Bomb => [1, 1, 1],
|
||||||
|
PaintingKind::Plant => [1, 1, 1],
|
||||||
|
PaintingKind::Wasteland => [1, 1, 1],
|
||||||
|
PaintingKind::Pool => [2, 1, 2],
|
||||||
|
PaintingKind::Courbet => [2, 1, 2],
|
||||||
|
PaintingKind::Sea => [2, 1, 2],
|
||||||
|
PaintingKind::Sunset => [2, 1, 2],
|
||||||
|
PaintingKind::Creebet => [2, 1, 2],
|
||||||
|
PaintingKind::Wanderer => [1, 2, 1],
|
||||||
|
PaintingKind::Graham => [1, 2, 1],
|
||||||
|
PaintingKind::Match => [2, 2, 2],
|
||||||
|
PaintingKind::Bust => [2, 2, 2],
|
||||||
|
PaintingKind::Stage => [2, 2, 2],
|
||||||
|
PaintingKind::Void => [2, 2, 2],
|
||||||
|
PaintingKind::SkullAndRoses => [2, 2, 2],
|
||||||
|
PaintingKind::Wither => [2, 2, 2],
|
||||||
|
PaintingKind::Fighters => [4, 2, 4],
|
||||||
|
PaintingKind::Pointer => [4, 4, 4],
|
||||||
|
PaintingKind::Pigscene => [4, 4, 4],
|
||||||
|
PaintingKind::BurningSkull => [4, 4, 4],
|
||||||
|
PaintingKind::Skeleton => [4, 3, 4],
|
||||||
|
PaintingKind::Earth => [2, 2, 2],
|
||||||
|
PaintingKind::Wind => [2, 2, 2],
|
||||||
|
PaintingKind::Water => [2, 2, 2],
|
||||||
|
PaintingKind::Fire => [2, 2, 2],
|
||||||
|
PaintingKind::DonkeyKong => [4, 3, 4],
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let mut center_pos = self.new_position + 0.5;
|
||||||
|
|
||||||
|
let (facing_x, facing_z, cc_facing_x, cc_facing_z) =
|
||||||
|
match ((self.yaw + 45.0).rem_euclid(360.0) / 90.0) as u8 {
|
||||||
|
0 => (0, 1, 1, 0), // South
|
||||||
|
1 => (-1, 0, 0, 1), // West
|
||||||
|
2 => (0, -1, -1, 0), // North
|
||||||
|
_ => (1, 0, 0, -1), // East
|
||||||
|
};
|
||||||
|
|
||||||
|
center_pos.x -= facing_x as f64 * 0.46875;
|
||||||
|
center_pos.z -= facing_z as f64 * 0.46875;
|
||||||
|
|
||||||
|
center_pos.x += cc_facing_x as f64 * if bounds.x % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
center_pos.y += if bounds.y % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
center_pos.z += cc_facing_z as f64 * if bounds.z % 2 == 0 { 0.5 } else { 0.0 };
|
||||||
|
|
||||||
|
let bounds = match (facing_x, facing_z) {
|
||||||
|
(1, 0) | (-1, 0) => bounds.as_().with_x(0.0625),
|
||||||
|
_ => bounds.as_().with_z(0.0625),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Aabb {
|
||||||
|
min: center_pos - bounds / 2.0,
|
||||||
|
max: center_pos + bounds / 2.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
TrackedData::Panda(e) => baby(e.get_child(), [1.3, 1.25, 1.3]),
|
||||||
TrackedData::Parrot(_) => [0.5, 0.9, 0.5],
|
TrackedData::Parrot(_) => [0.5, 0.9, 0.5],
|
||||||
TrackedData::Phantom(_) => [0.9, 0.5, 0.9],
|
TrackedData::Phantom(_) => [0.9, 0.5, 0.9],
|
||||||
TrackedData::Pig(_) => [0.9, 0.9, 0.9], // TODO: baby size?
|
TrackedData::Pig(e) => baby(e.get_child(), [0.9, 0.9, 0.9]),
|
||||||
TrackedData::Piglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::Piglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::PiglinBrute(_) => [0.6, 1.95, 0.6],
|
TrackedData::PiglinBrute(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::Pillager(_) => [0.6, 1.95, 0.6],
|
TrackedData::Pillager(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::PolarBear(_) => [1.4, 1.4, 1.4], // TODO: baby size?
|
TrackedData::PolarBear(e) => baby(e.get_child(), [1.4, 1.4, 1.4]),
|
||||||
TrackedData::Tnt(_) => [0.98, 0.98, 0.98],
|
TrackedData::Tnt(_) => [0.98, 0.98, 0.98],
|
||||||
TrackedData::Pufferfish(_) => [0.7, 0.7, 0.7],
|
TrackedData::Pufferfish(_) => [0.7, 0.7, 0.7],
|
||||||
TrackedData::Rabbit(_) => [0.4, 0.5, 0.4], // TODO: baby size?
|
TrackedData::Rabbit(e) => baby(e.get_child(), [0.4, 0.5, 0.4]),
|
||||||
TrackedData::Ravager(_) => [1.95, 2.2, 1.95],
|
TrackedData::Ravager(_) => [1.95, 2.2, 1.95],
|
||||||
TrackedData::Salmon(_) => [0.7, 0.4, 0.7],
|
TrackedData::Salmon(_) => [0.7, 0.4, 0.7],
|
||||||
TrackedData::Sheep(_) => [0.9, 1.3, 0.9], // TODO: baby size?
|
TrackedData::Sheep(e) => baby(e.get_child(), [0.9, 1.3, 0.9]),
|
||||||
TrackedData::Shulker(_) => [1.0, 1.0, 1.0], // TODO: how is height calculated?
|
TrackedData::Shulker(e) => {
|
||||||
|
const PI: f64 = std::f64::consts::PI;
|
||||||
|
|
||||||
|
let pos = self.new_position + 0.5;
|
||||||
|
let mut min = pos - 0.5;
|
||||||
|
let mut max = pos + 0.5;
|
||||||
|
|
||||||
|
let peek = 0.5 - f64::cos(e.get_peek_amount() as f64 * 0.01 * PI) * 0.5;
|
||||||
|
|
||||||
|
match e.get_attached_face() {
|
||||||
|
Facing::Down => max.y += peek,
|
||||||
|
Facing::Up => min.y -= peek,
|
||||||
|
Facing::North => max.z += peek,
|
||||||
|
Facing::South => min.z -= peek,
|
||||||
|
Facing::West => max.x += peek,
|
||||||
|
Facing::East => min.x -= peek,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Aabb { min, max };
|
||||||
|
}
|
||||||
TrackedData::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125],
|
TrackedData::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125],
|
||||||
TrackedData::Silverfish(_) => [0.4, 0.3, 0.4],
|
TrackedData::Silverfish(_) => [0.4, 0.3, 0.4],
|
||||||
TrackedData::Skeleton(_) => [0.6, 1.99, 0.6],
|
TrackedData::Skeleton(_) => [0.6, 1.99, 0.6],
|
||||||
TrackedData::SkeletonHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
TrackedData::SkeletonHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
TrackedData::Slime(e) => {
|
TrackedData::Slime(e) => {
|
||||||
let s = 0.51000005 * e.get_slime_size() as f64;
|
let s = 0.5202 * e.get_slime_size() as f64;
|
||||||
[s, s, s]
|
[s, s, s]
|
||||||
}
|
}
|
||||||
TrackedData::SmallFireball(_) => [0.3125, 0.3125, 0.3125],
|
TrackedData::SmallFireball(_) => [0.3125, 0.3125, 0.3125],
|
||||||
|
@ -534,7 +660,7 @@ impl<C: Config> Entity<C> {
|
||||||
TrackedData::Spider(_) => [1.4, 0.9, 1.4],
|
TrackedData::Spider(_) => [1.4, 0.9, 1.4],
|
||||||
TrackedData::Squid(_) => [0.8, 0.8, 0.8],
|
TrackedData::Squid(_) => [0.8, 0.8, 0.8],
|
||||||
TrackedData::Stray(_) => [0.6, 1.99, 0.6],
|
TrackedData::Stray(_) => [0.6, 1.99, 0.6],
|
||||||
TrackedData::Strider(_) => [0.9, 1.7, 0.9], // TODO: baby size?
|
TrackedData::Strider(e) => baby(e.get_child(), [0.9, 1.7, 0.9]),
|
||||||
TrackedData::Egg(_) => [0.25, 0.25, 0.25],
|
TrackedData::Egg(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::EnderPearl(_) => [0.25, 0.25, 0.25],
|
TrackedData::EnderPearl(_) => [0.25, 0.25, 0.25],
|
||||||
TrackedData::ExperienceBottle(_) => [0.25, 0.25, 0.25],
|
TrackedData::ExperienceBottle(_) => [0.25, 0.25, 0.25],
|
||||||
|
@ -542,26 +668,41 @@ impl<C: Config> Entity<C> {
|
||||||
TrackedData::Trident(_) => [0.5, 0.5, 0.5],
|
TrackedData::Trident(_) => [0.5, 0.5, 0.5],
|
||||||
TrackedData::TraderLlama(_) => [0.9, 1.87, 0.9],
|
TrackedData::TraderLlama(_) => [0.9, 1.87, 0.9],
|
||||||
TrackedData::TropicalFish(_) => [0.5, 0.4, 0.5],
|
TrackedData::TropicalFish(_) => [0.5, 0.4, 0.5],
|
||||||
TrackedData::Turtle(_) => [1.2, 0.4, 1.2], // TODO: baby size?
|
TrackedData::Turtle(e) => {
|
||||||
|
if e.get_child() {
|
||||||
|
[0.36, 0.12, 0.36]
|
||||||
|
} else {
|
||||||
|
[1.2, 0.4, 1.2]
|
||||||
|
}
|
||||||
|
}
|
||||||
TrackedData::Vex(_) => [0.4, 0.8, 0.4],
|
TrackedData::Vex(_) => [0.4, 0.8, 0.4],
|
||||||
TrackedData::Villager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::Villager(e) => baby(e.get_child(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::Vindicator(_) => [0.6, 1.95, 0.6],
|
TrackedData::Vindicator(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::WanderingTrader(_) => [0.6, 1.95, 0.6],
|
TrackedData::WanderingTrader(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::Witch(_) => [0.6, 1.95, 0.6],
|
TrackedData::Witch(_) => [0.6, 1.95, 0.6],
|
||||||
TrackedData::Wither(_) => [0.9, 3.5, 0.9],
|
TrackedData::Wither(_) => [0.9, 3.5, 0.9],
|
||||||
TrackedData::WitherSkeleton(_) => [0.7, 2.4, 0.7],
|
TrackedData::WitherSkeleton(_) => [0.7, 2.4, 0.7],
|
||||||
TrackedData::WitherSkull(_) => [0.3125, 0.3125, 0.3125],
|
TrackedData::WitherSkull(_) => [0.3125, 0.3125, 0.3125],
|
||||||
TrackedData::Wolf(_) => [0.6, 0.85, 0.6], // TODO: baby size?
|
TrackedData::Wolf(e) => baby(e.get_child(), [0.6, 0.85, 0.6]),
|
||||||
TrackedData::Zoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size?
|
TrackedData::Zoglin(e) => baby(e.get_baby(), [1.39648, 1.4, 1.39648]),
|
||||||
TrackedData::Zombie(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::Zombie(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::ZombieHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size?
|
TrackedData::ZombieHorse(e) => baby(e.get_child(), [1.39648, 1.6, 1.39648]),
|
||||||
TrackedData::ZombieVillager(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::ZombieVillager(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::ZombifiedPiglin(_) => [0.6, 1.95, 0.6], // TODO: baby size?
|
TrackedData::ZombifiedPiglin(e) => baby(e.get_baby(), [0.6, 1.95, 0.6]),
|
||||||
TrackedData::Player(_) => [0.6, 1.8, 0.6], // TODO: changes depending on the pose.
|
TrackedData::Player(e) => match e.get_pose() {
|
||||||
|
types::Pose::Standing => [0.6, 1.8, 0.6],
|
||||||
|
types::Pose::Sleeping => [0.2, 0.2, 0.2],
|
||||||
|
types::Pose::FallFlying => [0.6, 0.6, 0.6],
|
||||||
|
types::Pose::Swimming => [0.6, 0.6, 0.6],
|
||||||
|
types::Pose::SpinAttack => [0.6, 0.6, 0.6],
|
||||||
|
types::Pose::Sneaking => [0.6, 1.5, 0.6],
|
||||||
|
types::Pose::Dying => [0.2, 0.2, 0.2],
|
||||||
|
_ => [0.6, 1.8, 0.6],
|
||||||
|
},
|
||||||
TrackedData::FishingBobber(_) => [0.25, 0.25, 0.25],
|
TrackedData::FishingBobber(_) => [0.25, 0.25, 0.25],
|
||||||
};
|
};
|
||||||
|
|
||||||
aabb_from_bottom_and_size(self.new_position, dims.into())
|
aabb_from_bottom_and_size(self.new_position, dimensions.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the tracked data packet to send to clients after this entity has
|
/// Gets the tracked data packet to send to clients after this entity has
|
||||||
|
@ -597,6 +738,20 @@ impl<C: Config> Entity<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn spawn_packet(&self, this_id: EntityId) -> Option<EntitySpawnPacket> {
|
pub(crate) fn spawn_packet(&self, this_id: EntityId) -> Option<EntitySpawnPacket> {
|
||||||
|
let with_object_data = |data| {
|
||||||
|
Some(EntitySpawnPacket::Entity(SpawnEntity {
|
||||||
|
entity_id: VarInt(this_id.to_network_id()),
|
||||||
|
object_uuid: self.uuid,
|
||||||
|
kind: VarInt(self.kind() as i32),
|
||||||
|
position: self.new_position,
|
||||||
|
pitch: ByteAngle::from_degrees(self.pitch),
|
||||||
|
yaw: ByteAngle::from_degrees(self.yaw),
|
||||||
|
head_yaw: ByteAngle::from_degrees(self.head_yaw),
|
||||||
|
data: VarInt(data),
|
||||||
|
velocity: velocity_to_packet_units(self.velocity),
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
match &self.variants {
|
match &self.variants {
|
||||||
TrackedData::Marker(_) => None,
|
TrackedData::Marker(_) => None,
|
||||||
TrackedData::ExperienceOrb(_) => {
|
TrackedData::ExperienceOrb(_) => {
|
||||||
|
@ -613,17 +768,22 @@ impl<C: Config> Entity<C> {
|
||||||
yaw: ByteAngle::from_degrees(self.yaw),
|
yaw: ByteAngle::from_degrees(self.yaw),
|
||||||
pitch: ByteAngle::from_degrees(self.pitch),
|
pitch: ByteAngle::from_degrees(self.pitch),
|
||||||
})),
|
})),
|
||||||
_ => Some(EntitySpawnPacket::Entity(SpawnEntity {
|
TrackedData::ItemFrame(e) => with_object_data(e.get_rotation()),
|
||||||
entity_id: VarInt(this_id.to_network_id()),
|
TrackedData::GlowItemFrame(e) => with_object_data(e.get_rotation()),
|
||||||
object_uuid: self.uuid,
|
TrackedData::Painting(_) => {
|
||||||
kind: VarInt(self.kind() as i32),
|
with_object_data(match ((self.yaw + 45.0).rem_euclid(360.0) / 90.0) as u8 {
|
||||||
position: self.new_position,
|
0 => 3,
|
||||||
pitch: ByteAngle::from_degrees(self.pitch),
|
1 => 4,
|
||||||
yaw: ByteAngle::from_degrees(self.yaw),
|
2 => 2,
|
||||||
head_yaw: ByteAngle::from_degrees(self.head_yaw),
|
_ => 5,
|
||||||
data: VarInt(1), // TODO
|
})
|
||||||
velocity: velocity_to_packet_units(self.velocity),
|
}
|
||||||
})),
|
TrackedData::FallingBlock(_) => with_object_data(1), // TODO: set block state ID.
|
||||||
|
TrackedData::FishingBobber(e) => with_object_data(e.get_hook_entity_id()),
|
||||||
|
TrackedData::Warden(e) => {
|
||||||
|
with_object_data((e.get_pose() == types::Pose::Emerging).into())
|
||||||
|
}
|
||||||
|
_ => with_object_data(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,7 +234,36 @@ impl Encode for FrogKind {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||||
pub enum PaintingKind {
|
pub enum PaintingKind {
|
||||||
#[default]
|
#[default]
|
||||||
Kebab, // TODO
|
Kebab,
|
||||||
|
Aztec,
|
||||||
|
Alban,
|
||||||
|
Aztec2,
|
||||||
|
Bomb,
|
||||||
|
Plant,
|
||||||
|
Wasteland,
|
||||||
|
Pool,
|
||||||
|
Courbet,
|
||||||
|
Sea,
|
||||||
|
Sunset,
|
||||||
|
Creebet,
|
||||||
|
Wanderer,
|
||||||
|
Graham,
|
||||||
|
Match,
|
||||||
|
Bust,
|
||||||
|
Stage,
|
||||||
|
Void,
|
||||||
|
SkullAndRoses,
|
||||||
|
Wither,
|
||||||
|
Fighters,
|
||||||
|
Pointer,
|
||||||
|
Pigscene,
|
||||||
|
BurningSkull,
|
||||||
|
Skeleton,
|
||||||
|
Earth,
|
||||||
|
Wind,
|
||||||
|
Water,
|
||||||
|
Fire,
|
||||||
|
DonkeyKong,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for PaintingKind {
|
impl Encode for PaintingKind {
|
||||||
|
|
Loading…
Reference in a new issue