Refactor World Entity System
Also support Item drops (WIP)
This commit is contained in:
parent
8bb47b6ec7
commit
ef497fb435
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -30,7 +30,46 @@ pub struct Metadata<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Metadata<T> {
|
impl<T> Metadata<T> {
|
||||||
pub fn new(index: u8, typ: VarInt, value: T) -> Self {
|
pub fn new(index: u8, typ: MetaDataType, value: T) -> Self {
|
||||||
Self { index, typ, value }
|
Self {
|
||||||
|
index,
|
||||||
|
typ: VarInt(typ as i32),
|
||||||
|
value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum MetaDataType {
|
||||||
|
Byte,
|
||||||
|
Integer,
|
||||||
|
Long,
|
||||||
|
Float,
|
||||||
|
String,
|
||||||
|
TextComponent,
|
||||||
|
OptionalTextComponent,
|
||||||
|
ItemStack,
|
||||||
|
Boolean,
|
||||||
|
Rotation,
|
||||||
|
BlockPos,
|
||||||
|
OptionalBlockPos,
|
||||||
|
Facing,
|
||||||
|
OptionalUuid,
|
||||||
|
BlockState,
|
||||||
|
OptionalBlockState,
|
||||||
|
NbtCompound,
|
||||||
|
Particle,
|
||||||
|
ParticleList,
|
||||||
|
VillagerData,
|
||||||
|
OptionalInt,
|
||||||
|
EntityPose,
|
||||||
|
CatVariant,
|
||||||
|
WolfVariant,
|
||||||
|
FrogVariant,
|
||||||
|
OptionalGlobalPos,
|
||||||
|
PaintingVariant,
|
||||||
|
SnifferState,
|
||||||
|
ArmadilloState,
|
||||||
|
Vector3f,
|
||||||
|
QuaternionF,
|
||||||
|
}
|
||||||
|
|||||||
@ -43,8 +43,7 @@ struct Particle {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
struct ParticleOptions {
|
struct ParticleOptions {
|
||||||
#[serde(rename = "type")]
|
r#type: String,
|
||||||
typee: String,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
value: Option<VarInt>,
|
value: Option<VarInt>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,7 @@ pub enum MonsterSpawnLightLevel {
|
|||||||
pub struct MonsterSpawnLightLevelTagged {
|
pub struct MonsterSpawnLightLevelTagged {
|
||||||
min_inclusive: i32,
|
min_inclusive: i32,
|
||||||
max_inclusive: i32,
|
max_inclusive: i32,
|
||||||
#[serde(rename = "type")]
|
r#type: String,
|
||||||
typee: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i32> for MonsterSpawnLightLevel {
|
impl From<i32> for MonsterSpawnLightLevel {
|
||||||
|
|||||||
@ -26,9 +26,8 @@ pub enum HoverEvent {
|
|||||||
/// The UUID cannot use uuid::Uuid because its serialization parses it into bytes, so its double bytes serialized
|
/// The UUID cannot use uuid::Uuid because its serialization parses it into bytes, so its double bytes serialized
|
||||||
id: Cow<'static, str>,
|
id: Cow<'static, str>,
|
||||||
/// Resource identifier of the entity
|
/// Resource identifier of the entity
|
||||||
#[serde(rename = "type")]
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
kind: Option<Cow<'static, str>>,
|
r#type: Option<Cow<'static, str>>,
|
||||||
/// Optional custom name for the entity
|
/// Optional custom name for the entity
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
name: Option<Vec<TextComponentBase>>,
|
name: Option<Vec<TextComponentBase>>,
|
||||||
@ -45,7 +44,7 @@ impl HoverEvent {
|
|||||||
{
|
{
|
||||||
Self::ShowEntity {
|
Self::ShowEntity {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
kind: kind.map(|kind| kind.into()),
|
r#type: kind.map(|kind| kind.into()),
|
||||||
name: match name {
|
name: match name {
|
||||||
Some(name) => Some(vec![name.0]),
|
Some(name) => Some(vec![name.0]),
|
||||||
None => None,
|
None => None,
|
||||||
|
|||||||
@ -3,6 +3,8 @@ use std::sync::LazyLock;
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::loot::LootTable;
|
||||||
|
|
||||||
pub static BLOCKS: LazyLock<TopLevel> = LazyLock::new(|| {
|
pub static BLOCKS: LazyLock<TopLevel> = LazyLock::new(|| {
|
||||||
serde_json::from_str(include_str!("../../../assets/blocks.json"))
|
serde_json::from_str(include_str!("../../../assets/blocks.json"))
|
||||||
.expect("Could not parse blocks.json registry.")
|
.expect("Could not parse blocks.json registry.")
|
||||||
@ -103,13 +105,13 @@ pub fn get_block_collision_shapes(block_id: u16) -> Option<Vec<Shape>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct TopLevel {
|
pub struct TopLevel {
|
||||||
block_entity_types: Vec<String>,
|
block_entity_types: Vec<String>,
|
||||||
shapes: Vec<Shape>,
|
shapes: Vec<Shape>,
|
||||||
pub blocks: Vec<Block>,
|
pub blocks: Vec<Block>,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub id: u16,
|
pub id: u16,
|
||||||
pub item_id: u16,
|
pub item_id: u16,
|
||||||
@ -117,6 +119,7 @@ pub struct Block {
|
|||||||
pub wall_variant_id: Option<u16>,
|
pub wall_variant_id: Option<u16>,
|
||||||
pub translation_key: String,
|
pub translation_key: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub loot_table: Option<LootTable>,
|
||||||
pub properties: Vec<Property>,
|
pub properties: Vec<Property>,
|
||||||
pub default_state_id: u16,
|
pub default_state_id: u16,
|
||||||
pub states: Vec<State>,
|
pub states: Vec<State>,
|
||||||
|
|||||||
@ -77,8 +77,7 @@ pub struct AttributeModifiers {
|
|||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct Modifier {
|
pub struct Modifier {
|
||||||
#[serde(rename = "type")]
|
pub r#type: String,
|
||||||
pub type_val: String,
|
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub amount: f64,
|
pub amount: f64,
|
||||||
pub operation: Operation,
|
pub operation: Operation,
|
||||||
|
|||||||
@ -18,6 +18,7 @@ mod generation;
|
|||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod level;
|
pub mod level;
|
||||||
mod lock;
|
mod lock;
|
||||||
|
pub mod loot;
|
||||||
pub mod world_info;
|
pub mod world_info;
|
||||||
pub const WORLD_HEIGHT: usize = 384;
|
pub const WORLD_HEIGHT: usize = 384;
|
||||||
pub const WORLD_LOWEST_Y: i16 = -64;
|
pub const WORLD_LOWEST_Y: i16 = -64;
|
||||||
|
|||||||
37
pumpkin-world/src/loot/mod.rs
Normal file
37
pumpkin-world/src/loot/mod.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct LootTable {
|
||||||
|
r#type: LootTableType,
|
||||||
|
pools: Option<Vec<LootPool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct LootPool {
|
||||||
|
entries: Vec<LootPoolEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct LootPoolEntry {
|
||||||
|
// TODO
|
||||||
|
r#type: Option<String>,
|
||||||
|
// TODO
|
||||||
|
name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
#[serde(rename = "snake_case")]
|
||||||
|
pub enum LootTableType {
|
||||||
|
#[serde(rename = "minecraft:empty")]
|
||||||
|
/// Nothing will be droped
|
||||||
|
Empty,
|
||||||
|
#[serde(rename = "minecraft:block")]
|
||||||
|
/// A Block will be droped
|
||||||
|
Block,
|
||||||
|
#[serde(rename = "minecraft:chest")]
|
||||||
|
/// A Item will be droped
|
||||||
|
Chest,
|
||||||
|
}
|
||||||
@ -112,7 +112,7 @@ pub async fn standard_open_container_unique<C: Container + Default + 'static>(
|
|||||||
|
|
||||||
pub async fn close_all_in_container(player: &Player, container: &OpenContainer) {
|
pub async fn close_all_in_container(player: &Player, container: &OpenContainer) {
|
||||||
for id in container.all_player_ids() {
|
for id in container.all_player_ids() {
|
||||||
if let Some(remote_player) = player.world().get_player_by_entityid(id).await {
|
if let Some(remote_player) = player.world().get_player_by_id(id).await {
|
||||||
remote_player.close_container().await;
|
remote_player.close_container().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,10 +38,7 @@ impl CommandExecutor for SummonExecutor {
|
|||||||
if let Some(player) = sender.as_player() {
|
if let Some(player) = sender.as_player() {
|
||||||
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
let pos = pos.unwrap_or(player.living_entity.entity.pos.load());
|
||||||
let (mob, uuid) = mob::from_type(entity, server, pos, player.world()).await;
|
let (mob, uuid) = mob::from_type(entity, server, pos, player.world()).await;
|
||||||
player
|
player.world().spawn_entity(uuid, mob).await;
|
||||||
.world()
|
|
||||||
.broadcast_packet_all(&mob.living_entity.entity.create_spawn_packet(uuid))
|
|
||||||
.await;
|
|
||||||
sender
|
sender
|
||||||
.send_message(TextComponent::translate(
|
.send_message(TextComponent::translate(
|
||||||
"commands.summon.success",
|
"commands.summon.success",
|
||||||
|
|||||||
38
pumpkin/src/entity/item.rs
Normal file
38
pumpkin/src/entity/item.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_protocol::{
|
||||||
|
client::play::{MetaDataType, Metadata},
|
||||||
|
codec::slot::Slot,
|
||||||
|
};
|
||||||
|
use pumpkin_world::item::ItemStack;
|
||||||
|
|
||||||
|
use super::{living::LivingEntity, Entity, EntityBase};
|
||||||
|
|
||||||
|
pub struct ItemEntity {
|
||||||
|
entity: Entity,
|
||||||
|
item: Slot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemEntity {
|
||||||
|
pub fn new(entity: Entity, stack: &ItemStack) -> Self {
|
||||||
|
let slot = Slot::from(stack);
|
||||||
|
Self { entity, item: slot }
|
||||||
|
}
|
||||||
|
pub async fn send_meta_packet(&self) {
|
||||||
|
self.entity
|
||||||
|
.send_meta_data(Metadata::new(8, MetaDataType::ItemStack, self.item.clone()))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EntityBase for ItemEntity {
|
||||||
|
async fn tick(&self) {}
|
||||||
|
|
||||||
|
fn get_entity(&self) -> &Entity {
|
||||||
|
&self.entity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ use async_trait::async_trait;
|
|||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use pumpkin_data::sound::Sound;
|
use pumpkin_data::sound::Sound;
|
||||||
use pumpkin_nbt::tag::NbtTag;
|
use pumpkin_nbt::tag::NbtTag;
|
||||||
use pumpkin_protocol::client::play::{CDamageEvent, CEntityStatus, CSetEntityMetadata, Metadata};
|
use pumpkin_protocol::client::play::{CDamageEvent, CEntityStatus, MetaDataType, Metadata};
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
|
||||||
use super::{Entity, EntityId, NBTStorage};
|
use super::{Entity, EntityId, NBTStorage};
|
||||||
@ -58,11 +58,7 @@ impl LivingEntity {
|
|||||||
self.health.store(health);
|
self.health.store(health);
|
||||||
// tell everyone entities health changed
|
// tell everyone entities health changed
|
||||||
self.entity
|
self.entity
|
||||||
.world
|
.send_meta_data(Metadata::new(9, MetaDataType::Float, health))
|
||||||
.broadcast_packet_all(&CSetEntityMetadata::new(
|
|
||||||
self.entity.entity_id.into(),
|
|
||||||
Metadata::new(9, 3.into(), health),
|
|
||||||
))
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@ -11,18 +12,20 @@ use crate::{server::Server, world::World};
|
|||||||
use super::{
|
use super::{
|
||||||
ai::{goal::Goal, path::Navigator},
|
ai::{goal::Goal, path::Navigator},
|
||||||
living::LivingEntity,
|
living::LivingEntity,
|
||||||
|
Entity, EntityBase,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod zombie;
|
pub mod zombie;
|
||||||
|
|
||||||
pub struct MobEntity {
|
pub struct MobEntity {
|
||||||
pub living_entity: Arc<LivingEntity>,
|
pub living_entity: LivingEntity,
|
||||||
pub goals: Mutex<Vec<(Arc<dyn Goal>, bool)>>,
|
pub goals: Mutex<Vec<(Arc<dyn Goal>, bool)>>,
|
||||||
pub navigator: Mutex<Navigator>,
|
pub navigator: Mutex<Navigator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MobEntity {
|
#[async_trait]
|
||||||
pub async fn tick(&self) {
|
impl EntityBase for MobEntity {
|
||||||
|
async fn tick(&self) {
|
||||||
let mut goals = self.goals.lock().await;
|
let mut goals = self.goals.lock().await;
|
||||||
for (goal, running) in goals.iter_mut() {
|
for (goal, running) in goals.iter_mut() {
|
||||||
if *running {
|
if *running {
|
||||||
@ -38,6 +41,14 @@ impl MobEntity {
|
|||||||
let mut navigator = self.navigator.lock().await;
|
let mut navigator = self.navigator.lock().await;
|
||||||
navigator.tick(&self.living_entity).await;
|
navigator.tick(&self.living_entity).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_entity(&self) -> &Entity {
|
||||||
|
&self.living_entity.entity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
|
Some(&self.living_entity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_type(
|
pub async fn from_type(
|
||||||
@ -45,15 +56,20 @@ pub async fn from_type(
|
|||||||
server: &Server,
|
server: &Server,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
world: &Arc<World>,
|
world: &Arc<World>,
|
||||||
) -> (Arc<MobEntity>, Uuid) {
|
) -> (Arc<dyn EntityBase>, Uuid) {
|
||||||
let entity = server.add_mob_entity(entity_type, position, world).await;
|
let (entity, uuid) = server.add_entity(position, entity_type, world);
|
||||||
|
let mob = MobEntity {
|
||||||
|
living_entity: LivingEntity::new(entity),
|
||||||
|
goals: Mutex::new(vec![]),
|
||||||
|
navigator: Mutex::new(Navigator::default()),
|
||||||
|
};
|
||||||
#[expect(clippy::single_match)]
|
#[expect(clippy::single_match)]
|
||||||
match entity_type {
|
match entity_type {
|
||||||
EntityType::Zombie => Zombie::make(&entity.0).await,
|
EntityType::Zombie => Zombie::make(&mob).await,
|
||||||
// TODO
|
// TODO
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
entity
|
(Arc::new(mob), uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MobEntity {
|
impl MobEntity {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::sync::{atomic::AtomicBool, Arc};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
|
use living::LivingEntity;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
entity::{EntityPose, EntityType},
|
entity::{EntityPose, EntityType},
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
@ -10,7 +11,8 @@ use pumpkin_data::{
|
|||||||
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{
|
client::play::{
|
||||||
CHeadRot, CSetEntityMetadata, CSpawnEntity, CTeleportEntity, CUpdateEntityRot, Metadata,
|
CHeadRot, CSetEntityMetadata, CSpawnEntity, CTeleportEntity, CUpdateEntityRot,
|
||||||
|
MetaDataType, Metadata,
|
||||||
},
|
},
|
||||||
codec::var_int::VarInt,
|
codec::var_int::VarInt,
|
||||||
};
|
};
|
||||||
@ -22,18 +24,26 @@ use pumpkin_util::math::{
|
|||||||
vector3::Vector3,
|
vector3::Vector3,
|
||||||
wrap_degrees,
|
wrap_degrees,
|
||||||
};
|
};
|
||||||
|
use serde::Serialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
pub mod ai;
|
pub mod ai;
|
||||||
pub mod mob;
|
pub mod item;
|
||||||
|
|
||||||
pub mod living;
|
pub mod living;
|
||||||
|
pub mod mob;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
|
|
||||||
pub type EntityId = i32;
|
pub type EntityId = i32;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EntityBase: Send + Sync {
|
||||||
|
async fn tick(&self);
|
||||||
|
fn get_entity(&self) -> &Entity;
|
||||||
|
fn get_living_entity(&self) -> Option<&LivingEntity>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a not living Entity (e.g. Item, Egg, Snowball...)
|
/// Represents a not living Entity (e.g. Item, Egg, Snowball...)
|
||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
/// A unique identifier for the entity
|
/// A unique identifier for the entity
|
||||||
@ -295,8 +305,8 @@ impl Entity {
|
|||||||
} else {
|
} else {
|
||||||
b &= !(1 << index);
|
b &= !(1 << index);
|
||||||
}
|
}
|
||||||
let packet = CSetEntityMetadata::new(self.entity_id.into(), Metadata::new(0, 0.into(), b));
|
self.send_meta_data(Metadata::new(0, MetaDataType::Byte, b))
|
||||||
self.world.broadcast_packet_all(&packet).await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Plays sound at this entity's position with the entity's sound category
|
/// Plays sound at this entity's position with the entity's sound category
|
||||||
@ -306,14 +316,33 @@ impl Entity {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn send_meta_data<T>(&self, meta: Metadata<T>)
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
self.world
|
||||||
|
.broadcast_packet_all(&CSetEntityMetadata::new(self.entity_id.into(), meta))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_pose(&self, pose: EntityPose) {
|
pub async fn set_pose(&self, pose: EntityPose) {
|
||||||
self.pose.store(pose);
|
self.pose.store(pose);
|
||||||
let pose = pose as i32;
|
let pose = pose as i32;
|
||||||
let packet = CSetEntityMetadata::<VarInt>::new(
|
self.send_meta_data(Metadata::new(6, MetaDataType::EntityPose, pose))
|
||||||
self.entity_id.into(),
|
.await;
|
||||||
Metadata::new(6, 21.into(), pose.into()),
|
}
|
||||||
);
|
}
|
||||||
self.world.broadcast_packet_all(&packet).await;
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EntityBase for Entity {
|
||||||
|
async fn tick(&self) {}
|
||||||
|
|
||||||
|
fn get_entity(&self) -> &Entity {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,8 @@ use pumpkin_protocol::{
|
|||||||
client::play::{
|
client::play::{
|
||||||
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation,
|
||||||
CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
CKeepAlive, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition,
|
||||||
CSetHealth, CSubtitle, CSystemChatMessage, CTitleText, GameEvent, PlayerAction,
|
CSetHealth, CSubtitle, CSystemChatMessage, CTitleText, GameEvent, MetaDataType,
|
||||||
|
PlayerAction,
|
||||||
},
|
},
|
||||||
server::play::{
|
server::play::{
|
||||||
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
|
||||||
@ -40,7 +41,7 @@ use pumpkin_protocol::{
|
|||||||
};
|
};
|
||||||
use pumpkin_protocol::{client::play::CUpdateTime, codec::var_int::VarInt};
|
use pumpkin_protocol::{client::play::CUpdateTime, codec::var_int::VarInt};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{CSetEntityMetadata, Metadata},
|
client::play::Metadata,
|
||||||
server::play::{SClickContainer, SKeepAlive},
|
server::play::{SClickContainer, SKeepAlive},
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
@ -63,7 +64,7 @@ use pumpkin_world::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::{Mutex, Notify, RwLock};
|
use tokio::sync::{Mutex, Notify, RwLock};
|
||||||
|
|
||||||
use super::{Entity, EntityId, NBTStorage};
|
use super::{item::ItemEntity, Entity, EntityId, NBTStorage};
|
||||||
use crate::{
|
use crate::{
|
||||||
command::{client_cmd_suggestions, dispatcher::CommandDispatcher},
|
command::{client_cmd_suggestions, dispatcher::CommandDispatcher},
|
||||||
data::op_data::OPERATOR_CONFIG,
|
data::op_data::OPERATOR_CONFIG,
|
||||||
@ -535,10 +536,10 @@ impl Player {
|
|||||||
/// Sends the mobs to just the player.
|
/// Sends the mobs to just the player.
|
||||||
// TODO: This should be optimized for larger servers based on current player chunk
|
// TODO: This should be optimized for larger servers based on current player chunk
|
||||||
pub async fn send_mobs(&self, world: &World) {
|
pub async fn send_mobs(&self, world: &World) {
|
||||||
let mobs = world.current_living_mobs.lock().await.clone();
|
let entities = world.entities.lock().await.clone();
|
||||||
for (uuid, mob) in mobs {
|
for (uuid, entity) in entities {
|
||||||
self.client
|
self.client
|
||||||
.send_packet(&mob.living_entity.entity.create_spawn_packet(uuid))
|
.send_packet(&entity.get_entity().create_spawn_packet(uuid))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,19 +682,18 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Send skin layers and used hand to all players
|
/// Send skin layers and used hand to all players
|
||||||
pub async fn update_client_information(&self) {
|
pub async fn send_client_information(&self) {
|
||||||
let config = self.config.lock().await;
|
let config = self.config.lock().await;
|
||||||
let world = self.world();
|
self.living_entity
|
||||||
world
|
.entity
|
||||||
.broadcast_packet_all(&CSetEntityMetadata::new(
|
.send_meta_data(Metadata::new(17, MetaDataType::Byte, config.skin_parts))
|
||||||
self.entity_id().into(),
|
|
||||||
Metadata::new(17, 0.into(), config.skin_parts),
|
|
||||||
))
|
|
||||||
.await;
|
.await;
|
||||||
world
|
self.living_entity
|
||||||
.broadcast_packet_all(&CSetEntityMetadata::new(
|
.entity
|
||||||
self.entity_id().into(),
|
.send_meta_data(Metadata::new(
|
||||||
Metadata::new(18, 0.into(), config.main_hand as u8),
|
18,
|
||||||
|
MetaDataType::Byte,
|
||||||
|
config.main_hand as u8,
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -715,6 +715,20 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn drop_item(&self, server: &Server) {
|
||||||
|
let inv = self.inventory.lock().await;
|
||||||
|
if let Some(item) = inv.held_item() {
|
||||||
|
let (entity, uuid) = server.add_entity(
|
||||||
|
self.living_entity.entity.pos.load(),
|
||||||
|
EntityType::Item,
|
||||||
|
self.world(),
|
||||||
|
);
|
||||||
|
let item_entity = Arc::new(ItemEntity::new(entity, item));
|
||||||
|
self.world().spawn_entity(uuid, item_entity.clone()).await;
|
||||||
|
item_entity.send_meta_packet().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_system_message(&self, text: &TextComponent) {
|
pub async fn send_system_message(&self, text: &TextComponent) {
|
||||||
self.send_system_message_raw(text, false).await;
|
self.send_system_message_raw(text, false).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -490,7 +490,7 @@ impl Player {
|
|||||||
.living_entity
|
.living_entity
|
||||||
.entity
|
.entity
|
||||||
.world
|
.world
|
||||||
.current_players
|
.players
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
world::player_chunker,
|
world::player_chunker,
|
||||||
};
|
};
|
||||||
use pumpkin_config::ADVANCED_CONFIG;
|
use pumpkin_config::ADVANCED_CONFIG;
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::{EntityPose, EntityType};
|
||||||
use pumpkin_data::world::CHAT;
|
use pumpkin_data::world::CHAT;
|
||||||
use pumpkin_inventory::player::PlayerInventory;
|
use pumpkin_inventory::player::PlayerInventory;
|
||||||
use pumpkin_inventory::InventoryError;
|
use pumpkin_inventory::InventoryError;
|
||||||
@ -660,7 +660,7 @@ impl Player {
|
|||||||
self.gameprofile.name,
|
self.gameprofile.name,
|
||||||
self.client.id,
|
self.client.id,
|
||||||
);
|
);
|
||||||
self.update_client_information().await;
|
self.send_client_information().await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.kick(TextComponent::text("Invalid hand or chat type"))
|
self.kick(TextComponent::text("Invalid hand or chat type"))
|
||||||
@ -719,8 +719,8 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let world = &entity.world;
|
let world = &entity.world;
|
||||||
let player_victim = world.get_player_by_entityid(entity_id.0).await;
|
let player_victim = world.get_player_by_id(entity_id.0).await;
|
||||||
let entity_victim = world.get_living_entity_by_entityid(entity_id.0).await;
|
let entity_victim = world.get_entity_by_id(entity_id.0).await;
|
||||||
if let Some(player_victim) = player_victim {
|
if let Some(player_victim) = player_victim {
|
||||||
if player_victim.living_entity.health.load() <= 0.0 {
|
if player_victim.living_entity.health.load() <= 0.0 {
|
||||||
// you can trigger this from a non-modded / innocent client client,
|
// you can trigger this from a non-modded / innocent client client,
|
||||||
@ -729,11 +729,15 @@ impl Player {
|
|||||||
}
|
}
|
||||||
self.attack(&player_victim).await;
|
self.attack(&player_victim).await;
|
||||||
} else if let Some(entity_victim) = entity_victim {
|
} else if let Some(entity_victim) = entity_victim {
|
||||||
|
// Checks if victim is a living entity
|
||||||
|
if let Some(entity_victim) = entity_victim.get_living_entity() {
|
||||||
if entity_victim.health.load() <= 0.0 {
|
if entity_victim.health.load() <= 0.0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
entity_victim.entity.set_pose(EntityPose::Dying).await;
|
||||||
entity_victim.kill().await;
|
entity_victim.kill().await;
|
||||||
world.clone().remove_mob_entity(entity_victim).await;
|
world.clone().remove_entity(&entity_victim.entity).await;
|
||||||
|
}
|
||||||
// TODO: block entities should be checked here (signs)
|
// TODO: block entities should be checked here (signs)
|
||||||
} else {
|
} else {
|
||||||
log::error!(
|
log::error!(
|
||||||
@ -836,10 +840,10 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Status::DropItemStack
|
Status::DropItemStack | Status::DropItem => {
|
||||||
| Status::DropItem
|
self.drop_item(server).await;
|
||||||
| Status::ShootArrowOrFinishEating
|
}
|
||||||
| Status::SwapItem => {
|
Status::ShootArrowOrFinishEating | Status::SwapItem => {
|
||||||
log::debug!("todo");
|
log::debug!("todo");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1130,12 +1134,10 @@ impl Player {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// set the rotation
|
// set the rotation
|
||||||
mob.living_entity.entity.set_rotation(yaw, 0.0);
|
mob.get_entity().set_rotation(yaw, 0.0);
|
||||||
|
|
||||||
// broadcast new mob to all players
|
// broadcast new mob to all players
|
||||||
world
|
world.spawn_entity(uuid, mob).await;
|
||||||
.broadcast_packet_all(&mob.living_entity.entity.create_spawn_packet(uuid))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// TODO: send/configure additional commands/data based on type of entity (horse, slime, etc)
|
// TODO: send/configure additional commands/data based on type of entity (horse, slime, etc)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -115,7 +115,7 @@ async fn handle_packet(
|
|||||||
let mut players: Vec<CString> = Vec::new();
|
let mut players: Vec<CString> = Vec::new();
|
||||||
for world in server.worlds.read().await.iter() {
|
for world in server.worlds.read().await.iter() {
|
||||||
let mut world_players = world
|
let mut world_players = world
|
||||||
.current_players
|
.players
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
// Although there is no documented limit, we will limit to 4 players
|
// Although there is no documented limit, we will limit to 4 players
|
||||||
|
|||||||
@ -55,7 +55,7 @@ impl Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for world in self.server.worlds.read().await.iter() {
|
for world in self.server.worlds.read().await.iter() {
|
||||||
for player in world.current_players.lock().await.values() {
|
for player in world.players.lock().await.values() {
|
||||||
client_cmd_suggestions::send_c_commands_packet(
|
client_cmd_suggestions::send_c_commands_packet(
|
||||||
player,
|
player,
|
||||||
&self.server.command_dispatcher,
|
&self.server.command_dispatcher,
|
||||||
@ -72,7 +72,7 @@ impl Context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for world in self.server.worlds.read().await.iter() {
|
for world in self.server.worlds.read().await.iter() {
|
||||||
for player in world.current_players.lock().await.values() {
|
for player in world.players.lock().await.values() {
|
||||||
client_cmd_suggestions::send_c_commands_packet(
|
client_cmd_suggestions::send_c_commands_packet(
|
||||||
player,
|
player,
|
||||||
&self.server.command_dispatcher,
|
&self.server.command_dispatcher,
|
||||||
|
|||||||
@ -34,9 +34,6 @@ use uuid::Uuid;
|
|||||||
use crate::block::block_manager::BlockManager;
|
use crate::block::block_manager::BlockManager;
|
||||||
use crate::block::properties::BlockPropertiesManager;
|
use crate::block::properties::BlockPropertiesManager;
|
||||||
use crate::block::{default_block_manager, default_block_properties_manager};
|
use crate::block::{default_block_manager, default_block_properties_manager};
|
||||||
use crate::entity::ai::path::Navigator;
|
|
||||||
use crate::entity::living::LivingEntity;
|
|
||||||
use crate::entity::mob::MobEntity;
|
|
||||||
use crate::entity::{Entity, EntityId};
|
use crate::entity::{Entity, EntityId};
|
||||||
use crate::net::EncryptionError;
|
use crate::net::EncryptionError;
|
||||||
use crate::world::custom_bossbar::CustomBossbars;
|
use crate::world::custom_bossbar::CustomBossbars;
|
||||||
@ -207,22 +204,6 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_mob_entity(
|
|
||||||
&self,
|
|
||||||
entity_type: EntityType,
|
|
||||||
position: Vector3<f64>,
|
|
||||||
world: &Arc<World>,
|
|
||||||
) -> (Arc<MobEntity>, Uuid) {
|
|
||||||
let (living_entity, uuid) = self.add_living_entity(position, entity_type, world);
|
|
||||||
|
|
||||||
let mob = Arc::new(MobEntity {
|
|
||||||
living_entity,
|
|
||||||
goals: Mutex::new(vec![]),
|
|
||||||
navigator: Mutex::new(Navigator::default()),
|
|
||||||
});
|
|
||||||
world.add_mob_entity(uuid, mob.clone()).await;
|
|
||||||
(mob, uuid)
|
|
||||||
}
|
|
||||||
/// Adds a new living entity to the server. This does not Spawn the entity
|
/// Adds a new living entity to the server. This does not Spawn the entity
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@ -232,12 +213,12 @@ impl Server {
|
|||||||
/// - `Arc<LivingEntity>`: A reference to the newly created living entity.
|
/// - `Arc<LivingEntity>`: A reference to the newly created living entity.
|
||||||
/// - `Arc<World>`: A reference to the world that the living entity was added to.
|
/// - `Arc<World>`: A reference to the world that the living entity was added to.
|
||||||
/// - `Uuid`: The uuid of the newly created living entity to be used to send to the client.
|
/// - `Uuid`: The uuid of the newly created living entity to be used to send to the client.
|
||||||
fn add_living_entity(
|
pub fn add_entity(
|
||||||
&self,
|
&self,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
entity_type: EntityType,
|
entity_type: EntityType,
|
||||||
world: &Arc<World>,
|
world: &Arc<World>,
|
||||||
) -> (Arc<LivingEntity>, Uuid) {
|
) -> (Entity, Uuid) {
|
||||||
let entity_id = self.new_entity_id();
|
let entity_id = self.new_entity_id();
|
||||||
|
|
||||||
// TODO: this should be resolved to a integer using a macro when calling this function
|
// TODO: this should be resolved to a integer using a macro when calling this function
|
||||||
@ -254,7 +235,7 @@ impl Server {
|
|||||||
|
|
||||||
// TODO: standing eye height should be per mob
|
// TODO: standing eye height should be per mob
|
||||||
let new_uuid = uuid::Uuid::new_v4();
|
let new_uuid = uuid::Uuid::new_v4();
|
||||||
let mob = Arc::new(LivingEntity::new(Entity::new(
|
let entity = Entity::new(
|
||||||
entity_id,
|
entity_id,
|
||||||
new_uuid,
|
new_uuid,
|
||||||
world.clone(),
|
world.clone(),
|
||||||
@ -263,9 +244,9 @@ impl Server {
|
|||||||
1.62,
|
1.62,
|
||||||
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
AtomicCell::new(BoundingBox::new_default(&bounding_box_size)),
|
||||||
AtomicCell::new(bounding_box_size),
|
AtomicCell::new(bounding_box_size),
|
||||||
)));
|
);
|
||||||
|
|
||||||
(mob, new_uuid)
|
(entity, new_uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn try_get_container(
|
pub async fn try_get_container(
|
||||||
@ -380,7 +361,7 @@ impl Server {
|
|||||||
let mut players = Vec::<Arc<Player>>::new();
|
let mut players = Vec::<Arc<Player>>::new();
|
||||||
|
|
||||||
for world in self.worlds.read().await.iter() {
|
for world in self.worlds.read().await.iter() {
|
||||||
for (_, player) in world.current_players.lock().await.iter() {
|
for (_, player) in world.players.lock().await.iter() {
|
||||||
if player.client.address.lock().await.ip() == ip {
|
if player.client.address.lock().await.ip() == ip {
|
||||||
players.push(player.clone());
|
players.push(player.clone());
|
||||||
}
|
}
|
||||||
@ -395,7 +376,7 @@ impl Server {
|
|||||||
let mut players = Vec::<Arc<Player>>::new();
|
let mut players = Vec::<Arc<Player>>::new();
|
||||||
|
|
||||||
for world in self.worlds.read().await.iter() {
|
for world in self.worlds.read().await.iter() {
|
||||||
for (_, player) in world.current_players.lock().await.iter() {
|
for (_, player) in world.players.lock().await.iter() {
|
||||||
players.push(player.clone());
|
players.push(player.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -441,7 +422,7 @@ impl Server {
|
|||||||
pub async fn get_player_count(&self) -> usize {
|
pub async fn get_player_count(&self) -> usize {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for world in self.worlds.read().await.iter() {
|
for world in self.worlds.read().await.iter() {
|
||||||
count += world.current_players.lock().await.len();
|
count += world.players.lock().await.len();
|
||||||
}
|
}
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
@ -450,7 +431,7 @@ impl Server {
|
|||||||
pub async fn has_n_players(&self, n: usize) -> bool {
|
pub async fn has_n_players(&self, n: usize) -> bool {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
for world in self.worlds.read().await.iter() {
|
for world in self.worlds.read().await.iter() {
|
||||||
count += world.current_players.lock().await.len();
|
count += world.players.lock().await.len();
|
||||||
if count >= n {
|
if count >= n {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ pub mod player_chunker;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command::client_cmd_suggestions,
|
command::client_cmd_suggestions,
|
||||||
entity::{living::LivingEntity, mob::MobEntity, player::Player, Entity, EntityId},
|
entity::{player::Player, Entity, EntityBase, EntityId},
|
||||||
error::PumpkinError,
|
error::PumpkinError,
|
||||||
plugin::{
|
plugin::{
|
||||||
block::BlockBreakEvent,
|
block::BlockBreakEvent,
|
||||||
@ -17,19 +17,16 @@ use crate::{
|
|||||||
use level_time::LevelTime;
|
use level_time::LevelTime;
|
||||||
use pumpkin_config::BasicConfiguration;
|
use pumpkin_config::BasicConfiguration;
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
entity::{EntityPose, EntityType},
|
entity::EntityType,
|
||||||
sound::{Sound, SoundCategory},
|
sound::{Sound, SoundCategory},
|
||||||
world::WorldEvent,
|
world::WorldEvent,
|
||||||
};
|
};
|
||||||
use pumpkin_protocol::client::play::{CBlockUpdate, CDisguisedChatMessage, CRespawn, CWorldEvent};
|
use pumpkin_protocol::client::play::{CBlockUpdate, CDisguisedChatMessage, CRespawn, CWorldEvent};
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{client::play::CLevelEvent, codec::identifier::Identifier};
|
||||||
client::play::CLevelEvent,
|
|
||||||
codec::{identifier::Identifier, var_int::VarInt},
|
|
||||||
};
|
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::play::{
|
client::play::{
|
||||||
CChunkData, CGameEvent, CLogin, CPlayerInfoUpdate, CRemoveEntities, CRemovePlayerInfo,
|
CChunkData, CGameEvent, CLogin, CPlayerInfoUpdate, CRemoveEntities, CRemovePlayerInfo,
|
||||||
CSetEntityMetadata, CSpawnEntity, GameEvent, Metadata, PlayerAction,
|
CSpawnEntity, GameEvent, PlayerAction,
|
||||||
},
|
},
|
||||||
ClientPacket,
|
ClientPacket,
|
||||||
};
|
};
|
||||||
@ -99,9 +96,10 @@ pub struct World {
|
|||||||
/// The underlying level, responsible for chunk management and terrain generation.
|
/// The underlying level, responsible for chunk management and terrain generation.
|
||||||
pub level: Arc<Level>,
|
pub level: Arc<Level>,
|
||||||
/// A map of active players within the world, keyed by their unique UUID.
|
/// A map of active players within the world, keyed by their unique UUID.
|
||||||
pub current_players: Arc<Mutex<HashMap<uuid::Uuid, Arc<Player>>>>,
|
pub players: Arc<Mutex<HashMap<uuid::Uuid, Arc<Player>>>>,
|
||||||
/// A map of active mob entities within the world, keyed by their unique UUID.
|
/// A map of active entities within the world, keyed by their unique UUID.
|
||||||
pub current_living_mobs: Arc<Mutex<HashMap<uuid::Uuid, Arc<MobEntity>>>>,
|
/// This does not include Players
|
||||||
|
pub entities: Arc<Mutex<HashMap<uuid::Uuid, Arc<dyn EntityBase>>>>,
|
||||||
/// The world's scoreboard, used for tracking scores, objectives, and display information.
|
/// The world's scoreboard, used for tracking scores, objectives, and display information.
|
||||||
pub scoreboard: Mutex<Scoreboard>,
|
pub scoreboard: Mutex<Scoreboard>,
|
||||||
/// The world's worldborder, defining the playable area and controlling its expansion or contraction.
|
/// The world's worldborder, defining the playable area and controlling its expansion or contraction.
|
||||||
@ -118,8 +116,8 @@ impl World {
|
|||||||
pub fn load(level: Level, dimension_type: DimensionType) -> Self {
|
pub fn load(level: Level, dimension_type: DimensionType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
level: Arc::new(level),
|
level: Arc::new(level),
|
||||||
current_players: Arc::new(Mutex::new(HashMap::new())),
|
players: Arc::new(Mutex::new(HashMap::new())),
|
||||||
current_living_mobs: Arc::new(Mutex::new(HashMap::new())),
|
entities: Arc::new(Mutex::new(HashMap::new())),
|
||||||
scoreboard: Mutex::new(Scoreboard::new()),
|
scoreboard: Mutex::new(Scoreboard::new()),
|
||||||
worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 29_999_984.0, 0, 0, 0)),
|
worldborder: Mutex::new(Worldborder::new(0.0, 0.0, 29_999_984.0, 0, 0, 0)),
|
||||||
level_time: Mutex::new(LevelTime::new()),
|
level_time: Mutex::new(LevelTime::new()),
|
||||||
@ -140,7 +138,7 @@ impl World {
|
|||||||
where
|
where
|
||||||
P: ClientPacket,
|
P: ClientPacket,
|
||||||
{
|
{
|
||||||
let current_players = self.current_players.lock().await;
|
let current_players = self.players.lock().await;
|
||||||
for player in current_players.values() {
|
for player in current_players.values() {
|
||||||
player.client.send_packet(packet).await;
|
player.client.send_packet(packet).await;
|
||||||
}
|
}
|
||||||
@ -171,7 +169,7 @@ impl World {
|
|||||||
where
|
where
|
||||||
P: ClientPacket,
|
P: ClientPacket,
|
||||||
{
|
{
|
||||||
let current_players = self.current_players.lock().await;
|
let current_players = self.players.lock().await;
|
||||||
for (_, player) in current_players.iter().filter(|c| !except.contains(c.0)) {
|
for (_, player) in current_players.iter().filter(|c| !except.contains(c.0)) {
|
||||||
player.client.send_packet(packet).await;
|
player.client.send_packet(packet).await;
|
||||||
}
|
}
|
||||||
@ -191,7 +189,7 @@ impl World {
|
|||||||
pitch: f32,
|
pitch: f32,
|
||||||
) {
|
) {
|
||||||
let seed = thread_rng().gen::<f64>();
|
let seed = thread_rng().gen::<f64>();
|
||||||
let players = self.current_players.lock().await;
|
let players = self.players.lock().await;
|
||||||
for (_, player) in players.iter() {
|
for (_, player) in players.iter() {
|
||||||
player
|
player
|
||||||
.play_sound(sound_id, category, position, volume, pitch, seed)
|
.play_sound(sound_id, category, position, volume, pitch, seed)
|
||||||
@ -233,11 +231,11 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// player ticks
|
// player ticks
|
||||||
for player in self.current_players.lock().await.values() {
|
for player in self.players.lock().await.values() {
|
||||||
player.tick().await;
|
player.tick().await;
|
||||||
}
|
}
|
||||||
// entities tick
|
// entities tick
|
||||||
for entity in self.current_living_mobs.lock().await.values() {
|
for entity in self.entities.lock().await.values() {
|
||||||
entity.tick().await;
|
entity.tick().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -339,12 +337,12 @@ impl World {
|
|||||||
}],
|
}],
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
player.update_client_information().await;
|
player.send_client_information().await;
|
||||||
|
|
||||||
// here we send all the infos of already joined players
|
// here we send all the infos of already joined players
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
{
|
{
|
||||||
let current_players = self.current_players.lock().await;
|
let current_players = self.players.lock().await;
|
||||||
for (_, playerr) in current_players
|
for (_, playerr) in current_players
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(c, _)| **c != player.gameprofile.id)
|
.filter(|(c, _)| **c != player.gameprofile.id)
|
||||||
@ -394,13 +392,7 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
// spawn players for our client
|
// spawn players for our client
|
||||||
let id = player.gameprofile.id;
|
let id = player.gameprofile.id;
|
||||||
for (_, existing_player) in self
|
for (_, existing_player) in self.players.lock().await.iter().filter(|c| c.0 != &id) {
|
||||||
.current_players
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.iter()
|
|
||||||
.filter(|c| c.0 != &id)
|
|
||||||
{
|
|
||||||
let entity = &existing_player.living_entity.entity;
|
let entity = &existing_player.living_entity.entity;
|
||||||
let pos = entity.pos.load();
|
let pos = entity.pos.load();
|
||||||
let gameprofile = &existing_player.gameprofile;
|
let gameprofile = &existing_player.gameprofile;
|
||||||
@ -426,14 +418,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
// entity meta data
|
// entity meta data
|
||||||
// set skin parts
|
// set skin parts
|
||||||
if let Some(config) = player.client.config.lock().await.as_ref() {
|
player.send_client_information().await;
|
||||||
let packet = CSetEntityMetadata::new(
|
|
||||||
entity_id.into(),
|
|
||||||
Metadata::new(17, VarInt(0), config.skin_parts),
|
|
||||||
);
|
|
||||||
log::debug!("Broadcasting skin for {}", player.gameprofile.name);
|
|
||||||
self.broadcast_packet_all(&packet).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start waiting for level chunks, Sets the "Loading Terrain" screen
|
// Start waiting for level chunks, Sets the "Loading Terrain" screen
|
||||||
log::debug!("Sending waiting chunks to {}", player.gameprofile.name);
|
log::debug!("Sending waiting chunks to {}", player.gameprofile.name);
|
||||||
@ -530,11 +515,6 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let entity = &player.living_entity.entity;
|
let entity = &player.living_entity.entity;
|
||||||
let entity_id = entity.entity_id;
|
|
||||||
|
|
||||||
let skin_parts = player.config.lock().await.skin_parts;
|
|
||||||
let entity_metadata_packet =
|
|
||||||
CSetEntityMetadata::new(entity_id.into(), Metadata::new(17, VarInt(0), &skin_parts));
|
|
||||||
|
|
||||||
self.broadcast_packet_except(
|
self.broadcast_packet_except(
|
||||||
&[player.gameprofile.id],
|
&[player.gameprofile.id],
|
||||||
@ -556,9 +536,9 @@ impl World {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
player.send_client_information().await;
|
||||||
|
|
||||||
player_chunker::player_join(player).await;
|
player_chunker::player_join(player).await;
|
||||||
self.broadcast_packet_all(&entity_metadata_packet).await;
|
|
||||||
// update commands
|
// update commands
|
||||||
|
|
||||||
player.set_health(20.0, 20, 20.0).await;
|
player.set_health(20.0, 20, 20.0).await;
|
||||||
@ -634,8 +614,8 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a Player by entity id
|
/// Gets a Player by entity id
|
||||||
pub async fn get_player_by_entityid(&self, id: EntityId) -> Option<Arc<Player>> {
|
pub async fn get_player_by_id(&self, id: EntityId) -> Option<Arc<Player>> {
|
||||||
for player in self.current_players.lock().await.values() {
|
for player in self.players.lock().await.values() {
|
||||||
if player.entity_id() == id {
|
if player.entity_id() == id {
|
||||||
return Some(player.clone());
|
return Some(player.clone());
|
||||||
}
|
}
|
||||||
@ -643,12 +623,11 @@ impl World {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a Living Entity by entity id
|
/// Gets a Entity by entity id
|
||||||
pub async fn get_living_entity_by_entityid(&self, id: EntityId) -> Option<Arc<LivingEntity>> {
|
pub async fn get_entity_by_id(&self, id: EntityId) -> Option<Arc<dyn EntityBase>> {
|
||||||
for mob_entity in self.current_living_mobs.lock().await.values() {
|
for entity in self.entities.lock().await.values() {
|
||||||
let living_entity = &mob_entity.living_entity;
|
if entity.get_entity().entity_id == id {
|
||||||
if living_entity.entity_id() == id {
|
return Some(entity.clone());
|
||||||
return Some(living_entity.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -656,7 +635,7 @@ impl World {
|
|||||||
|
|
||||||
/// Gets a Player by username
|
/// Gets a Player by username
|
||||||
pub async fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
pub async fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
|
||||||
for player in self.current_players.lock().await.values() {
|
for player in self.players.lock().await.values() {
|
||||||
if player.gameprofile.name.to_lowercase() == name.to_lowercase() {
|
if player.gameprofile.name.to_lowercase() == name.to_lowercase() {
|
||||||
return Some(player.clone());
|
return Some(player.clone());
|
||||||
}
|
}
|
||||||
@ -677,7 +656,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// An `Option<Arc<Player>>` containing the player if found, or `None` if not.
|
/// An `Option<Arc<Player>>` containing the player if found, or `None` if not.
|
||||||
pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option<Arc<Player>> {
|
pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option<Arc<Player>> {
|
||||||
return self.current_players.lock().await.get(&id).cloned();
|
return self.players.lock().await.get(&id).cloned();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a list of players who's location equals the given position in the world.
|
/// Gets a list of players who's location equals the given position in the world.
|
||||||
@ -690,7 +669,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// * `position`: The position the function will check.
|
/// * `position`: The position the function will check.
|
||||||
pub async fn get_players_by_pos(&self, position: BlockPos) -> HashMap<uuid::Uuid, Arc<Player>> {
|
pub async fn get_players_by_pos(&self, position: BlockPos) -> HashMap<uuid::Uuid, Arc<Player>> {
|
||||||
self.current_players
|
self.players
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
@ -720,7 +699,7 @@ impl World {
|
|||||||
) -> HashMap<uuid::Uuid, Arc<Player>> {
|
) -> HashMap<uuid::Uuid, Arc<Player>> {
|
||||||
let radius_squared = radius.powi(2);
|
let radius_squared = radius.powi(2);
|
||||||
|
|
||||||
self.current_players
|
self.players
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
@ -766,11 +745,11 @@ impl World {
|
|||||||
/// * `player`: An `Arc<Player>` reference to the player object.
|
/// * `player`: An `Arc<Player>` reference to the player object.
|
||||||
pub async fn add_player(&self, uuid: uuid::Uuid, player: Arc<Player>) {
|
pub async fn add_player(&self, uuid: uuid::Uuid, player: Arc<Player>) {
|
||||||
{
|
{
|
||||||
let mut current_players = self.current_players.lock().await;
|
let mut current_players = self.players.lock().await;
|
||||||
current_players.insert(uuid, player.clone())
|
current_players.insert(uuid, player.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_players = self.current_players.clone();
|
let current_players = self.players.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let msg_comp = TextComponent::translate(
|
let msg_comp = TextComponent::translate(
|
||||||
"multiplayer.player.joined",
|
"multiplayer.player.joined",
|
||||||
@ -815,7 +794,7 @@ impl World {
|
|||||||
/// - This function assumes `broadcast_packet_expect` and `remove_entity` are defined elsewhere.
|
/// - This function assumes `broadcast_packet_expect` and `remove_entity` are defined elsewhere.
|
||||||
/// - The disconnect message sending is currently optional. Consider making it a configurable option.
|
/// - The disconnect message sending is currently optional. Consider making it a configurable option.
|
||||||
pub async fn remove_player(&self, player: Arc<Player>) {
|
pub async fn remove_player(&self, player: Arc<Player>) {
|
||||||
self.current_players
|
self.players
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.remove(&player.gameprofile.id)
|
.remove(&player.gameprofile.id)
|
||||||
@ -842,7 +821,7 @@ impl World {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
if !event.cancelled {
|
if !event.cancelled {
|
||||||
let players = self.current_players.lock().await;
|
let players = self.players.lock().await;
|
||||||
for player in players.values() {
|
for player in players.values() {
|
||||||
player.send_system_message(&event.leave_message).await;
|
player.send_system_message(&event.leave_message).await;
|
||||||
}
|
}
|
||||||
@ -859,25 +838,11 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// * `uuid`: The unique UUID of the living entity to add.
|
/// * `uuid`: The unique UUID of the living entity to add.
|
||||||
/// * `living_entity`: A `Arc<LivingEntity>` reference to the living entity object.
|
/// * `living_entity`: A `Arc<LivingEntity>` reference to the living entity object.
|
||||||
pub async fn add_mob_entity(&self, uuid: uuid::Uuid, living_entity: Arc<MobEntity>) {
|
pub async fn spawn_entity(&self, uuid: uuid::Uuid, entity: Arc<dyn EntityBase>) {
|
||||||
let mut current_living_entities = self.current_living_mobs.lock().await;
|
self.broadcast_packet_all(&entity.get_entity().create_spawn_packet(uuid))
|
||||||
current_living_entities.insert(uuid, living_entity);
|
.await;
|
||||||
}
|
let mut current_living_entities = self.entities.lock().await;
|
||||||
|
current_living_entities.insert(uuid, entity);
|
||||||
pub async fn remove_mob_entity(self: Arc<Self>, living_entity: Arc<LivingEntity>) {
|
|
||||||
let mut current_living_entities = self.current_living_mobs.lock().await;
|
|
||||||
current_living_entities
|
|
||||||
.remove(&living_entity.entity.entity_uuid)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// TODO: does this work with collisions?
|
|
||||||
living_entity.entity.set_pose(EntityPose::Dying).await;
|
|
||||||
|
|
||||||
let world = self.clone();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(2000)).await;
|
|
||||||
world.remove_entity(&living_entity.entity).await;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_entity(&self, entity: &Entity) {
|
pub async fn remove_entity(&self, entity: &Entity) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user