add working container state and change to fix with latest current branch

This commit is contained in:
Edvin Bryntesson 2024-09-02 15:05:56 +02:00
parent c98c0ec6bf
commit d1c4e59955
No known key found for this signature in database
11 changed files with 244 additions and 367 deletions

5
Cargo.lock generated
View File

@ -1901,6 +1901,7 @@ dependencies = [
"digest 0.11.0-pre.9",
"hmac",
"image",
"itertools 0.13.0",
"log",
"mio",
"num-bigint",
@ -2806,9 +2807,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.39.2"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
dependencies = [
"backtrace",
"bytes",

View File

@ -1,4 +1,3 @@
use crate::WindowType;
use pumpkin_world::item::ItemStack;
pub struct ClickMode {
@ -8,8 +7,6 @@ pub struct ClickMode {
pub struct Click {
pub mode: ClickMode,
pub state_id: u32,
pub window_type: WindowType,
pub changed_items: Vec<ItemChange>,
pub carried_item: Option<ItemStack>,
}
@ -25,7 +22,7 @@ impl ClickMode {
click_type: ClickType::CreativePickItem,
slot: Slot::Normal(slot.try_into().unwrap()),
},
4 => Self::new_drop_item(button, slot),
4 => Self::new_drop_item(button),
5 => Self::new_drag_item(button, slot),
6 => Self {
click_type: ClickType::DoubleClick,
@ -78,7 +75,7 @@ impl ClickMode {
}
}
fn new_drop_item(button: i8, slot: i16) -> Self {
fn new_drop_item(button: i8) -> Self {
let drop_type = match button {
0 => DropType::SingleItem,
1 => DropType::FullStack,
@ -87,7 +84,7 @@ impl ClickMode {
};
Self {
click_type: ClickType::DropType(drop_type),
slot: Slot::Normal(slot.try_into().unwrap()),
slot: Slot::OutsideInventory,
}
}
@ -108,7 +105,6 @@ impl ClickMode {
MouseDragState::End,
Slot::OutsideInventory,
),
4 => (
MouseDragType::Right,
MouseDragState::Start,

View File

@ -76,7 +76,28 @@ pub trait Container: Sync + Send {
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>>;
fn state_id(&mut self) -> i32;
fn all_combinable_slots(&self) -> Vec<Option<&ItemStack>> {
self.all_slots_ref()
}
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.all_slots()
}
fn advance_state_id(&mut self) -> i32;
fn reset_state_id(&mut self);
fn state_id(&self) -> i32;
fn print_all_contents(&self) {
self.all_slots_ref()
.into_iter()
.enumerate()
.filter_map(|(slot, item)| item.map(|item| (slot, item)))
.for_each(|(slot, item)| {
dbg!(slot, item);
});
}
}
pub fn handle_item_take(
@ -106,12 +127,6 @@ pub fn handle_item_change(
current_slot: &mut Option<ItemStack>,
mouse_click: MouseClick,
) {
if let Some(carried) = carried_slot.as_ref() {
dbg!(carried);
}
if let Some(current) = current_slot.as_ref() {
dbg!(current);
}
match (current_slot.as_mut(), carried_slot.as_mut()) {
// Swap or combine current and carried
(Some(current), Some(carried)) => {
@ -170,25 +185,23 @@ pub fn combine_stacks(
}
}
pub struct OptionallyCombinedContainer<'a> {
pub struct OptionallyCombinedContainer<'a, 'b> {
container: Option<&'a mut Box<dyn Container>>,
inventory: &'a mut PlayerInventory,
state_id: i32,
inventory: &'b mut PlayerInventory,
}
impl<'a> OptionallyCombinedContainer<'a> {
impl<'a, 'b> OptionallyCombinedContainer<'a, 'b> {
pub fn new(
player_inventory: &'a mut PlayerInventory,
player_inventory: &'b mut PlayerInventory,
container: Option<&'a mut Box<dyn Container>>,
) -> Self {
Self {
inventory: player_inventory,
container,
state_id: 0,
}
}
}
impl<'a> Container for OptionallyCombinedContainer<'a> {
impl<'a> Container for OptionallyCombinedContainer<'a, 'a> {
fn window_type(&self) -> &'static WindowType {
if let Some(container) = &self.container {
container.window_type()
@ -198,27 +211,38 @@ impl<'a> Container for OptionallyCombinedContainer<'a> {
}
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
let mut slots = if let Some(container) = &mut self.container {
container.all_slots()
} else {
vec![]
let slots = match &mut self.container {
Some(container) => {
let mut slots = container.all_slots();
slots.extend(self.inventory.all_combinable_slots_mut());
slots
}
None => self.inventory.all_slots(),
};
slots.extend(self.inventory.all_slots());
dbg!(slots.len());
slots
}
fn all_slots_ref(&self) -> Vec<Option<&ItemStack>> {
let mut slots = if let Some(container) = &self.container {
container.all_slots_ref()
} else {
vec![]
};
slots.extend(self.inventory.all_slots_ref());
slots
match &self.container {
Some(container) => {
let mut slots = container.all_slots_ref();
slots.extend(self.inventory.all_combinable_slots());
slots
}
None => self.inventory.all_slots_ref(),
}
}
fn state_id(&mut self) -> i32 {
self.state_id += 1;
self.state_id - 1
fn advance_state_id(&mut self) -> i32 {
self.inventory.advance_state_id()
}
fn reset_state_id(&mut self) {
self.inventory.reset_state_id()
}
fn state_id(&self) -> i32 {
self.inventory.state_id()
}
}

View File

@ -10,6 +10,7 @@ pub struct OpenContainer {
impl OpenContainer {
pub fn try_open(&self, player_id: i32) -> Option<MutexGuard<Box<dyn Container>>> {
if !self.players.contains(&player_id) {
dbg!("couldn't open container");
return None;
}
let container = self.container.lock().unwrap();
@ -31,6 +32,18 @@ impl OpenContainer {
}
}
pub fn remove_player(&mut self, player_id: i32) {
if let Some(index) = self.players.iter().enumerate().find_map(|(index, id)| {
if *id == player_id {
Some(index)
} else {
None
}
}) {
self.players.remove(index);
}
}
pub fn empty(player_id: i32) -> Self {
Self {
players: vec![player_id],
@ -58,6 +71,7 @@ impl Container for Chest {
}
fn all_slots(&mut self) -> Vec<&mut Option<ItemStack>> {
dbg!(self.slots.iter().len());
self.slots.iter_mut().collect()
}
@ -65,8 +79,16 @@ impl Container for Chest {
self.slots.iter().map(|slot| slot.as_ref()).collect()
}
fn state_id(&mut self) -> i32 {
fn advance_state_id(&mut self) -> i32 {
self.state_id += 1;
self.state_id - 1
}
fn reset_state_id(&mut self) {
self.state_id = 0;
}
fn state_id(&self) -> i32 {
self.state_id
}
}

View File

@ -14,6 +14,7 @@ pub struct PlayerInventory {
// current selected slot in hotbar
selected: usize,
state_id: i32,
pub total_opened_containers: u8,
}
impl Default for PlayerInventory {
@ -33,6 +34,7 @@ impl PlayerInventory {
// TODO: What when player spawns in with an different index ?
selected: 0,
state_id: 0,
total_opened_containers: 2,
}
}
@ -155,8 +157,23 @@ impl Container for PlayerInventory {
self.slots()
}
fn state_id(&mut self) -> i32 {
fn all_combinable_slots(&self) -> Vec<Option<&ItemStack>> {
self.items.iter().map(|item| item.as_ref()).collect()
}
fn all_combinable_slots_mut(&mut self) -> Vec<&mut Option<ItemStack>> {
self.items.iter_mut().collect()
}
fn advance_state_id(&mut self) -> i32 {
self.state_id += 1;
self.state_id - 1
self.state_id
}
fn reset_state_id(&mut self) {
self.state_id = 0;
}
fn state_id(&self) -> i32 {
self.state_id
}
}

View File

@ -64,3 +64,4 @@ crossbeam-channel = "0.5.13"
uuid.workspace = true
tokio.workspace = true
rayon.workspace = true
itertools = "0.13.0"

View File

@ -1,8 +1,8 @@
use num_traits::FromPrimitive;
use itertools::Itertools;
use pumpkin_core::text::TextComponent;
use pumpkin_inventory::container_click::{KeyClick, MouseClick};
use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait};
use pumpkin_inventory::{container_click, handle_item_change, OptionallyCombinedContainer};
use pumpkin_inventory::{container_click, OptionallyCombinedContainer};
use pumpkin_inventory::{Container, WindowType};
use pumpkin_protocol::client::play::{
CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot,
@ -10,6 +10,7 @@ use pumpkin_protocol::client::play::{
use pumpkin_protocol::server::play::SClickContainer;
use pumpkin_protocol::slot::Slot;
use pumpkin_world::item::ItemStack;
use std::sync::MutexGuard;
use crate::entity::player::Player;
use crate::server::Server;
@ -17,12 +18,12 @@ use crate::server::Server;
impl Player {
pub fn open_container(
&mut self,
container: &Box<dyn Container>,
server: &mut Server,
minecraft_menu_id: &str,
window_title: Option<&str>,
carried_item: Option<&ItemStack>,
state_id: i32,
) {
let mut container = self.get_open_container(server);
let container = container.as_deref_mut();
let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY
.get("minecraft:menu")
.unwrap()
@ -32,56 +33,48 @@ impl Player {
.get("protocol_id")
.unwrap())
.into();
let window_type = container.window_type();
let window_type = match &container {
Some(container) => container.window_type(),
None => &WindowType::Generic9x1,
}
.to_owned();
let title = TextComponent::text(window_title.unwrap_or(window_type.default_title()));
self.inventory.reset_state_id();
self.client.send_packet(&COpenScreen::new(
(*window_type as u8 + 1).into(),
self.inventory.total_opened_containers.into(),
menu_protocol_id,
title,
));
self.set_container_content(Some(container), carried_item, state_id);
self.set_container_content(container);
}
pub fn set_container_content<'a>(
&mut self,
container: Option<&Box<dyn Container>>,
carried_item: Option<&'a ItemStack>,
state_id: i32,
) {
let slots: Vec<Slot> = {
let items = container.map(|container| container.all_slots_ref());
if let Some(mut items) = items {
items.extend(self.inventory.all_slots_ref());
items
} else {
self.inventory.all_slots_ref()
}
pub fn set_container_content(&mut self, container: Option<&mut Box<dyn Container>>) {
let total_opened_containers = self.inventory.total_opened_containers;
let container = OptionallyCombinedContainer::new(&mut self.inventory, container);
let slots = container
.all_slots_ref()
.into_iter()
.map(|item| {
if let Some(item) = item {
Slot::from(item)
} else {
Slot::empty()
}
})
.collect()
};
.map(Slot::from)
.collect_vec();
let carried_item = {
if let Some(item) = carried_item {
if let Some(item) = self.carried_item.as_ref() {
item.into()
} else {
Slot::empty()
}
};
let window_type = match container {
Some(container) => *container.window_type() as u8,
None => 0,
};
let packet = CSetContainerContent::new(window_type, state_id.into(), &slots, &carried_item);
let packet = CSetContainerContent::new(
total_opened_containers,
self.inventory.state_id().into(),
&slots,
&carried_item,
);
self.inventory.advance_state_id();
self.client.send_packet(&packet);
}
@ -101,32 +94,54 @@ impl Player {
}
/// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent
pub fn close_container(&mut self, window_type: WindowType) {
self.client
.send_packet(&CCloseContainer::new(window_type as u8))
pub fn close_container(&mut self) {
self.inventory.total_opened_containers += 1;
self.client.send_packet(&CCloseContainer::new(
self.inventory.total_opened_containers,
))
}
pub fn set_container_property<T: WindowPropertyTrait>(
&mut self,
window_type: WindowType,
window_property: WindowProperty<T>,
) {
let (id, value) = window_property.into_tuple();
self.client
.send_packet(&CSetContainerProperty::new(window_type as u8, id, value));
self.client.send_packet(&CSetContainerProperty::new(
self.inventory.total_opened_containers,
id,
value,
));
}
pub fn handle_click_container(&mut self, server: &mut Server, packet: SClickContainer) {
use container_click::*;
let mut opened_container = if let Some(id) = self.open_container {
server.try_get_container(self.entity_id(), id)
} else {
None
};
let mut opened_container = self.get_open_container(server);
let opened_container = opened_container.as_deref_mut();
let current_state_id = if let Some(container) = opened_container.as_ref() {
container.state_id()
} else {
self.inventory.state_id()
};
if current_state_id != packet.state_id.0 {
dbg!(current_state_id, packet.state_id.0);
//self.set_container_content(opened_container.as_deref_mut());
//return;
}
if opened_container.is_some() {
if packet.window_id != self.inventory.total_opened_containers {
dbg!(
self.inventory.total_opened_containers,
opened_container.unwrap().window_type()
);
return;
}
} else if packet.window_id != 0 {
dbg!("weird");
return;
}
let click = Click {
state_id: packet.state_id.0.try_into().unwrap(),
changed_items: packet
.array_of_changed_slots
.into_iter()
@ -139,12 +154,6 @@ impl Player {
}
})
.collect::<Vec<_>>(),
window_type: WindowType::from_u8(if packet.window_id == 0 {
0
} else {
packet.window_id - 1
})
.unwrap(),
carried_item: packet.carried_item.to_item(),
mode: ClickMode::new(
packet
@ -157,18 +166,13 @@ impl Player {
),
};
match click.mode.click_type {
ClickType::MouseClick(mouse_click) => self.mouse_click(
opened_container,
mouse_click,
click.window_type,
click.mode.slot,
),
ClickType::ShiftClick => {
self.shift_mouse_click(opened_container, click.window_type, click.mode.slot)
ClickType::MouseClick(mouse_click) => {
self.mouse_click(opened_container, mouse_click, click.mode.slot)
}
ClickType::ShiftClick => self.shift_mouse_click(opened_container, click.mode.slot),
ClickType::KeyClick(key_click) => match click.mode.slot {
container_click::Slot::Normal(slot) => {
self.number_button_pressed(opened_container, click.window_type, key_click, slot)
self.number_button_pressed(opened_container, key_click, slot)
}
container_click::Slot::OutsideInventory => {
unimplemented!("This is not a valid state")
@ -189,20 +193,16 @@ impl Player {
drag_type: _,
} => (),
ClickType::DropType(_drop_type) => (),
}
};
}
fn mouse_click(
&mut self,
opened_container: Option<&mut Box<dyn Container>>,
mouse_click: MouseClick,
window_type: WindowType,
slot: container_click::Slot,
) {
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
match slot {
container_click::Slot::Normal(slot) => {
@ -215,41 +215,42 @@ impl Player {
fn shift_mouse_click(
&mut self,
opened_container: Option<&mut Box<dyn Container>>,
window_type: WindowType,
slot: container_click::Slot,
) {
let container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
match slot {
container_click::Slot::Normal(slot) => {
if let Some(item_in_pressed_slot) = self.inventory.slots()[slot] {
let slots = self.inventory.slots().into_iter().enumerate();
let all_slots = container.all_slots();
if let Some(item_in_pressed_slot) = all_slots[slot].to_owned() {
let slots = all_slots.into_iter().enumerate();
// Hotbar
let find_condition = |(_, slot): &(usize, Option<&ItemStack>)| {
slot.is_none_or(|item| item.item_id == item_in_pressed_slot.item_id)
let find_condition = |(slot_number, slot): (usize, &mut Option<ItemStack>)| {
// TODO: Check for max item count here
match slot {
Some(item) => {
if item.item_id == item_in_pressed_slot.item_id
&& item.item_count != 64
{
Some(slot_number)
} else {
None
}
}
None => Some(slot_number),
}
};
let slots = if slot > 35 {
slots
.skip(9)
.find(find_condition)
.map(|(slot_num, _)| slot_num)
slots.skip(9).find_map(find_condition)
} else {
slots
.skip(36)
.rev()
.find(find_condition)
.map(|(slot_num, _)| slot_num)
slots.skip(36).rev().find_map(find_condition)
};
if let Some(slot) = slots {
let mut item_slot = self.inventory.slots()[slot].map(|i| i.to_owned());
let mut item_slot = container.all_slots()[slot].map(|i| i.to_owned());
self.inventory
.handle_item_change(&mut item_slot, slot, MouseClick::Left);
*self.inventory.slots_mut()[slot] = item_slot;
container.handle_item_change(&mut item_slot, slot, MouseClick::Left);
*container.all_slots()[slot] = item_slot;
}
}
}
@ -260,7 +261,6 @@ impl Player {
fn number_button_pressed(
&mut self,
opened_container: Option<&mut Box<dyn Container>>,
window_type: WindowType,
key_click: KeyClick,
slot: usize,
) {
@ -270,15 +270,9 @@ impl Player {
};
let mut changing_item_slot = self.inventory.get_slot(changing_slot as usize).to_owned();
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
if container.window_type() != &window_type {
return;
}
let item_slot = &mut container.all_slots()[slot];
if item_slot.is_some() {
handle_item_change(&mut changing_item_slot, item_slot, MouseClick::Left);
*self.inventory.get_slot(changing_slot as usize) = changing_item_slot
}
container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left);
*self.inventory.get_slot(changing_slot as usize) = changing_item_slot
}
fn creative_pick_item(
@ -293,4 +287,15 @@ impl Player {
}
fn double_click(&mut self, _slot: usize) {}
pub fn get_open_container<'a>(
&self,
server: &'a Server,
) -> Option<MutexGuard<'a, Box<dyn Container>>> {
if let Some(id) = self.open_container {
server.try_get_container(self.entity_id(), id)
} else {
None
}
}
}

View File

@ -537,8 +537,14 @@ impl Player {
// TODO:
// This function will in the future be used to keep track of if the client is in a valid state.
// But this is not possible yet
pub fn handle_close_container(&mut self, _server: &mut Server, packet: SCloseContainer) {
pub fn handle_close_container(&mut self, server: &mut Server, packet: SCloseContainer) {
// window_id 0 represents both 9x1 Generic AND inventory here
if let Some(id) = self.open_container {
if let Some(container) = server.open_containers.get_mut(&id) {
container.remove_player(self.entity_id())
}
self.open_container = None;
}
let Some(_window_type) = WindowType::from_u8(packet.window_id) else {
self.kick(TextComponent::text("Invalid window ID"));
return;

View File

@ -10,34 +10,18 @@ const DESCRIPTION: &str =
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| {
if let Some(player) = sender.as_mut_player() {
let entity_id = player.entity_id().clone();
let entity_id = player.entity_id();
player.open_container = Some(0);
let mut container = match server.open_containers.get_mut(&0) {
match server.open_containers.get_mut(&0) {
Some(ender_chest) => {
if ender_chest.try_open(entity_id).is_none() {
ender_chest.add_player(entity_id);
}
ender_chest.try_open(entity_id).unwrap()
ender_chest.add_player(entity_id);
}
None => {
let open_container = OpenContainer::empty(entity_id);
server.open_containers.insert(0, open_container);
server
.open_containers
.get_mut(&0)
.unwrap()
.try_open(entity_id)
.unwrap()
}
};
let state_id = container.state_id();
player.open_container(
&container,
"minecraft:generic_9x3",
Some("Ender Chest"),
None,
state_id,
);
}
player.open_container(server, "minecraft:generic_9x3", Some("Ender Chest"));
}
Ok(())

View File

@ -1,8 +1,6 @@
use std::sync::Arc;
use crate::{client::Client, server::Server};
use num_derive::{FromPrimitive, ToPrimitive};
use num_derive::FromPrimitive;
use num_traits::ToPrimitive;
use pumpkin_core::{
math::{boundingbox::BoundingBox, position::WorldPosition, vector3::Vector3},
@ -26,16 +24,13 @@ use pumpkin_protocol::{
ConnectionState, RawPacket, ServerPacket, VarInt,
};
use pumpkin_protocol::server::play::SCloseContainer;
use pumpkin_world::item::ItemStack;
use pumpkin_world::vector3::Vector3;
use serde::{Deserialize, Serialize};
use crate::{
client::{authentication::GameProfile, Client, PlayerConfig},
server::Server,
world::World,
util::boundingbox::BoundingBox
};
pub struct PlayerAbilities {
@ -460,6 +455,10 @@ impl Player {
self.handle_click_container(server, SClickContainer::read(bytebuf)?);
Ok(())
}
SCloseContainer::PACKET_ID => {
self.handle_close_container(server, SCloseContainer::read(bytebuf)?);
Ok(())
}
_ => {
log::error!("Failed to handle player packet id {:#04x}", packet.id.0);
Ok(())

View File

@ -1,13 +1,3 @@
use std::{
io::Cursor,
path::Path,
sync::{
atomic::{AtomicI32, Ordering},
Arc, Mutex,
},
time::Duration,
};
use base64::{engine::general_purpose, Engine};
use image::GenericImageView;
use mio::Token;
@ -20,19 +10,21 @@ use pumpkin_protocol::{
CURRENT_MC_PROTOCOL,
};
use pumpkin_world::dimension::Dimension;
use crate::{
client::Client,
config::{AdvancedConfiguration, BasicConfiguration},
entity::player::{GameMode, Player},
world::World,
use std::collections::HashMap;
use std::{
io::Cursor,
path::Path,
sync::{
atomic::{AtomicI32, Ordering},
Arc, Mutex, MutexGuard,
},
time::Duration,
};
use crate::{client::Client, entity::player::Player, world::World};
use pumpkin_inventory::{Container, OpenContainer};
use pumpkin_registry::Registry;
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
pub const CURRENT_MC_VERSION: &str = "1.21.1";
@ -55,7 +47,6 @@ pub struct Server {
/// Cache the registry so we don't have to parse it every time a player joins
pub cached_registry: Vec<Registry>,
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
pub open_containers: HashMap<u64, OpenContainer>,
entity_id: AtomicI32,
@ -102,6 +93,7 @@ impl Server {
Self {
plugin_loader,
cached_registry: Registry::get_static(),
open_containers: HashMap::new(),
// 0 is invalid
entity_id: 2.into(),
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
@ -111,8 +103,6 @@ impl Server {
status_response,
status_response_json,
public_key_der,
open_containers: HashMap::new(),
base_config: config.0,
auth_client,
}
}
@ -134,188 +124,20 @@ impl Server {
client,
world.clone(),
entity_id,
self.base_config.hardcore,
&["minecraft:overworld"],
self.base_config.max_players.into(),
self.base_config.view_distance.into(), // TODO: view distance
self.base_config.simulation_distance.into(), // TODO: sim view dinstance
false,
false,
false,
0.into(),
"minecraft:overworld",
0, // seed
gamemode.to_u8().unwrap(),
self.base_config.default_gamemode.to_i8().unwrap(),
false,
false,
None,
0.into(),
false,
));
dbg!("sending abilities");
// player abilities
player
.client
.send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1));
// teleport
let x = 10.0;
let y = 120.0;
let z = 10.0;
let yaw = 10.0;
let pitch = 10.0;
player.teleport(x, y, z, 10.0, 10.0);
let gameprofile = &player.gameprofile;
// first send info update to our new player, So he can see his Skin
// also send his info to everyone else
self.broadcast_packet(
player,
&CPlayerInfoUpdate::new(
0x01 | 0x08,
&[pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![
PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
},
PlayerAction::UpdateListed(true),
],
}],
),
);
// here we send all the infos of already joined players
let mut entries = Vec::new();
for (_, playerr) in self
.current_players
.iter()
.filter(|c| c.0 != &player.client.token)
{
let playerr = playerr.as_ref().lock().unwrap();
let gameprofile = &playerr.gameprofile;
entries.push(pumpkin_protocol::client::play::Player {
uuid: gameprofile.id,
actions: vec![
PlayerAction::AddPlayer {
name: gameprofile.name.clone(),
properties: gameprofile.properties.clone(),
},
PlayerAction::UpdateListed(true),
],
})
}
player
.client
.send_packet(&CPlayerInfoUpdate::new(0x01 | 0x08, &entries));
// Start waiting for level chunks
player.client.send_packet(&CGameEvent::new(13, 0.0));
let gameprofile = &player.gameprofile;
// spawn player for every client
self.broadcast_packet_except(
&[&player.client.token],
// TODO: add velo
&CSpawnEntity::new(
entity_id.into(),
UUID(gameprofile.id),
(EntityType::Player as i32).into(),
x,
y,
z,
pitch,
yaw,
yaw,
0.into(),
0.0,
0.0,
0.0,
),
);
// spawn players for our client
let token = player.client.token.clone();
for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) {
let existing_player = existing_player.as_ref().lock().unwrap();
let entity = &existing_player.entity;
let gameprofile = &existing_player.gameprofile;
player.client.send_packet(&CSpawnEntity::new(
existing_player.entity_id().into(),
UUID(gameprofile.id),
EntityType::Player.to_i32().unwrap().into(),
entity.x,
entity.y,
entity.z,
entity.yaw,
entity.pitch,
entity.pitch,
0.into(),
0.0,
0.0,
0.0,
))
}
// entity meta data
if let Some(config) = player.client.config.as_ref() {
self.broadcast_packet(
player,
&CSetEntityMetadata::new(
entity_id.into(),
Metadata::new(17, VarInt(0), config.skin_parts),
),
)
}
self.spawn_test_chunk(player, self.base_config.view_distance as u32)
.await;
}
/// TODO: This definitly should be in world
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<MutexGuard<Player>> {
for (_, player) in self
.current_players
.iter()
.filter(|c| c.0 != &from.client.token)
{
let player = player.lock().unwrap();
if player.entity_id() == id {
return Some(player);
}
}
None
gamemode,
)));
world.lock().await.add_player(token, player.clone());
(player, world)
}
pub fn try_get_container(
&self,
player_id: EntityId,
container_id: u64,
) -> Option<MutexGuard<Box<dyn Container>>> {
) -> Option<MutexGuard<'_, Box<dyn Container>>> {
self.open_containers.get(&container_id)?.try_open(player_id)
}
/// Sends a Packet to all Players
pub fn broadcast_packet<P>(&self, from: &mut Player, packet: &P)
where
P: ClientPacket,
{
// we can't borrow twice at same time
from.client.send_packet(packet);
for (_, player) in self
.current_players
.iter()
.filter(|c| c.0 != &from.client.token)
{
let mut player = player.lock().unwrap();
player.client.send_packet(packet);
}
gamemode,
)));
world.lock().await.add_player(token, player.clone());
(player, world)
}
/// Sends a Packet to all Players in all worlds
pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], packet: &P)
where