mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
Merge pull request #88 from Snowiiii/fix-locks
Solve Pumpkin's locking problem
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std::sync::atomic::AtomicU32;
|
||||
|
||||
use crate::container_click::MouseClick;
|
||||
use crate::{handle_item_change, Container, InventoryError, WindowType};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
@@ -11,7 +13,7 @@ pub struct PlayerInventory {
|
||||
offhand: Option<ItemStack>,
|
||||
// current selected slot in hotbar
|
||||
selected: usize,
|
||||
pub state_id: u32,
|
||||
pub state_id: AtomicU32,
|
||||
// Notchian server wraps this value at 100, we can just keep it as a u8 that automatically wraps
|
||||
pub total_opened_containers: u8,
|
||||
}
|
||||
@@ -32,7 +34,7 @@ impl PlayerInventory {
|
||||
offhand: None,
|
||||
// TODO: What when player spawns in with an different index ?
|
||||
selected: 0,
|
||||
state_id: 0,
|
||||
state_id: AtomicU32::new(0),
|
||||
total_opened_containers: 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ pub enum PacketError {
|
||||
MalformedLength,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ConnectionState {
|
||||
HandShake,
|
||||
Status,
|
||||
|
||||
@@ -85,7 +85,6 @@ impl PacketEncoder {
|
||||
|
||||
let mut front = &mut self.buf[start_len..];
|
||||
|
||||
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||
VarInt(packet_len as i32)
|
||||
.encode(&mut front)
|
||||
.map_err(|_| PacketError::EncodeLength)?;
|
||||
|
||||
@@ -37,12 +37,16 @@ use super::{
|
||||
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT
|
||||
/// TODO: REMOVE ALL UNWRAPS
|
||||
impl Client {
|
||||
pub fn handle_handshake(&mut self, _server: &Arc<Server>, handshake: SHandShake) {
|
||||
pub fn handle_handshake(&self, _server: &Arc<Server>, handshake: SHandShake) {
|
||||
dbg!("handshake");
|
||||
self.protocol_version = handshake.protocol_version.0;
|
||||
self.connection_state = handshake.next_state;
|
||||
if self.connection_state != ConnectionState::Status {
|
||||
let protocol = self.protocol_version;
|
||||
let version = handshake.protocol_version.0;
|
||||
self.protocol_version
|
||||
.store(version, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut connection_state = self.connection_state.lock().unwrap();
|
||||
|
||||
*connection_state = handshake.next_state;
|
||||
if *connection_state != ConnectionState::Status {
|
||||
let protocol = version;
|
||||
match protocol.cmp(&(CURRENT_MC_PROTOCOL as i32)) {
|
||||
std::cmp::Ordering::Less => {
|
||||
self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}"));
|
||||
@@ -55,11 +59,11 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_status_request(&mut self, server: &Arc<Server>, _status_request: SStatusRequest) {
|
||||
pub fn handle_status_request(&self, server: &Arc<Server>, _status_request: SStatusRequest) {
|
||||
self.send_packet(&CStatusResponse::new(&server.status_response_json));
|
||||
}
|
||||
|
||||
pub fn handle_ping_request(&mut self, _server: &Arc<Server>, ping_request: SStatusPingRequest) {
|
||||
pub fn handle_ping_request(&self, _server: &Arc<Server>, ping_request: SStatusPingRequest) {
|
||||
dbg!("ping");
|
||||
self.send_packet(&CPingResponse::new(ping_request.payload));
|
||||
self.close();
|
||||
@@ -72,7 +76,7 @@ impl Client {
|
||||
.all(|c| c > 32_u8 as char && c < 127_u8 as char)
|
||||
}
|
||||
|
||||
pub fn handle_login_start(&mut self, server: &Arc<Server>, login_start: SLoginStart) {
|
||||
pub fn handle_login_start(&self, server: &Arc<Server>, login_start: SLoginStart) {
|
||||
log::debug!("login start, State {:?}", self.connection_state);
|
||||
|
||||
if !Self::is_valid_player_name(&login_start.name) {
|
||||
@@ -81,7 +85,8 @@ impl Client {
|
||||
}
|
||||
// default game profile, when no online mode
|
||||
// TODO: make offline uuid
|
||||
self.gameprofile = Some(GameProfile {
|
||||
let mut gameprofile = self.gameprofile.lock().unwrap();
|
||||
*gameprofile = Some(GameProfile {
|
||||
id: login_start.uuid,
|
||||
name: login_start.name,
|
||||
properties: vec![],
|
||||
@@ -108,7 +113,7 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn handle_encryption_response(
|
||||
&mut self,
|
||||
&self,
|
||||
server: &Arc<Server>,
|
||||
encryption_response: SEncryptionResponse,
|
||||
) {
|
||||
@@ -120,15 +125,17 @@ impl Client {
|
||||
self.enable_encryption(&shared_secret)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
|
||||
let mut gameprofile = self.gameprofile.lock().unwrap();
|
||||
|
||||
if BASIC_CONFIG.online_mode {
|
||||
let hash = Sha1::new()
|
||||
.chain_update(&shared_secret)
|
||||
.chain_update(&server.public_key_der)
|
||||
.finalize();
|
||||
let hash = auth_digest(&hash);
|
||||
let ip = self.address.ip();
|
||||
let ip = self.address.lock().unwrap().ip();
|
||||
match authentication::authenticate(
|
||||
&self.gameprofile.as_ref().unwrap().name,
|
||||
&gameprofile.as_ref().unwrap().name,
|
||||
&hash,
|
||||
&ip,
|
||||
server,
|
||||
@@ -159,12 +166,12 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.gameprofile = Some(p);
|
||||
*gameprofile = Some(p);
|
||||
}
|
||||
Err(e) => self.kick(&e.to_string()),
|
||||
}
|
||||
}
|
||||
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
||||
for ele in gameprofile.as_ref().unwrap().properties.clone() {
|
||||
// todo, use this
|
||||
unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures);
|
||||
}
|
||||
@@ -177,7 +184,7 @@ impl Client {
|
||||
self.set_compression(Some((threshold, level)));
|
||||
}
|
||||
|
||||
if let Some(profile) = self.gameprofile.as_ref().cloned() {
|
||||
if let Some(profile) = gameprofile.as_ref().cloned() {
|
||||
let packet = CLoginSuccess::new(&profile.id, &profile.name, &profile.properties, false);
|
||||
self.send_packet(&packet);
|
||||
} else {
|
||||
@@ -186,18 +193,18 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn handle_plugin_response(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
_plugin_response: SLoginPluginResponse,
|
||||
) {
|
||||
}
|
||||
|
||||
pub fn handle_login_acknowledged(
|
||||
&mut self,
|
||||
&self,
|
||||
server: &Arc<Server>,
|
||||
_login_acknowledged: SLoginAcknowledged,
|
||||
) {
|
||||
self.connection_state = ConnectionState::Config;
|
||||
*self.connection_state.lock().unwrap() = ConnectionState::Config;
|
||||
server.send_brand(self);
|
||||
|
||||
let resource_config = &ADVANCED_CONFIG.resource_pack;
|
||||
@@ -228,12 +235,12 @@ impl Client {
|
||||
dbg!("login achnowlaged");
|
||||
}
|
||||
pub fn handle_client_information_config(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
client_information: SClientInformationConfig,
|
||||
) {
|
||||
dbg!("got client settings");
|
||||
self.config = Some(PlayerConfig {
|
||||
*self.config.lock().unwrap() = Some(PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
view_distance: client_information.view_distance,
|
||||
chat_mode: ChatMode::from_i32(client_information.chat_mode.into()).unwrap(),
|
||||
@@ -245,19 +252,19 @@ impl Client {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn handle_plugin_message(&mut self, _server: &Arc<Server>, plugin_message: SPluginMessage) {
|
||||
pub fn handle_plugin_message(&self, _server: &Arc<Server>, plugin_message: SPluginMessage) {
|
||||
if plugin_message.channel.starts_with("minecraft:brand")
|
||||
|| plugin_message.channel.starts_with("MC|Brand")
|
||||
{
|
||||
dbg!("got a client brand");
|
||||
match String::from_utf8(plugin_message.data) {
|
||||
Ok(brand) => self.brand = Some(brand),
|
||||
Ok(brand) => *self.brand.lock().unwrap() = Some(brand),
|
||||
Err(e) => self.kick(&e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_known_packs(&mut self, server: &Arc<Server>, _config_acknowledged: SKnownPacks) {
|
||||
pub fn handle_known_packs(&self, server: &Arc<Server>, _config_acknowledged: SKnownPacks) {
|
||||
for registry in &server.cached_registry {
|
||||
self.send_packet(&CRegistryData::new(
|
||||
®istry.registry_id,
|
||||
@@ -271,12 +278,13 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn handle_config_acknowledged(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
_config_acknowledged: SAcknowledgeFinishConfig,
|
||||
) {
|
||||
dbg!("config acknowledged");
|
||||
self.connection_state = ConnectionState::Play;
|
||||
self.make_player = true;
|
||||
*self.connection_state.lock().unwrap() = ConnectionState::Play;
|
||||
self.make_player
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use pumpkin_world::item::ItemStack;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
impl Player {
|
||||
pub fn open_container(&mut self, server: &Arc<Server>, minecraft_menu_id: &str) {
|
||||
pub fn open_container(&self, server: &Arc<Server>, minecraft_menu_id: &str) {
|
||||
self.inventory.state_id = 0;
|
||||
let total_opened_containers = self.inventory.total_opened_containers;
|
||||
let container = self.get_open_container(server);
|
||||
@@ -49,9 +49,9 @@ impl Player {
|
||||
self.set_container_content(container.as_deref_mut());
|
||||
}
|
||||
|
||||
pub fn set_container_content(&mut self, container: Option<&mut Box<dyn Container>>) {
|
||||
pub fn set_container_content(&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 container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), container);
|
||||
|
||||
let slots = container
|
||||
.all_slots_ref()
|
||||
@@ -77,7 +77,7 @@ 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) {
|
||||
pub fn close_container(&self) {
|
||||
self.inventory.total_opened_containers += 1;
|
||||
self.client.send_packet(&CCloseContainer::new(
|
||||
self.inventory.total_opened_containers,
|
||||
@@ -97,7 +97,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn handle_click_container(
|
||||
&mut self,
|
||||
&self,
|
||||
server: &Arc<Server>,
|
||||
packet: SClickContainer,
|
||||
) -> Result<(), InventoryError> {
|
||||
@@ -178,7 +178,7 @@ impl Player {
|
||||
self.send_whole_container_change(server).await?;
|
||||
} else if let container_click::Slot::Normal(slot_index) = click.slot {
|
||||
let combined_container = OptionallyCombinedContainer::new(
|
||||
&mut self.inventory,
|
||||
&mut self.inventory.lock().unwrap().lock().unwrap().lock().unwrap(),
|
||||
Some(&mut opened_container),
|
||||
);
|
||||
if let Some(slot) = combined_container.get_slot_excluding_inventory(slot_index) {
|
||||
@@ -193,27 +193,30 @@ impl Player {
|
||||
}
|
||||
|
||||
fn mouse_click(
|
||||
&mut self,
|
||||
&self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
mouse_click: MouseClick,
|
||||
slot: container_click::Slot,
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
|
||||
let mut inventory = self.inventory.lock().unwrap();
|
||||
let mut container =
|
||||
OptionallyCombinedContainer::new(&mut inventory, opened_container);
|
||||
|
||||
match slot {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
container.handle_item_change(&mut self.carried_item, slot, mouse_click)
|
||||
container.handle_item_change(&mut self.carried_item.lock().unwrap(), slot, mouse_click)
|
||||
}
|
||||
container_click::Slot::OutsideInventory => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn shift_mouse_click(
|
||||
&mut self,
|
||||
&self,
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
slot: container_click::Slot,
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
let mut inventory = self.inventory.lock().unwrap();
|
||||
let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container);
|
||||
|
||||
match slot {
|
||||
container_click::Slot::Normal(slot) => {
|
||||
@@ -265,7 +268,7 @@ impl Player {
|
||||
KeyClick::Offhand => 45,
|
||||
};
|
||||
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);
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container);
|
||||
|
||||
container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left)?;
|
||||
*self.inventory.get_slot(changing_slot as usize)? = changing_item_slot;
|
||||
@@ -280,7 +283,7 @@ impl Player {
|
||||
if self.gamemode != GameMode::Creative {
|
||||
return Err(InventoryError::PermissionError);
|
||||
}
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container);
|
||||
if let Some(Some(item)) = container.all_slots().get_mut(slot) {
|
||||
self.carried_item = Some(item.to_owned())
|
||||
}
|
||||
@@ -292,7 +295,7 @@ impl Player {
|
||||
opened_container: Option<&mut Box<dyn Container>>,
|
||||
slot: usize,
|
||||
) -> Result<(), InventoryError> {
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container);
|
||||
let mut slots = container.all_slots();
|
||||
|
||||
let Some(item) = slots.get_mut(slot) else {
|
||||
@@ -345,7 +348,7 @@ impl Player {
|
||||
MouseDragState::AddSlot(slot) => drag_handler.add_slot(container_id, player_id, slot),
|
||||
MouseDragState::End => {
|
||||
let mut container =
|
||||
OptionallyCombinedContainer::new(&mut self.inventory, opened_container);
|
||||
OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container);
|
||||
drag_handler.apply_drag(
|
||||
&mut self.carried_item,
|
||||
&mut container,
|
||||
@@ -356,10 +359,7 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_current_players_in_container(
|
||||
&mut self,
|
||||
server: &Server,
|
||||
) -> Vec<Arc<Mutex<Player>>> {
|
||||
async fn get_current_players_in_container(&self, server: &Server) -> Vec<Arc<Player>> {
|
||||
let player_ids = {
|
||||
let open_containers = server
|
||||
.open_containers
|
||||
@@ -378,13 +378,18 @@ impl Player {
|
||||
// TODO: Figure out better way to get only the players from player_ids
|
||||
// Also refactor out a better method to get individual advanced state ids
|
||||
|
||||
let world = self.entity.world.lock().await;
|
||||
let players = world
|
||||
let players = self
|
||||
.entity
|
||||
.lock()
|
||||
.unwrap()
|
||||
.world
|
||||
.current_players
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter_map(|(token, player)| {
|
||||
if *token != player_token {
|
||||
let entity_id = player.lock().unwrap().entity_id();
|
||||
let entity_id = player.entity_id();
|
||||
if player_ids.contains(&entity_id) {
|
||||
Some(player.clone())
|
||||
} else {
|
||||
@@ -405,13 +410,16 @@ impl Player {
|
||||
slot: Slot,
|
||||
) -> Result<(), InventoryError> {
|
||||
for player in self.get_current_players_in_container(server).await {
|
||||
let mut player = player.lock().unwrap();
|
||||
let total_opened_containers = player.inventory.total_opened_containers;
|
||||
|
||||
player.inventory.state_id += 1;
|
||||
// Returns previous value
|
||||
let i = player
|
||||
.inventory
|
||||
.state_id
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let packet = CSetContainerSlot::new(
|
||||
total_opened_containers as i8,
|
||||
player.inventory.state_id as i32,
|
||||
(i + 1) as i32,
|
||||
slot_index,
|
||||
&slot,
|
||||
);
|
||||
@@ -420,11 +428,10 @@ impl Player {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_whole_container_change(&mut self, server: &Server) -> Result<(), InventoryError> {
|
||||
async fn send_whole_container_change(&self, server: &Server) -> Result<(), InventoryError> {
|
||||
let players = self.get_current_players_in_container(server).await;
|
||||
|
||||
for player in players {
|
||||
let mut player = player.lock().unwrap();
|
||||
let container = player.get_open_container(server);
|
||||
let mut container = container.as_ref().map(|v| v.lock().unwrap());
|
||||
player.set_container_content(container.as_deref_mut());
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicI32},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -31,7 +34,7 @@ use thiserror::Error;
|
||||
|
||||
pub mod authentication;
|
||||
mod client_packet;
|
||||
mod container;
|
||||
// mod container;
|
||||
pub mod player_packet;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -62,95 +65,104 @@ impl Default for PlayerConfig {
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
pub gameprofile: Option<GameProfile>,
|
||||
pub gameprofile: Mutex<Option<GameProfile>>,
|
||||
|
||||
pub config: Option<PlayerConfig>,
|
||||
pub brand: Option<String>,
|
||||
pub config: Mutex<Option<PlayerConfig>>,
|
||||
pub brand: Mutex<Option<String>>,
|
||||
|
||||
pub protocol_version: i32,
|
||||
pub connection_state: ConnectionState,
|
||||
pub encryption: bool,
|
||||
pub closed: bool,
|
||||
pub protocol_version: AtomicI32,
|
||||
pub connection_state: Mutex<ConnectionState>,
|
||||
pub encryption: AtomicBool,
|
||||
pub closed: AtomicBool,
|
||||
pub token: Token,
|
||||
pub connection: TcpStream,
|
||||
pub address: SocketAddr,
|
||||
enc: PacketEncoder,
|
||||
dec: PacketDecoder,
|
||||
pub client_packets_queue: Vec<RawPacket>,
|
||||
pub connection: Arc<Mutex<TcpStream>>,
|
||||
pub address: Mutex<SocketAddr>,
|
||||
enc: Arc<Mutex<PacketEncoder>>,
|
||||
dec: Arc<Mutex<PacketDecoder>>,
|
||||
pub client_packets_queue: Arc<Mutex<Vec<RawPacket>>>,
|
||||
|
||||
pub make_player: bool,
|
||||
pub make_player: AtomicBool,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(token: Token, connection: TcpStream, address: SocketAddr) -> Self {
|
||||
Self {
|
||||
protocol_version: 0,
|
||||
gameprofile: None,
|
||||
config: None,
|
||||
brand: None,
|
||||
protocol_version: AtomicI32::new(0),
|
||||
gameprofile: Mutex::new(None),
|
||||
config: Mutex::new(None),
|
||||
brand: Mutex::new(None),
|
||||
token,
|
||||
address,
|
||||
connection_state: ConnectionState::HandShake,
|
||||
connection,
|
||||
enc: PacketEncoder::default(),
|
||||
dec: PacketDecoder::default(),
|
||||
encryption: true,
|
||||
closed: false,
|
||||
client_packets_queue: Vec::new(),
|
||||
make_player: false,
|
||||
address: Mutex::new(address),
|
||||
connection_state: Mutex::new(ConnectionState::HandShake),
|
||||
connection: Arc::new(Mutex::new(connection)),
|
||||
enc: Arc::new(Mutex::new(PacketEncoder::default())),
|
||||
dec: Arc::new(Mutex::new(PacketDecoder::default())),
|
||||
encryption: AtomicBool::new(false),
|
||||
closed: AtomicBool::new(false),
|
||||
client_packets_queue: Arc::new(Mutex::new(Vec::new())),
|
||||
make_player: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// adds a Incoming packet to the queue
|
||||
pub fn add_packet(&mut self, packet: RawPacket) {
|
||||
self.client_packets_queue.push(packet);
|
||||
pub fn add_packet(&self, packet: RawPacket) {
|
||||
let mut client_packets_queue = self.client_packets_queue.lock().unwrap();
|
||||
client_packets_queue.push(packet);
|
||||
}
|
||||
|
||||
/// enables encryption
|
||||
pub fn enable_encryption(
|
||||
&mut self,
|
||||
&self,
|
||||
shared_secret: &[u8], // decrypted
|
||||
) -> Result<(), EncryptionError> {
|
||||
self.encryption = true;
|
||||
self.encryption
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let crypt_key: [u8; 16] = shared_secret
|
||||
.try_into()
|
||||
.map_err(|_| EncryptionError::SharedWrongLength)?;
|
||||
self.dec.enable_encryption(&crypt_key);
|
||||
self.enc.enable_encryption(&crypt_key);
|
||||
self.dec.lock().unwrap().enable_encryption(&crypt_key);
|
||||
self.enc.lock().unwrap().enable_encryption(&crypt_key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Compression threshold, Compression level
|
||||
pub fn set_compression(&mut self, compression: Option<(u32, u32)>) {
|
||||
self.dec.set_compression(compression.map(|v| v.0));
|
||||
self.enc.set_compression(compression);
|
||||
pub fn set_compression(&self, compression: Option<(u32, u32)>) {
|
||||
self.dec
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_compression(compression.map(|v| v.0));
|
||||
self.enc.lock().unwrap().set_compression(compression);
|
||||
}
|
||||
|
||||
/// Send a Clientbound Packet to the Client
|
||||
pub fn send_packet<P: ClientPacket>(&mut self, packet: &P) {
|
||||
pub fn send_packet<P: ClientPacket>(&self, packet: &P) {
|
||||
// assert!(!self.closed);
|
||||
|
||||
self.enc
|
||||
.append_packet(packet)
|
||||
let mut enc = self.enc.lock().unwrap();
|
||||
enc.append_packet(packet)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
self.connection
|
||||
.write_all(&self.enc.take())
|
||||
.lock()
|
||||
.unwrap()
|
||||
.write_all(&enc.take())
|
||||
.map_err(|_| PacketError::ConnectionWrite)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
}
|
||||
|
||||
pub fn try_send_packet<P: ClientPacket>(&mut self, packet: &P) -> Result<(), PacketError> {
|
||||
pub fn try_send_packet<P: ClientPacket>(&self, packet: &P) -> Result<(), PacketError> {
|
||||
// assert!(!self.closed);
|
||||
|
||||
self.enc.append_packet(packet)?;
|
||||
let mut enc = self.enc.lock().unwrap();
|
||||
enc.append_packet(packet)?;
|
||||
self.connection
|
||||
.write_all(&self.enc.take())
|
||||
.lock()
|
||||
.unwrap()
|
||||
.write_all(&enc.take())
|
||||
.map_err(|_| PacketError::ConnectionWrite)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn process_packets(&mut self, server: &Arc<Server>) {
|
||||
while let Some(mut packet) = self.client_packets_queue.pop() {
|
||||
pub async fn process_packets(&self, server: &Arc<Server>) {
|
||||
while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() {
|
||||
match self.handle_packet(server, &mut packet).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
@@ -164,13 +176,16 @@ impl Client {
|
||||
|
||||
/// Handles an incoming decoded not Play state Packet
|
||||
pub async fn handle_packet(
|
||||
&mut self,
|
||||
&self,
|
||||
server: &Arc<Server>,
|
||||
packet: &mut RawPacket,
|
||||
) -> Result<(), DeserializerError> {
|
||||
// TODO: handle each packet's Error instead of calling .unwrap()
|
||||
let bytebuf = &mut packet.bytebuf;
|
||||
match self.connection_state {
|
||||
let locked_state = self.connection_state.lock().unwrap();
|
||||
let state = locked_state.clone();
|
||||
drop(locked_state);
|
||||
match state {
|
||||
pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 {
|
||||
SHandShake::PACKET_ID => {
|
||||
self.handle_handshake(server, SHandShake::read(bytebuf)?);
|
||||
@@ -268,15 +283,16 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the connection until our buffer of len 4096 is full, then decode
|
||||
/// Close connection when an error occurs
|
||||
pub async fn poll(&mut self, event: &Event) {
|
||||
/// Reads the connection until our buffer of len 4096 is full, then decode
|
||||
/// Close connection when an error occurs or when the Client closed the connection
|
||||
pub async fn poll(&self, event: &Event) {
|
||||
if event.is_readable() {
|
||||
let mut received_data = vec![0; 4096];
|
||||
let mut bytes_read = 0;
|
||||
// We can (maybe) read from the connection.
|
||||
loop {
|
||||
match self.connection.read(&mut received_data[bytes_read..]) {
|
||||
let connection = self.connection.clone();
|
||||
let mut connection = connection.lock().unwrap();
|
||||
match connection.read(&mut received_data[bytes_read..]) {
|
||||
Ok(0) => {
|
||||
// Reading 0 bytes means the other side has closed the
|
||||
// connection or is done writing, then so are we.
|
||||
@@ -297,9 +313,9 @@ impl Client {
|
||||
}
|
||||
|
||||
if bytes_read != 0 {
|
||||
self.dec.reserve(4096);
|
||||
self.dec.queue_slice(&received_data[..bytes_read]);
|
||||
match self.dec.decode() {
|
||||
let mut dec = self.dec.lock().unwrap();
|
||||
dec.queue_slice(&received_data[..bytes_read]);
|
||||
match dec.decode() {
|
||||
Ok(packet) => {
|
||||
if let Some(packet) = packet {
|
||||
self.add_packet(packet);
|
||||
@@ -307,15 +323,15 @@ impl Client {
|
||||
}
|
||||
Err(err) => self.kick(&err.to_string()),
|
||||
}
|
||||
self.dec.clear();
|
||||
dec.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kicks the Client with a reason depending on the connection state
|
||||
pub fn kick(&mut self, reason: &str) {
|
||||
pub fn kick(&self, reason: &str) {
|
||||
dbg!(reason);
|
||||
match self.connection_state {
|
||||
match *self.connection_state.lock().unwrap() {
|
||||
ConnectionState::Login => {
|
||||
self.try_send_packet(&CLoginDisconnect::new(
|
||||
&serde_json::to_string_pretty(&reason).unwrap_or("".into()),
|
||||
@@ -339,8 +355,9 @@ impl Client {
|
||||
}
|
||||
|
||||
/// You should prefer to use `kick` when you can
|
||||
pub fn close(&mut self) {
|
||||
self.closed = true;
|
||||
pub fn close(&self) {
|
||||
self.closed
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,16 +42,17 @@ fn modulus(a: f32, b: f32) -> f32 {
|
||||
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT ARE FORBIDDEN
|
||||
impl Player {
|
||||
pub fn handle_confirm_teleport(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
confirm_teleport: SConfirmTeleport,
|
||||
) {
|
||||
if let Some((id, position)) = self.awaiting_teleport.as_ref() {
|
||||
let mut awaiting_teleport = self.awaiting_teleport.lock().unwrap();
|
||||
if let Some((id, position)) = awaiting_teleport.as_ref() {
|
||||
if id == &confirm_teleport.teleport_id {
|
||||
// we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported
|
||||
self.entity.set_pos(position.x, position.y, position.z);
|
||||
|
||||
self.awaiting_teleport = None;
|
||||
*awaiting_teleport = None;
|
||||
} else {
|
||||
self.kick(TextComponent::text("Wrong teleport id"))
|
||||
}
|
||||
@@ -70,25 +71,27 @@ impl Player {
|
||||
pos.clamp(-2.0E7, 2.0E7)
|
||||
}
|
||||
|
||||
pub async fn handle_position(&mut self, _server: &Arc<Server>, position: SPlayerPosition) {
|
||||
pub async fn handle_position(&self, _server: &Arc<Server>, position: SPlayerPosition) {
|
||||
if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() {
|
||||
self.kick(TextComponent::text("Invalid movement"));
|
||||
return;
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
self.last_position = entity.pos;
|
||||
let entity = &self.entity;
|
||||
entity.set_pos(
|
||||
Self::clamp_horizontal(position.x),
|
||||
Self::clamp_vertical(position.feet_y),
|
||||
Self::clamp_horizontal(position.z),
|
||||
);
|
||||
entity.on_ground = position.ground;
|
||||
let on_ground = entity.on_ground;
|
||||
let mut last_position = self.last_position.lock().unwrap();
|
||||
let pos = entity.pos.lock().unwrap();
|
||||
*last_position = *pos;
|
||||
entity
|
||||
.on_ground
|
||||
.store(position.ground, std::sync::atomic::Ordering::Relaxed);
|
||||
let entity_id = entity.entity_id;
|
||||
let (x, y, z) = entity.pos.into();
|
||||
let (lastx, lasty, lastz) = self.last_position.into();
|
||||
let world = self.entity.world.clone();
|
||||
let world = world.lock().await;
|
||||
let (x, y, z) = (*pos).into();
|
||||
let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z);
|
||||
let world = entity.world.clone();
|
||||
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
@@ -103,21 +106,21 @@ impl Player {
|
||||
// return;
|
||||
// }
|
||||
// send new position to all other players
|
||||
world.broadcast_packet(
|
||||
world.broadcast_packet_expect(
|
||||
&[self.client.token],
|
||||
&CUpdateEntityPos::new(
|
||||
entity_id.into(),
|
||||
(x * 4096.0 - lastx * 4096.0) as i16,
|
||||
(y * 4096.0 - lasty * 4096.0) as i16,
|
||||
(z * 4096.0 - lastz * 4096.0) as i16,
|
||||
on_ground,
|
||||
position.ground,
|
||||
),
|
||||
);
|
||||
player_chunker::update_position(&world, self).await;
|
||||
player_chunker::update_position(entity, self).await;
|
||||
}
|
||||
|
||||
pub async fn handle_position_rotation(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
position_rotation: SPlayerPositionRotation,
|
||||
) {
|
||||
@@ -132,27 +135,32 @@ impl Player {
|
||||
self.kick(TextComponent::text("Invalid rotation"));
|
||||
return;
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
let entity = &self.entity;
|
||||
|
||||
self.last_position = entity.pos;
|
||||
entity.set_pos(
|
||||
Self::clamp_horizontal(position_rotation.x),
|
||||
Self::clamp_vertical(position_rotation.feet_y),
|
||||
Self::clamp_horizontal(position_rotation.z),
|
||||
);
|
||||
entity.on_ground = position_rotation.ground;
|
||||
entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0;
|
||||
entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
||||
let mut last_position = self.last_position.lock().unwrap();
|
||||
let pos = entity.pos.lock().unwrap();
|
||||
*last_position = *pos;
|
||||
entity.on_ground.store(
|
||||
position_rotation.ground,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
entity.set_rotation(
|
||||
wrap_degrees(position_rotation.yaw) % 360.0,
|
||||
wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0,
|
||||
);
|
||||
|
||||
let on_ground = entity.on_ground;
|
||||
let entity_id = entity.entity_id;
|
||||
let (x, y, z) = entity.pos.into();
|
||||
let (lastx, lasty, lastz) = self.last_position.into();
|
||||
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
||||
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
||||
let (x, y, z) = (*pos).into();
|
||||
let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z);
|
||||
let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0);
|
||||
let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0);
|
||||
// let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
|
||||
let world = self.entity.world.clone();
|
||||
let world = world.lock().await;
|
||||
let world = &entity.world;
|
||||
|
||||
// let delta = Vector3::new(x - lastx, y - lasty, z - lastz);
|
||||
// let velocity = self.velocity;
|
||||
@@ -168,7 +176,7 @@ impl Player {
|
||||
// }
|
||||
// send new position to all other players
|
||||
|
||||
world.broadcast_packet(
|
||||
world.broadcast_packet_expect(
|
||||
&[self.client.token],
|
||||
&CUpdateEntityPosRot::new(
|
||||
entity_id.into(),
|
||||
@@ -177,88 +185,94 @@ impl Player {
|
||||
(z * 4096.0 - lastz * 4096.0) as i16,
|
||||
yaw as u8,
|
||||
pitch as u8,
|
||||
on_ground,
|
||||
position_rotation.ground,
|
||||
),
|
||||
);
|
||||
world.broadcast_packet(
|
||||
world.broadcast_packet_expect(
|
||||
&[self.client.token],
|
||||
&CHeadRot::new(entity_id.into(), yaw as u8),
|
||||
);
|
||||
|
||||
player_chunker::update_position(&world, self).await;
|
||||
player_chunker::update_position(entity, self).await;
|
||||
}
|
||||
|
||||
pub async fn handle_rotation(&mut self, _server: &Arc<Server>, rotation: SPlayerRotation) {
|
||||
pub async fn handle_rotation(&self, _server: &Arc<Server>, rotation: SPlayerRotation) {
|
||||
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
|
||||
self.kick(TextComponent::text("Invalid rotation"));
|
||||
return;
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
entity.on_ground = rotation.ground;
|
||||
entity.yaw = wrap_degrees(rotation.yaw) % 360.0;
|
||||
entity.pitch = wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0;
|
||||
let entity = &self.entity;
|
||||
entity
|
||||
.on_ground
|
||||
.store(rotation.ground, std::sync::atomic::Ordering::Relaxed);
|
||||
entity.set_rotation(
|
||||
wrap_degrees(rotation.yaw) % 360.0,
|
||||
wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0,
|
||||
);
|
||||
// send new position to all other players
|
||||
let on_ground = entity.on_ground;
|
||||
let entity_id = entity.entity_id;
|
||||
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
||||
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
||||
let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0);
|
||||
let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0);
|
||||
// let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0);
|
||||
|
||||
let world = self.entity.world.lock().await;
|
||||
let packet = CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground);
|
||||
// self.client.send_packet(&packet);
|
||||
world.broadcast_packet(&[self.client.token], &packet);
|
||||
let world = &entity.world;
|
||||
let packet =
|
||||
CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, rotation.ground);
|
||||
world.broadcast_packet_expect(&[self.client.token], &packet);
|
||||
let packet = CHeadRot::new(entity_id.into(), yaw as u8);
|
||||
// self.client.send_packet(&packet);
|
||||
world.broadcast_packet(&[self.client.token], &packet);
|
||||
world.broadcast_packet_expect(&[self.client.token], &packet);
|
||||
}
|
||||
|
||||
pub fn handle_chat_command(&mut self, server: &Arc<Server>, command: SChatCommand) {
|
||||
pub fn handle_chat_command(&self, server: &Arc<Server>, command: SChatCommand) {
|
||||
let dispatcher = server.command_dispatcher.clone();
|
||||
dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command);
|
||||
}
|
||||
|
||||
pub fn handle_player_ground(&mut self, _server: &Arc<Server>, ground: SSetPlayerGround) {
|
||||
self.entity.on_ground = ground.on_ground;
|
||||
pub fn handle_player_ground(&self, _server: &Arc<Server>, ground: SSetPlayerGround) {
|
||||
self.entity
|
||||
.on_ground
|
||||
.store(ground.on_ground, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub async fn handle_player_command(&mut self, _server: &Arc<Server>, command: SPlayerCommand) {
|
||||
if command.entity_id != self.entity.entity_id.into() {
|
||||
pub async fn handle_player_command(&self, _server: &Arc<Server>, command: SPlayerCommand) {
|
||||
if command.entity_id != self.entity_id().into() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(action) = Action::from_i32(command.action.0) {
|
||||
let entity = &self.entity;
|
||||
match action {
|
||||
pumpkin_protocol::server::play::Action::StartSneaking => {
|
||||
if !self.entity.sneaking {
|
||||
self.entity.set_sneaking(&mut self.client, true).await
|
||||
if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
entity.set_sneaking(true).await
|
||||
}
|
||||
}
|
||||
pumpkin_protocol::server::play::Action::StopSneaking => {
|
||||
if self.entity.sneaking {
|
||||
self.entity.set_sneaking(&mut self.client, false).await
|
||||
if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
entity.set_sneaking(false).await
|
||||
}
|
||||
}
|
||||
pumpkin_protocol::server::play::Action::LeaveBed => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StartSprinting => {
|
||||
if !self.entity.sprinting {
|
||||
self.entity.set_sprinting(&mut self.client, true).await
|
||||
if !entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
entity.set_sprinting(true).await
|
||||
}
|
||||
}
|
||||
pumpkin_protocol::server::play::Action::StopSprinting => {
|
||||
if self.entity.sprinting {
|
||||
self.entity.set_sprinting(&mut self.client, false).await
|
||||
if entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
entity.set_sprinting(false).await
|
||||
}
|
||||
}
|
||||
pumpkin_protocol::server::play::Action::StartHorseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StopHorseJump => todo!(),
|
||||
pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(),
|
||||
pumpkin_protocol::server::play::Action::StartFlyingElytra => {
|
||||
let fall_flying = self.entity.check_fall_flying();
|
||||
if self.entity.fall_flying != fall_flying {
|
||||
self.entity
|
||||
.set_fall_flying(&mut self.client, fall_flying)
|
||||
.await;
|
||||
let fall_flying = entity.check_fall_flying();
|
||||
if entity
|
||||
.fall_flying
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
!= fall_flying
|
||||
{
|
||||
entity.set_fall_flying(fall_flying).await;
|
||||
}
|
||||
} // TODO
|
||||
}
|
||||
@@ -267,7 +281,7 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_swing_arm(&mut self, _server: &Arc<Server>, swing_arm: SSwingArm) {
|
||||
pub async fn handle_swing_arm(&self, _server: &Arc<Server>, swing_arm: SSwingArm) {
|
||||
match Hand::from_i32(swing_arm.hand.0) {
|
||||
Some(hand) => {
|
||||
let animation = match hand {
|
||||
@@ -275,8 +289,8 @@ impl Player {
|
||||
Hand::Off => Animation::SwingOffhand,
|
||||
};
|
||||
let id = self.entity_id();
|
||||
let world = self.entity.world.lock().await;
|
||||
world.broadcast_packet(
|
||||
let world = &self.entity.world;
|
||||
world.broadcast_packet_expect(
|
||||
&[self.client.token],
|
||||
&CEntityAnimation::new(id.into(), animation as u8),
|
||||
)
|
||||
@@ -287,7 +301,7 @@ impl Player {
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn handle_chat_message(&mut self, _server: &Arc<Server>, chat_message: SChatMessage) {
|
||||
pub async fn handle_chat_message(&self, _server: &Arc<Server>, chat_message: SChatMessage) {
|
||||
dbg!("got message");
|
||||
|
||||
let message = chat_message.message;
|
||||
@@ -299,24 +313,22 @@ impl Player {
|
||||
// TODO: filter message & validation
|
||||
let gameprofile = &self.gameprofile;
|
||||
|
||||
let world = self.entity.world.lock().await;
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CPlayerChatMessage::new(
|
||||
pumpkin_protocol::uuid::UUID(gameprofile.id),
|
||||
1.into(),
|
||||
chat_message.signature.as_deref(),
|
||||
&message,
|
||||
chat_message.timestamp,
|
||||
chat_message.salt,
|
||||
&[],
|
||||
Some(TextComponent::text(&message)),
|
||||
FilterType::PassThrough,
|
||||
1.into(),
|
||||
TextComponent::text(&gameprofile.name.clone()),
|
||||
None,
|
||||
),
|
||||
)
|
||||
let entity = &self.entity;
|
||||
let world = &entity.world;
|
||||
world.broadcast_packet_all(&CPlayerChatMessage::new(
|
||||
pumpkin_protocol::uuid::UUID(gameprofile.id),
|
||||
1.into(),
|
||||
chat_message.signature.as_deref(),
|
||||
&message,
|
||||
chat_message.timestamp,
|
||||
chat_message.salt,
|
||||
&[],
|
||||
Some(TextComponent::text(&message)),
|
||||
FilterType::PassThrough,
|
||||
1.into(),
|
||||
TextComponent::text(&gameprofile.name.clone()),
|
||||
None,
|
||||
))
|
||||
|
||||
/* server.broadcast_packet(
|
||||
self,
|
||||
@@ -330,7 +342,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub fn handle_client_information_play(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
client_information: SClientInformationPlay,
|
||||
) {
|
||||
@@ -338,7 +350,7 @@ impl Player {
|
||||
Hand::from_i32(client_information.main_hand.into()),
|
||||
ChatMode::from_i32(client_information.chat_mode.into()),
|
||||
) {
|
||||
self.config = PlayerConfig {
|
||||
*self.config.lock().unwrap() = PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
view_distance: client_information.view_distance,
|
||||
chat_mode,
|
||||
@@ -353,10 +365,11 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_interact(&mut self, _: &Arc<Server>, interact: SInteract) {
|
||||
pub async fn handle_interact(&self, _: &Arc<Server>, interact: SInteract) {
|
||||
let sneaking = interact.sneaking;
|
||||
if self.entity.sneaking != sneaking {
|
||||
self.entity.set_sneaking(&mut self.client, sneaking).await;
|
||||
let entity = &self.entity;
|
||||
if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking {
|
||||
entity.set_sneaking(sneaking).await;
|
||||
}
|
||||
match ActionType::from_i32(interact.typ.0) {
|
||||
Some(action) => match action {
|
||||
@@ -365,44 +378,42 @@ impl Player {
|
||||
// TODO: do validation and stuff
|
||||
let config = &ADVANCED_CONFIG.pvp;
|
||||
if config.enabled {
|
||||
let world = self.entity.world.clone();
|
||||
let world = world.lock().await;
|
||||
let world = entity.world.clone();
|
||||
let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId);
|
||||
if let Some(mut player) = attacked_player {
|
||||
let token = player.client.token;
|
||||
let velo = player.entity.velocity;
|
||||
if config.protect_creative && player.gamemode == GameMode::Creative {
|
||||
if let Some(player) = attacked_player {
|
||||
let victem_entity = &player.entity;
|
||||
if config.protect_creative
|
||||
&& *player.gamemode.lock().unwrap() == GameMode::Creative
|
||||
{
|
||||
return;
|
||||
}
|
||||
if config.knockback {
|
||||
let yaw = self.entity.yaw;
|
||||
let yaw = entity.yaw.lock().unwrap();
|
||||
let strength = 1.0;
|
||||
player.entity.knockback(
|
||||
let mut victem_velocity = victem_entity.velocity.lock().unwrap();
|
||||
let saved_velo = *victem_velocity;
|
||||
victem_entity.knockback(
|
||||
strength * 0.5,
|
||||
(yaw * (PI / 180.0)).sin() as f64,
|
||||
-(yaw * (PI / 180.0)).cos() as f64,
|
||||
(*yaw * (PI / 180.0)).sin() as f64,
|
||||
-(*yaw * (PI / 180.0)).cos() as f64,
|
||||
);
|
||||
let packet = &CEntityVelocity::new(
|
||||
&entity_id,
|
||||
velo.x as f32,
|
||||
velo.y as f32,
|
||||
velo.z as f32,
|
||||
victem_velocity.x as f32,
|
||||
victem_velocity.y as f32,
|
||||
victem_velocity.z as f32,
|
||||
);
|
||||
self.entity.velocity = self.entity.velocity.multiply(0.6, 1.0, 0.6);
|
||||
let mut velocity = entity.velocity.lock().unwrap();
|
||||
*velocity = velocity.multiply(0.6, 1.0, 0.6);
|
||||
|
||||
player.entity.velocity = velo;
|
||||
*victem_velocity = saved_velo;
|
||||
player.client.send_packet(packet);
|
||||
}
|
||||
if config.hurt_animation {
|
||||
// TODO
|
||||
// thats how we prevent borrow errors :c
|
||||
let packet = &CHurtAnimation::new(&entity_id, self.entity.yaw);
|
||||
self.client.send_packet(packet);
|
||||
player.client.send_packet(packet);
|
||||
world.broadcast_packet(
|
||||
&[self.client.token, token],
|
||||
&CHurtAnimation::new(&entity_id, 10.0),
|
||||
)
|
||||
world.broadcast_packet_all(&CHurtAnimation::new(
|
||||
&entity_id,
|
||||
*entity.yaw.lock().unwrap(),
|
||||
))
|
||||
}
|
||||
if config.swing {}
|
||||
} else {
|
||||
@@ -420,11 +431,7 @@ impl Player {
|
||||
None => self.kick(TextComponent::text("Invalid action type")),
|
||||
}
|
||||
}
|
||||
pub async fn handle_player_action(
|
||||
&mut self,
|
||||
_server: &Arc<Server>,
|
||||
player_action: SPlayerAction,
|
||||
) {
|
||||
pub async fn handle_player_action(&self, _server: &Arc<Server>, player_action: SPlayerAction) {
|
||||
match Status::from_i32(player_action.status.0) {
|
||||
Some(status) => match status {
|
||||
Status::StartedDigging => {
|
||||
@@ -434,20 +441,15 @@ impl Player {
|
||||
}
|
||||
// TODO: do validation
|
||||
// TODO: Config
|
||||
if self.gamemode == GameMode::Creative {
|
||||
if *self.gamemode.lock().unwrap() == GameMode::Creative {
|
||||
let location = player_action.location;
|
||||
// Block break & block break sound
|
||||
// TODO: currently this is always dirt replace it
|
||||
let world = self.entity.world.lock().await;
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CWorldEvent::new(2001, &location, 11, false),
|
||||
);
|
||||
let entity = &self.entity;
|
||||
let world = &entity.world;
|
||||
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
||||
// AIR
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CBlockUpdate::new(&location, 0.into()),
|
||||
);
|
||||
world.broadcast_packet_all(&CBlockUpdate::new(&location, 0.into()));
|
||||
}
|
||||
}
|
||||
Status::CancelledDigging => {
|
||||
@@ -455,7 +457,8 @@ impl Player {
|
||||
// TODO: maybe log?
|
||||
return;
|
||||
}
|
||||
self.current_block_destroy_stage = 0;
|
||||
self.current_block_destroy_stage
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
Status::FinishedDigging => {
|
||||
// TODO: do validation
|
||||
@@ -466,16 +469,11 @@ impl Player {
|
||||
}
|
||||
// Block break & block break sound
|
||||
// TODO: currently this is always dirt replace it
|
||||
let world = self.entity.world.lock().await;
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CWorldEvent::new(2001, &location, 11, false),
|
||||
);
|
||||
let entity = &self.entity;
|
||||
let world = &entity.world;
|
||||
world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false));
|
||||
// AIR
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CBlockUpdate::new(&location, 0.into()),
|
||||
);
|
||||
world.broadcast_packet_all(&CBlockUpdate::new(&location, 0.into()));
|
||||
// TODO: Send this every tick
|
||||
self.client
|
||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence));
|
||||
@@ -497,12 +495,12 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_play_ping_request(&mut self, _server: &Arc<Server>, request: SPlayPingRequest) {
|
||||
pub fn handle_play_ping_request(&self, _server: &Arc<Server>, request: SPlayPingRequest) {
|
||||
self.client
|
||||
.send_packet(&CPingResponse::new(request.payload));
|
||||
}
|
||||
|
||||
pub async fn handle_use_item_on(&mut self, _server: &Arc<Server>, use_item_on: SUseItemOn) {
|
||||
pub async fn handle_use_item_on(&self, _server: &Arc<Server>, use_item_on: SUseItemOn) {
|
||||
let location = use_item_on.location;
|
||||
|
||||
if !self.can_interact_with_block_at(&location, 1.0) {
|
||||
@@ -511,25 +509,23 @@ impl Player {
|
||||
}
|
||||
|
||||
if let Some(face) = BlockFace::from_i32(use_item_on.face.0) {
|
||||
if let Some(item) = self.inventory.held_item() {
|
||||
if let Some(item) = self.inventory.lock().unwrap().held_item() {
|
||||
let minecraft_id = global_registry::find_minecraft_id(
|
||||
global_registry::ITEM_REGISTRY,
|
||||
item.item_id,
|
||||
)
|
||||
.expect("All item ids are in the global registry");
|
||||
if let Ok(block_state_id) = BlockId::new(minecraft_id, None) {
|
||||
let world = self.entity.world.lock().await;
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()),
|
||||
);
|
||||
world.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CBlockUpdate::new(
|
||||
&WorldPosition(location.0 + face.to_offset()),
|
||||
block_state_id.get_id_mojang_repr().into(),
|
||||
),
|
||||
);
|
||||
let entity = &self.entity;
|
||||
let world = &entity.world;
|
||||
world.broadcast_packet_all(&CBlockUpdate::new(
|
||||
&location,
|
||||
block_state_id.get_id_mojang_repr().into(),
|
||||
));
|
||||
world.broadcast_packet_all(&CBlockUpdate::new(
|
||||
&WorldPosition(location.0 + face.to_offset()),
|
||||
block_state_id.get_id_mojang_repr().into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.client
|
||||
@@ -539,38 +535,46 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_use_item(&mut self, _server: &Arc<Server>, _use_item: SUseItem) {
|
||||
pub fn handle_use_item(&self, _server: &Arc<Server>, _use_item: SUseItem) {
|
||||
// TODO: handle packet correctly
|
||||
log::error!("An item was used(SUseItem), but the packet is not implemented yet");
|
||||
}
|
||||
|
||||
pub fn handle_set_held_item(&mut self, _server: &Arc<Server>, held: SSetHeldItem) {
|
||||
pub fn handle_set_held_item(&self, _server: &Arc<Server>, held: SSetHeldItem) {
|
||||
let slot = held.slot;
|
||||
if !(0..=8).contains(&slot) {
|
||||
self.kick(TextComponent::text("Invalid held slot"))
|
||||
}
|
||||
self.inventory.set_selected(slot as usize);
|
||||
self.inventory.lock().unwrap().set_selected(slot as usize);
|
||||
}
|
||||
|
||||
pub fn handle_set_creative_slot(
|
||||
&mut self,
|
||||
&self,
|
||||
_server: &Arc<Server>,
|
||||
packet: SSetCreativeSlot,
|
||||
) -> Result<(), InventoryError> {
|
||||
if self.gamemode != GameMode::Creative {
|
||||
if *self.gamemode.lock().unwrap() != GameMode::Creative {
|
||||
return Err(InventoryError::PermissionError);
|
||||
}
|
||||
self.inventory
|
||||
.set_slot(packet.slot as usize, packet.clicked_item.to_item(), false)
|
||||
self.inventory.lock().unwrap().set_slot(
|
||||
packet.slot as usize,
|
||||
packet.clicked_item.to_item(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
// 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: &Arc<Server>, packet: SCloseContainer) {
|
||||
pub fn handle_close_container(&self, server: &Arc<Server>, packet: SCloseContainer) {
|
||||
// window_id 0 represents both 9x1 Generic AND inventory here
|
||||
self.inventory.state_id = 0;
|
||||
if let Some(id) = self.open_container {
|
||||
self.inventory
|
||||
.lock()
|
||||
.unwrap()
|
||||
.state_id
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut open_container = self.open_container.lock().unwrap();
|
||||
if let Some(id) = *open_container {
|
||||
let mut open_containers = server
|
||||
.open_containers
|
||||
.write()
|
||||
@@ -578,7 +582,7 @@ impl Player {
|
||||
if let Some(container) = open_containers.get_mut(&id) {
|
||||
container.remove_player(self.entity_id())
|
||||
}
|
||||
self.open_container = None;
|
||||
*open_container = None;
|
||||
}
|
||||
let Some(_window_type) = WindowType::from_u8(packet.window_id) else {
|
||||
self.kick(TextComponent::text("Invalid window ID"));
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn parse_arg_player<'a>(
|
||||
src: &'a mut CommandSender,
|
||||
arg_name: &str,
|
||||
consumed_args: &ConsumedArgs,
|
||||
) -> Result<&'a mut crate::entity::player::Player, InvalidTreeError> {
|
||||
) -> Result<&'a crate::entity::player::Player, InvalidTreeError> {
|
||||
let s = consumed_args
|
||||
.get(arg_name)
|
||||
.ok_or(InvalidConsumptionError(None))?
|
||||
|
||||
@@ -11,7 +11,7 @@ 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();
|
||||
player.open_container = Some(0);
|
||||
*player.open_container.lock().unwrap() = Some(0);
|
||||
{
|
||||
let mut open_containers = server
|
||||
.open_containers
|
||||
@@ -27,7 +27,8 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
player.open_container(server, "minecraft:generic_9x3");
|
||||
// TODO
|
||||
// player.open_container(server, "minecraft:generic_9x3");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -65,15 +65,14 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
let gamemode = parse_arg_gamemode(args)?;
|
||||
|
||||
return if let Player(target) = sender {
|
||||
if target.gamemode == gamemode {
|
||||
if *target.gamemode.lock().unwrap() == gamemode {
|
||||
target.send_system_message(TextComponent::text(&format!(
|
||||
"You already in {:?} gamemode",
|
||||
gamemode
|
||||
)));
|
||||
} else {
|
||||
// TODO
|
||||
#[expect(clippy::let_underscore_future)]
|
||||
let _ = target.set_gamemode(gamemode);
|
||||
target.set_gamemode(gamemode);
|
||||
target.send_system_message(TextComponent::text(&format!(
|
||||
"Game mode was set to {:?}",
|
||||
gamemode
|
||||
@@ -90,15 +89,14 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
||||
let gamemode = parse_arg_gamemode(args)?;
|
||||
let target = parse_arg_player(sender, ARG_TARGET, args)?;
|
||||
|
||||
if target.gamemode == gamemode {
|
||||
if *target.gamemode.lock().unwrap() == gamemode {
|
||||
target.send_system_message(TextComponent::text(&format!(
|
||||
"You already in {:?} gamemode",
|
||||
gamemode
|
||||
)));
|
||||
} else {
|
||||
// TODO
|
||||
#[expect(clippy::let_underscore_future)]
|
||||
let _ = target.set_gamemode(gamemode);
|
||||
target.set_gamemode(gamemode);
|
||||
target.send_system_message(TextComponent::text(&format!(
|
||||
"Game mode was set to {:?}",
|
||||
gamemode
|
||||
|
||||
@@ -21,7 +21,7 @@ mod tree_format;
|
||||
pub enum CommandSender<'a> {
|
||||
Rcon(&'a mut Vec<String>),
|
||||
Console,
|
||||
Player(&'a mut Player),
|
||||
Player(&'a Player),
|
||||
}
|
||||
|
||||
impl<'a> CommandSender<'a> {
|
||||
@@ -49,7 +49,7 @@ impl<'a> CommandSender<'a> {
|
||||
CommandSender::Rcon(_) => true,
|
||||
}
|
||||
}
|
||||
pub fn as_mut_player(&mut self) -> Option<&mut Player> {
|
||||
pub fn as_mut_player(&mut self) -> Option<&Player> {
|
||||
match self {
|
||||
CommandSender::Player(player) => Some(player),
|
||||
CommandSender::Console => None,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{atomic::AtomicBool, Arc, Mutex};
|
||||
|
||||
use pumpkin_core::math::{
|
||||
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
|
||||
@@ -9,85 +9,97 @@ use pumpkin_protocol::{
|
||||
VarInt,
|
||||
};
|
||||
|
||||
use crate::{client::Client, world::World};
|
||||
use crate::world::World;
|
||||
|
||||
pub mod player;
|
||||
|
||||
pub struct Entity {
|
||||
pub entity_id: EntityId,
|
||||
pub entity_type: EntityType,
|
||||
pub world: Arc<tokio::sync::Mutex<World>>,
|
||||
pub world: Arc<World>,
|
||||
|
||||
pub pos: Vector3<f64>,
|
||||
pub block_pos: WorldPosition,
|
||||
pub chunk_pos: Vector2<i32>,
|
||||
pub pos: Mutex<Vector3<f64>>,
|
||||
pub block_pos: Mutex<WorldPosition>,
|
||||
pub chunk_pos: Mutex<Vector2<i32>>,
|
||||
|
||||
pub sneaking: bool,
|
||||
pub sprinting: bool,
|
||||
pub fall_flying: bool,
|
||||
pub velocity: Vector3<f64>,
|
||||
pub sneaking: AtomicBool,
|
||||
pub sprinting: AtomicBool,
|
||||
pub fall_flying: AtomicBool,
|
||||
pub velocity: Mutex<Vector3<f64>>,
|
||||
|
||||
// Should be not trusted
|
||||
pub on_ground: bool,
|
||||
pub on_ground: AtomicBool,
|
||||
|
||||
pub yaw: f32,
|
||||
pub head_yaw: f32,
|
||||
pub pitch: f32,
|
||||
pub yaw: Mutex<f32>,
|
||||
pub head_yaw: Mutex<f32>,
|
||||
pub pitch: Mutex<f32>,
|
||||
// TODO: Change this in diffrent poses
|
||||
pub standing_eye_height: f32,
|
||||
pub pose: EntityPose,
|
||||
pub pose: Mutex<EntityPose>,
|
||||
}
|
||||
|
||||
// TODO: Remove client: &mut Client, world: Arc<tokio::sync::Mutex<World>> bs
|
||||
impl Entity {
|
||||
pub fn new(
|
||||
entity_id: EntityId,
|
||||
world: Arc<tokio::sync::Mutex<World>>,
|
||||
world: Arc<World>,
|
||||
entity_type: EntityType,
|
||||
standing_eye_height: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
entity_type,
|
||||
on_ground: false,
|
||||
pos: Vector3::new(0.0, 0.0, 0.0),
|
||||
block_pos: WorldPosition(Vector3::new(0, 0, 0)),
|
||||
chunk_pos: Vector2::new(0, 0),
|
||||
sneaking: false,
|
||||
on_ground: AtomicBool::new(false),
|
||||
pos: Mutex::new(Vector3::new(0.0, 0.0, 0.0)),
|
||||
block_pos: Mutex::new(WorldPosition(Vector3::new(0, 0, 0))),
|
||||
chunk_pos: Mutex::new(Vector2::new(0, 0)),
|
||||
sneaking: AtomicBool::new(false),
|
||||
world,
|
||||
sprinting: false,
|
||||
fall_flying: false,
|
||||
yaw: 0.0,
|
||||
head_yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
velocity: Vector3::new(0.0, 0.0, 0.0),
|
||||
sprinting: AtomicBool::new(false),
|
||||
fall_flying: AtomicBool::new(false),
|
||||
yaw: Mutex::new(0.0),
|
||||
head_yaw: Mutex::new(0.0),
|
||||
pitch: Mutex::new(0.0),
|
||||
velocity: Mutex::new(Vector3::new(0.0, 0.0, 0.0)),
|
||||
standing_eye_height,
|
||||
pose: EntityPose::Standing,
|
||||
pose: Mutex::new(EntityPose::Standing),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pos(&mut self, x: f64, y: f64, z: f64) {
|
||||
if self.pos.x != x || self.pos.y != y || self.pos.z != z {
|
||||
self.pos = Vector3::new(x, y, z);
|
||||
pub fn set_pos(&self, x: f64, y: f64, z: f64) {
|
||||
let mut pos = self.pos.lock().unwrap();
|
||||
if pos.x != x || pos.y != y || pos.z != z {
|
||||
*pos = Vector3::new(x, y, z);
|
||||
let i = x.floor() as i32;
|
||||
let j = y.floor() as i32;
|
||||
let k = z.floor() as i32;
|
||||
|
||||
let block_pos = self.block_pos.0;
|
||||
if i != block_pos.x || j != block_pos.y || k != block_pos.z {
|
||||
self.block_pos = WorldPosition(Vector3::new(i, j, k));
|
||||
let mut block_pos = self.block_pos.lock().unwrap();
|
||||
let block_pos_vec = block_pos.0;
|
||||
if i != block_pos_vec.x || j != block_pos_vec.y || k != block_pos_vec.z {
|
||||
*block_pos = WorldPosition(Vector3::new(i, j, k));
|
||||
|
||||
if get_section_cord(i) != self.chunk_pos.x
|
||||
|| get_section_cord(k) != self.chunk_pos.z
|
||||
{
|
||||
self.chunk_pos =
|
||||
Vector2::new(get_section_cord(block_pos.x), get_section_cord(block_pos.z));
|
||||
let mut chunk_pos = self.chunk_pos.lock().unwrap();
|
||||
if get_section_cord(i) != chunk_pos.x || get_section_cord(k) != chunk_pos.z {
|
||||
*chunk_pos = Vector2::new(
|
||||
get_section_cord(block_pos_vec.x),
|
||||
get_section_cord(block_pos_vec.z),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn knockback(&mut self, strength: f64, x: f64, z: f64) {
|
||||
pub fn set_rotation(&self, yaw: f32, pitch: f32) {
|
||||
// TODO
|
||||
*self.yaw.lock().unwrap() = yaw;
|
||||
*self.pitch.lock().unwrap() = pitch
|
||||
}
|
||||
|
||||
pub async fn remove(&mut self) {
|
||||
self.world.remove_entity(self);
|
||||
}
|
||||
|
||||
pub fn knockback(&self, strength: f64, x: f64, z: f64) {
|
||||
// This has some vanilla magic
|
||||
let mut x = x;
|
||||
let mut z = z;
|
||||
@@ -97,23 +109,23 @@ impl Entity {
|
||||
}
|
||||
|
||||
let var8 = Vector3::new(x, 0.0, z).normalize() * strength;
|
||||
let var7 = self.velocity;
|
||||
self.velocity = Vector3::new(
|
||||
var7.x / 2.0 - var8.x,
|
||||
if self.on_ground {
|
||||
(var7.y / 2.0 + strength).min(0.4)
|
||||
let mut velocity = self.velocity.lock().unwrap();
|
||||
*velocity = Vector3::new(
|
||||
velocity.x / 2.0 - var8.x,
|
||||
if self.on_ground.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
(velocity.y / 2.0 + strength).min(0.4)
|
||||
} else {
|
||||
var7.y
|
||||
velocity.y
|
||||
},
|
||||
var7.z / 2.0 - var8.z,
|
||||
velocity.z / 2.0 - var8.z,
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn set_sneaking(&mut self, client: &mut Client, sneaking: bool) {
|
||||
assert!(self.sneaking != sneaking);
|
||||
self.sneaking = sneaking;
|
||||
self.set_flag(client, Self::SNEAKING_FLAG_INDEX, sneaking)
|
||||
.await;
|
||||
pub async fn set_sneaking(&self, sneaking: bool) {
|
||||
assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking);
|
||||
self.sneaking
|
||||
.store(sneaking, std::sync::atomic::Ordering::Relaxed);
|
||||
self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await;
|
||||
// if sneaking {
|
||||
// self.set_pose(EntityPose::Crouching).await;
|
||||
// } else {
|
||||
@@ -121,21 +133,22 @@ impl Entity {
|
||||
// }
|
||||
}
|
||||
|
||||
pub async fn set_sprinting(&mut self, client: &mut Client, sprinting: bool) {
|
||||
assert!(self.sprinting != sprinting);
|
||||
self.sprinting = sprinting;
|
||||
self.set_flag(client, Self::SPRINTING_FLAG_INDEX, sprinting)
|
||||
.await;
|
||||
pub async fn set_sprinting(&self, sprinting: bool) {
|
||||
assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting);
|
||||
self.sprinting
|
||||
.store(sprinting, std::sync::atomic::Ordering::Relaxed);
|
||||
self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await;
|
||||
}
|
||||
|
||||
pub fn check_fall_flying(&self) -> bool {
|
||||
!self.on_ground
|
||||
!self.on_ground.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub async fn set_fall_flying(&mut self, client: &mut Client, fall_flying: bool) {
|
||||
assert!(self.fall_flying != fall_flying);
|
||||
self.fall_flying = fall_flying;
|
||||
self.set_flag(client, Self::FALL_FLYING_FLAG_INDEX, fall_flying)
|
||||
pub async fn set_fall_flying(&self, fall_flying: bool) {
|
||||
assert!(self.fall_flying.load(std::sync::atomic::Ordering::Relaxed) != fall_flying);
|
||||
self.fall_flying
|
||||
.store(fall_flying, std::sync::atomic::Ordering::Relaxed);
|
||||
self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying)
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -146,7 +159,7 @@ impl Entity {
|
||||
pub const INVISIBLE_FLAG_INDEX: u32 = 5;
|
||||
pub const GLOWING_FLAG_INDEX: u32 = 6;
|
||||
pub const FALL_FLYING_FLAG_INDEX: u32 = 7;
|
||||
async fn set_flag(&mut self, client: &mut Client, index: u32, value: bool) {
|
||||
async fn set_flag(&self, index: u32, value: bool) {
|
||||
let mut b = 0i8;
|
||||
if value {
|
||||
b |= 1 << index;
|
||||
@@ -154,24 +167,16 @@ impl Entity {
|
||||
b &= !(1 << index);
|
||||
}
|
||||
let packet = CSetEntityMetadata::new(self.entity_id.into(), Metadata::new(0, 0.into(), b));
|
||||
client.send_packet(&packet);
|
||||
self.world
|
||||
.lock()
|
||||
.await
|
||||
.broadcast_packet(&[client.token], &packet);
|
||||
self.world.broadcast_packet_all(&packet);
|
||||
}
|
||||
|
||||
pub async fn set_pose(&mut self, client: &mut Client, pose: EntityPose) {
|
||||
self.pose = pose;
|
||||
let pose = self.pose as i32;
|
||||
pub async fn set_pose(&self, pose: EntityPose) {
|
||||
*self.pose.lock().unwrap() = pose;
|
||||
let pose = pose as i32;
|
||||
let packet = CSetEntityMetadata::<VarInt>::new(
|
||||
self.entity_id.into(),
|
||||
Metadata::new(6, 20.into(), (pose).into()),
|
||||
);
|
||||
client.send_packet(&packet);
|
||||
self.world
|
||||
.lock()
|
||||
.await
|
||||
.broadcast_packet(&[client.token], &packet)
|
||||
self.world.broadcast_packet_all(&packet)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{
|
||||
atomic::{AtomicI32, AtomicU8},
|
||||
Arc, Mutex,
|
||||
};
|
||||
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::ToPrimitive;
|
||||
@@ -62,39 +65,34 @@ pub struct Player {
|
||||
|
||||
pub gameprofile: GameProfile,
|
||||
pub client: Client,
|
||||
pub config: PlayerConfig,
|
||||
pub config: Mutex<PlayerConfig>,
|
||||
/// Current gamemode
|
||||
pub gamemode: GameMode,
|
||||
pub gamemode: Mutex<GameMode>,
|
||||
// TODO: prbly should put this into an Living Entitiy or something
|
||||
pub health: f32,
|
||||
pub food: i32,
|
||||
pub food_saturation: f32,
|
||||
pub inventory: PlayerInventory,
|
||||
pub open_container: Option<u64>,
|
||||
pub carried_item: Option<ItemStack>,
|
||||
pub health: Mutex<f32>,
|
||||
pub food: AtomicI32,
|
||||
pub food_saturation: Mutex<f32>,
|
||||
pub inventory: Mutex<PlayerInventory>,
|
||||
pub open_container: Mutex<Option<u64>>,
|
||||
pub carried_item: Mutex<Option<ItemStack>>,
|
||||
|
||||
/// send `send_abilties_update` when changed
|
||||
pub abilities: PlayerAbilities,
|
||||
pub last_position: Vector3<f64>,
|
||||
pub last_position: Mutex<Vector3<f64>>,
|
||||
|
||||
// TODO: This is currently unused, We have to calculate the block breaking speed our own and then break the block our own if its done
|
||||
pub current_block_destroy_stage: u8,
|
||||
pub current_block_destroy_stage: AtomicU8,
|
||||
|
||||
pub teleport_id_count: i32,
|
||||
pub teleport_id_count: AtomicI32,
|
||||
// Current awaiting teleport id and location, None if did not teleport
|
||||
pub awaiting_teleport: Option<(VarInt, Vector3<f64>)>,
|
||||
pub awaiting_teleport: Mutex<Option<(VarInt, Vector3<f64>)>>,
|
||||
|
||||
pub watched_section: Vector3<i32>,
|
||||
pub watched_section: Mutex<Vector3<i32>>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(
|
||||
client: Client,
|
||||
world: Arc<tokio::sync::Mutex<World>>,
|
||||
entity_id: EntityId,
|
||||
gamemode: GameMode,
|
||||
) -> Self {
|
||||
let gameprofile = match client.gameprofile.clone() {
|
||||
pub fn new(client: Client, world: Arc<World>, entity_id: EntityId, gamemode: GameMode) -> Self {
|
||||
let gameprofile = match client.gameprofile.lock().unwrap().clone() {
|
||||
Some(profile) => profile,
|
||||
None => {
|
||||
log::error!("No gameprofile?. Impossible");
|
||||
@@ -106,33 +104,32 @@ impl Player {
|
||||
}
|
||||
}
|
||||
};
|
||||
let config = client.config.clone().unwrap_or_default();
|
||||
let config = client.config.lock().unwrap().clone().unwrap_or_default();
|
||||
Self {
|
||||
entity: Entity::new(entity_id, world, EntityType::Player, 1.62),
|
||||
config,
|
||||
config: Mutex::new(config),
|
||||
gameprofile,
|
||||
client,
|
||||
awaiting_teleport: None,
|
||||
awaiting_teleport: Mutex::new(None),
|
||||
// TODO: Load this from previous instance
|
||||
health: 20.0,
|
||||
food: 20,
|
||||
food_saturation: 20.0,
|
||||
current_block_destroy_stage: 0,
|
||||
inventory: PlayerInventory::new(),
|
||||
open_container: None,
|
||||
carried_item: None,
|
||||
teleport_id_count: 0,
|
||||
health: Mutex::new(20.0),
|
||||
food: AtomicI32::new(20),
|
||||
food_saturation: Mutex::new(20.0),
|
||||
current_block_destroy_stage: AtomicU8::new(0),
|
||||
inventory: Mutex::new(PlayerInventory::new()),
|
||||
open_container: Mutex::new(None),
|
||||
carried_item: Mutex::new(None),
|
||||
teleport_id_count: AtomicI32::new(0),
|
||||
abilities: PlayerAbilities::default(),
|
||||
gamemode,
|
||||
watched_section: Vector3::new(0, 0, 0),
|
||||
last_position: Vector3::new(0.0, 0.0, 0.0),
|
||||
gamemode: Mutex::new(gamemode),
|
||||
watched_section: Mutex::new(Vector3::new(0, 0, 0)),
|
||||
last_position: Mutex::new(Vector3::new(0.0, 0.0, 0.0)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Put this into entity
|
||||
/// Removes the Player out of the current World
|
||||
pub async fn remove(&mut self) {
|
||||
self.entity.world.lock().await.remove_player(self);
|
||||
pub async fn remove(&self) {
|
||||
self.entity.world.remove_player(self);
|
||||
}
|
||||
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
@@ -162,17 +159,21 @@ impl Player {
|
||||
));
|
||||
}
|
||||
|
||||
pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) {
|
||||
pub fn teleport(&self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) {
|
||||
// this is the ultra special magic code used to create the teleport id
|
||||
self.teleport_id_count += 1;
|
||||
if self.teleport_id_count == i32::MAX {
|
||||
self.teleport_id_count = 0;
|
||||
// This returns the old value
|
||||
let i = self
|
||||
.teleport_id_count
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
if i + 2 == i32::MAX {
|
||||
self.teleport_id_count
|
||||
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
let entity = &mut self.entity;
|
||||
let teleport_id = i + 1;
|
||||
let entity = &self.entity;
|
||||
entity.set_pos(x, y, z);
|
||||
entity.yaw = yaw;
|
||||
entity.pitch = pitch;
|
||||
self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z)));
|
||||
entity.set_rotation(yaw, pitch);
|
||||
*self.awaiting_teleport.lock().unwrap() = Some((teleport_id.into(), Vector3::new(x, y, z)));
|
||||
self.client.send_packet(&CSyncPlayerPosition::new(
|
||||
x,
|
||||
y,
|
||||
@@ -180,12 +181,12 @@ impl Player {
|
||||
yaw,
|
||||
pitch,
|
||||
0,
|
||||
self.teleport_id_count.into(),
|
||||
teleport_id.into(),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn block_interaction_range(&self) -> f64 {
|
||||
if self.gamemode == GameMode::Creative {
|
||||
if *self.gamemode.lock().unwrap() == GameMode::Creative {
|
||||
5.0
|
||||
} else {
|
||||
4.5
|
||||
@@ -195,17 +196,22 @@ impl Player {
|
||||
pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool {
|
||||
let d = self.block_interaction_range() + additional_range;
|
||||
let box_pos = BoundingBox::from_block(pos);
|
||||
let entity_pos = self.entity.pos.lock().unwrap();
|
||||
let standing_eye_height = self.entity.standing_eye_height;
|
||||
box_pos.squared_magnitude(Vector3 {
|
||||
x: self.entity.pos.x,
|
||||
y: self.entity.pos.y + self.entity.standing_eye_height as f64,
|
||||
z: self.entity.pos.z,
|
||||
x: entity_pos.x,
|
||||
y: entity_pos.y + standing_eye_height as f64,
|
||||
z: entity_pos.z,
|
||||
}) < d * d
|
||||
}
|
||||
|
||||
/// Kicks the Client with a reason depending on the connection state
|
||||
pub fn kick(&mut self, reason: TextComponent) {
|
||||
assert!(self.client.connection_state == ConnectionState::Play);
|
||||
assert!(!self.client.closed);
|
||||
pub fn kick(&self, reason: TextComponent) {
|
||||
assert!(*self.client.connection_state.lock().unwrap() == ConnectionState::Play);
|
||||
assert!(!self
|
||||
.client
|
||||
.closed
|
||||
.load(std::sync::atomic::Ordering::Relaxed));
|
||||
|
||||
self.client
|
||||
.try_send_packet(&CPlayDisconnect::new(&reason))
|
||||
@@ -218,53 +224,45 @@ impl Player {
|
||||
self.client.close()
|
||||
}
|
||||
|
||||
pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) {
|
||||
self.health = health;
|
||||
self.food = food;
|
||||
self.food_saturation = food_saturation;
|
||||
pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) {
|
||||
*self.health.lock().unwrap() = health;
|
||||
self.food.store(food, std::sync::atomic::Ordering::Relaxed);
|
||||
*self.food_saturation.lock().unwrap() = food_saturation;
|
||||
}
|
||||
|
||||
pub async fn set_gamemode(&mut self, gamemode: GameMode) {
|
||||
pub fn set_gamemode(&self, gamemode: GameMode) {
|
||||
// We could send the same gamemode without problems. But why waste bandwidth ?
|
||||
let mut current_gamemode = self.gamemode.lock().unwrap();
|
||||
assert!(
|
||||
self.gamemode != gamemode,
|
||||
*current_gamemode != gamemode,
|
||||
"Setting the same gamemode as already is"
|
||||
);
|
||||
self.gamemode = gamemode;
|
||||
*current_gamemode = gamemode;
|
||||
// So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client
|
||||
// does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense
|
||||
|
||||
// TODO: fix this ugly mess :c, It gives me a liftime error when saving packet as a var
|
||||
self.client.send_packet(&CPlayerInfoUpdate::new(
|
||||
0x04,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: self.gameprofile.id,
|
||||
actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())],
|
||||
}],
|
||||
));
|
||||
self.entity.world.lock().await.broadcast_packet(
|
||||
&[self.client.token],
|
||||
&CPlayerInfoUpdate::new(
|
||||
self.entity
|
||||
.world
|
||||
.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
||||
0x04,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: self.gameprofile.id,
|
||||
actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())],
|
||||
actions: vec![PlayerAction::UpdateGameMode((gamemode as i32).into())],
|
||||
}],
|
||||
),
|
||||
);
|
||||
));
|
||||
self.client
|
||||
.send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap()));
|
||||
}
|
||||
|
||||
pub fn send_system_message(&mut self, text: TextComponent) {
|
||||
pub fn send_system_message(&self, text: TextComponent) {
|
||||
self.client
|
||||
.send_packet(&CSystemChatMessage::new(text, false));
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub async fn process_packets(&mut self, server: &Arc<Server>) {
|
||||
while let Some(mut packet) = self.client.client_packets_queue.pop() {
|
||||
pub async fn process_packets(&self, server: &Arc<Server>) {
|
||||
let mut packets = self.client.client_packets_queue.lock().unwrap();
|
||||
while let Some(mut packet) = packets.pop() {
|
||||
match self.handle_play_packet(server, &mut packet).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
@@ -277,7 +275,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn handle_play_packet(
|
||||
&mut self,
|
||||
&self,
|
||||
server: &Arc<Server>,
|
||||
packet: &mut RawPacket,
|
||||
) -> Result<(), DeserializerError> {
|
||||
@@ -362,9 +360,10 @@ impl Player {
|
||||
Ok(())
|
||||
}
|
||||
SClickContainer::PACKET_ID => {
|
||||
self.handle_click_container(server, SClickContainer::read(bytebuf)?)
|
||||
.await
|
||||
.unwrap();
|
||||
// TODO
|
||||
// self.handle_click_container(server, SClickContainer::read(bytebuf)?)
|
||||
// .await
|
||||
// .unwrap();
|
||||
Ok(())
|
||||
}
|
||||
SCloseContainer::PACKET_ID => {
|
||||
|
||||
@@ -7,7 +7,7 @@ use mio::net::TcpListener;
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self};
|
||||
use std::io::{self, Read};
|
||||
|
||||
use client::{interrupted, Client};
|
||||
use server::Server;
|
||||
@@ -24,7 +24,7 @@ pub mod util;
|
||||
pub mod world;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
use entity::player::Player;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
@@ -79,7 +79,7 @@ fn main() -> io::Result<()> {
|
||||
let rcon = ADVANCED_CONFIG.rcon.clone();
|
||||
|
||||
let mut clients: HashMap<Token, Client> = HashMap::new();
|
||||
let mut players: HashMap<Token, Arc<Mutex<Player>>> = HashMap::new();
|
||||
let mut players: HashMap<Token, Arc<Player>> = HashMap::new();
|
||||
|
||||
let server = Arc::new(Server::new());
|
||||
log::info!("Started Server took {}ms", time.elapsed().as_millis());
|
||||
@@ -159,17 +159,23 @@ fn main() -> io::Result<()> {
|
||||
token => {
|
||||
// Poll Players
|
||||
if let Some(player) = players.get_mut(&token) {
|
||||
let mut player = player.lock().unwrap();
|
||||
player.client.poll(event).await;
|
||||
if !player.client.closed {
|
||||
let closed = player
|
||||
.client
|
||||
.closed
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if !closed {
|
||||
player.process_packets(&server).await;
|
||||
}
|
||||
if player.client.closed {
|
||||
drop(player);
|
||||
if closed {
|
||||
if let Some(player) = players.remove(&token) {
|
||||
let mut player = player.lock().unwrap();
|
||||
dbg!("a");
|
||||
player.remove().await;
|
||||
poll.registry().deregister(&mut player.client.connection)?;
|
||||
dbg!("b");
|
||||
let connection = &mut player.client.connection.lock().unwrap();
|
||||
dbg!("c");
|
||||
|
||||
poll.registry().deregister(connection.by_ref())?;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -178,23 +184,29 @@ fn main() -> io::Result<()> {
|
||||
// Maybe received an event for a TCP connection.
|
||||
let (done, make_player) = if let Some(client) = clients.get_mut(&token) {
|
||||
client.poll(event).await;
|
||||
if !client.closed {
|
||||
let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if !closed {
|
||||
client.process_packets(&server).await;
|
||||
}
|
||||
(client.closed, client.make_player)
|
||||
(
|
||||
closed,
|
||||
client
|
||||
.make_player
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
)
|
||||
} else {
|
||||
// Sporadic events happen, we can safely ignore them.
|
||||
(false, false)
|
||||
};
|
||||
if done || make_player {
|
||||
if let Some(mut client) = clients.remove(&token) {
|
||||
if let Some(client) = clients.remove(&token) {
|
||||
if done {
|
||||
poll.registry().deregister(&mut client.connection)?;
|
||||
let connection = &mut client.connection.lock().unwrap();
|
||||
poll.registry().deregister(connection.by_ref())?;
|
||||
} else if make_player {
|
||||
let token = client.token;
|
||||
let (player, world) = server.add_player(token, client).await;
|
||||
players.insert(token, player.clone());
|
||||
let mut world = world.lock().await;
|
||||
world.spawn_player(&BASIC_CONFIG, player).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type HmacSha256 = Hmac<Sha256>;
|
||||
const MAX_SUPPORTED_FORWARDING_VERSION: i32 = 4;
|
||||
const PLAYER_INFO_CHANNEL: &str = "velocity:player_info";
|
||||
|
||||
pub fn velocity_login(client: &mut Client) {
|
||||
pub fn velocity_login(client: &Client) {
|
||||
let velocity_message_id: i32 = 0;
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
@@ -36,7 +36,7 @@ pub fn check_integrity(data: (&[u8], &[u8]), secret: String) -> bool {
|
||||
}
|
||||
|
||||
pub fn receive_plugin_response(
|
||||
client: &mut Client,
|
||||
client: &Client,
|
||||
config: VelocityConfig,
|
||||
response: SLoginPluginResponse,
|
||||
) {
|
||||
@@ -62,7 +62,7 @@ pub fn receive_plugin_response(
|
||||
}
|
||||
// TODO: no unwrap
|
||||
let addr: SocketAddr = buf.get_string().unwrap().parse().unwrap();
|
||||
client.address = addr;
|
||||
*client.address.lock().unwrap() = addr;
|
||||
todo!()
|
||||
} else {
|
||||
client.kick("This server requires you to connect with Velocity.")
|
||||
|
||||
@@ -45,7 +45,7 @@ pub struct Server {
|
||||
|
||||
pub command_dispatcher: Arc<CommandDispatcher<'static>>,
|
||||
|
||||
pub worlds: Vec<Arc<tokio::sync::Mutex<World>>>,
|
||||
pub worlds: Vec<Arc<World>>,
|
||||
pub status_response: StatusResponse,
|
||||
// We cache the json response here so we don't parse it every time someone makes a Status request.
|
||||
// Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves
|
||||
@@ -109,7 +109,7 @@ impl Server {
|
||||
drag_handler: DragHandler::new(),
|
||||
// 0 is invalid
|
||||
entity_id: 2.into(),
|
||||
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
|
||||
worlds: vec![Arc::new(world)],
|
||||
public_key,
|
||||
cached_server_brand,
|
||||
private_key,
|
||||
@@ -121,11 +121,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_player(
|
||||
&self,
|
||||
token: Token,
|
||||
client: Client,
|
||||
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
|
||||
pub async fn add_player(&self, token: Token, client: Client) -> (Arc<Player>, Arc<World>) {
|
||||
let entity_id = self.new_entity_id();
|
||||
let gamemode = match BASIC_CONFIG.default_gamemode {
|
||||
GameMode::Undefined => GameMode::Survival,
|
||||
@@ -135,13 +131,8 @@ impl Server {
|
||||
// TODO: select default from config
|
||||
let world = self.worlds[0].clone();
|
||||
|
||||
let player = Arc::new(Mutex::new(Player::new(
|
||||
client,
|
||||
world.clone(),
|
||||
entity_id,
|
||||
gamemode,
|
||||
)));
|
||||
world.lock().await.add_player(token, player.clone());
|
||||
let player = Arc::new(Player::new(client, world.clone(), entity_id, gamemode));
|
||||
world.add_player(token, player.clone());
|
||||
(player, world)
|
||||
}
|
||||
|
||||
@@ -161,12 +152,12 @@ impl Server {
|
||||
}
|
||||
|
||||
/// Sends a Packet to all Players in all worlds
|
||||
pub fn broadcast_packet_all<P>(&self, except: &[Token], packet: &P)
|
||||
pub fn broadcast_packet_all<P>(&self, packet: &P)
|
||||
where
|
||||
P: ClientPacket,
|
||||
{
|
||||
for world in &self.worlds {
|
||||
world.blocking_lock().broadcast_packet(except, packet)
|
||||
world.broadcast_packet_all(packet)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +174,7 @@ impl Server {
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn send_brand(&self, client: &mut Client) {
|
||||
pub fn send_brand(&self, client: &Client) {
|
||||
// send server brand
|
||||
client.send_packet(&CPluginMessage::new(
|
||||
"minecraft:brand",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::DerefMut,
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
pub mod player_chunker;
|
||||
@@ -22,11 +21,14 @@ use pumpkin_protocol::{
|
||||
use pumpkin_world::level::Level;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{client::Client, entity::player::Player};
|
||||
use crate::{
|
||||
client::Client,
|
||||
entity::{player::Player, Entity},
|
||||
};
|
||||
|
||||
pub struct World {
|
||||
pub level: Arc<Mutex<Level>>,
|
||||
pub current_players: HashMap<Token, Arc<Mutex<Player>>>,
|
||||
pub current_players: Arc<Mutex<HashMap<Token, Arc<Player>>>>,
|
||||
// entities, players...
|
||||
}
|
||||
|
||||
@@ -34,35 +36,36 @@ impl World {
|
||||
pub fn load(level: Level) -> Self {
|
||||
Self {
|
||||
level: Arc::new(Mutex::new(level)),
|
||||
current_players: HashMap::new(),
|
||||
current_players: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a Packet to all Players, Expect some players. Because we can't lock them twice
|
||||
pub fn broadcast_packet<P>(&self, except: &[Token], packet: &P)
|
||||
/// Sends a Packet to all Players in the World
|
||||
pub fn broadcast_packet_all<P>(&self, packet: &P)
|
||||
where
|
||||
P: ClientPacket,
|
||||
{
|
||||
for (_, player) in self
|
||||
.current_players
|
||||
.iter()
|
||||
.filter(|c| !except.contains(c.0))
|
||||
{
|
||||
let mut player = player.lock().unwrap();
|
||||
let current_players = self.current_players.lock().unwrap();
|
||||
for (_, player) in current_players.iter() {
|
||||
player.client.send_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn_player(
|
||||
&mut self,
|
||||
base_config: &BasicConfiguration,
|
||||
player: Arc<Mutex<Player>>,
|
||||
) {
|
||||
let mut player = player.lock().unwrap();
|
||||
let player = player.deref_mut();
|
||||
/// Sends a Packet to all Players in the World, Expect the Players given the the expect parameter
|
||||
pub fn broadcast_packet_expect<P>(&self, except: &[Token], packet: &P)
|
||||
where
|
||||
P: ClientPacket,
|
||||
{
|
||||
let current_players = self.current_players.lock().unwrap();
|
||||
for (_, player) in current_players.iter().filter(|c| !except.contains(c.0)) {
|
||||
player.client.send_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn_player(&self, base_config: &BasicConfiguration, player: Arc<Player>) {
|
||||
// This code follows the vanilla packet order
|
||||
let entity_id = player.entity_id();
|
||||
let gamemode = player.gamemode;
|
||||
let gamemode = player.gamemode.lock().unwrap();
|
||||
log::debug!("spawning player, entity id {}", entity_id);
|
||||
|
||||
// login packet for our new player
|
||||
@@ -104,7 +107,7 @@ impl World {
|
||||
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
|
||||
player.client.send_packet(&CPlayerInfoUpdate::new(
|
||||
self.broadcast_packet_all(&CPlayerInfoUpdate::new(
|
||||
0x01 | 0x08,
|
||||
&[pumpkin_protocol::client::play::Player {
|
||||
uuid: gameprofile.id,
|
||||
@@ -117,31 +120,16 @@ impl World {
|
||||
],
|
||||
}],
|
||||
));
|
||||
self.broadcast_packet(
|
||||
&[player.client.token],
|
||||
&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
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(c, _)| **c != player.client.token)
|
||||
{
|
||||
let playerr = playerr.as_ref().lock().unwrap();
|
||||
let gameprofile = &playerr.gameprofile;
|
||||
entries.push(pumpkin_protocol::client::play::Player {
|
||||
uuid: gameprofile.id,
|
||||
@@ -161,7 +149,7 @@ impl World {
|
||||
let gameprofile = &player.gameprofile;
|
||||
|
||||
// spawn player for every client
|
||||
self.broadcast_packet(
|
||||
self.broadcast_packet_expect(
|
||||
&[player.client.token],
|
||||
// TODO: add velo
|
||||
&CSpawnEntity::new(
|
||||
@@ -182,20 +170,26 @@ impl World {
|
||||
);
|
||||
// spawn players for our client
|
||||
let token = player.client.token;
|
||||
for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) {
|
||||
let existing_player = existing_player.as_ref().lock().unwrap();
|
||||
for (_, existing_player) in self
|
||||
.current_players
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.0 != &token)
|
||||
{
|
||||
let entity = &existing_player.entity;
|
||||
let pos = entity.pos.lock().unwrap();
|
||||
let gameprofile = &existing_player.gameprofile;
|
||||
player.client.send_packet(&CSpawnEntity::new(
|
||||
existing_player.entity_id().into(),
|
||||
UUID(gameprofile.id),
|
||||
(EntityType::Player as i32).into(),
|
||||
entity.pos.x,
|
||||
entity.pos.y,
|
||||
entity.pos.z,
|
||||
entity.yaw,
|
||||
entity.pitch,
|
||||
entity.head_yaw,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
*entity.yaw.lock().unwrap(),
|
||||
*entity.pitch.lock().unwrap(),
|
||||
*entity.head_yaw.lock().unwrap(),
|
||||
0.into(),
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -204,33 +198,27 @@ impl World {
|
||||
}
|
||||
// entity meta data
|
||||
// set skin parts
|
||||
if let Some(config) = player.client.config.as_ref() {
|
||||
if let Some(config) = player.client.config.lock().unwrap().as_ref() {
|
||||
let packet = CSetEntityMetadata::new(
|
||||
entity_id.into(),
|
||||
Metadata::new(17, VarInt(0), config.skin_parts),
|
||||
);
|
||||
player.client.send_packet(&packet);
|
||||
self.broadcast_packet(&[player.client.token], &packet)
|
||||
self.broadcast_packet_all(&packet)
|
||||
}
|
||||
|
||||
// Start waiting for level chunks
|
||||
player.client.send_packet(&CGameEvent::new(13, 0.0));
|
||||
|
||||
// Spawn in inital chunks
|
||||
player_chunker::player_join(self, player).await;
|
||||
player_chunker::player_join(self, player.clone()).await;
|
||||
}
|
||||
|
||||
async fn spawn_world_chunks(
|
||||
&self,
|
||||
client: &mut Client,
|
||||
chunks: Vec<Vector2<i32>>,
|
||||
distance: i32,
|
||||
) {
|
||||
async fn spawn_world_chunks(&self, client: &Client, chunks: Vec<Vector2<i32>>, distance: i32) {
|
||||
let inst = std::time::Instant::now();
|
||||
let (sender, mut chunk_receiver) = mpsc::channel(distance as usize);
|
||||
|
||||
let level = self.level.clone();
|
||||
let closed = client.closed;
|
||||
let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let chunks = Arc::new(chunks);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
level.lock().unwrap().fetch_chunks(&chunks, sender, closed)
|
||||
@@ -255,42 +243,47 @@ impl World {
|
||||
len / (1024 * 1024)
|
||||
);
|
||||
}
|
||||
if !client.closed {
|
||||
if !client.closed.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
client.send_packet(&CChunkData(&chunk_data));
|
||||
}
|
||||
}
|
||||
dbg!("DONE CHUNKS", inst.elapsed());
|
||||
}
|
||||
|
||||
/// TODO: This definitly should be in world
|
||||
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<MutexGuard<Player>> {
|
||||
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<Arc<Player>> {
|
||||
for (_, player) in self
|
||||
.current_players
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|c| c.0 != &from.client.token)
|
||||
{
|
||||
let player = player.lock().unwrap();
|
||||
if player.entity_id() == id {
|
||||
return Some(player);
|
||||
return Some(player.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn add_player(&mut self, token: Token, player: Arc<Mutex<Player>>) {
|
||||
self.current_players.insert(token, player);
|
||||
pub fn add_player(&self, token: Token, player: Arc<Player>) {
|
||||
self.current_players.lock().unwrap().insert(token, player);
|
||||
}
|
||||
|
||||
pub fn remove_player(&mut self, player: &Player) {
|
||||
self.current_players.remove(&player.client.token).unwrap();
|
||||
// despawn the player
|
||||
// todo: put this into the entitiy struct
|
||||
let id = player.entity_id();
|
||||
pub fn remove_player(&self, player: &Player) {
|
||||
self.current_players
|
||||
.lock()
|
||||
.unwrap()
|
||||
.remove(&player.client.token)
|
||||
.unwrap();
|
||||
let uuid = player.gameprofile.id;
|
||||
self.broadcast_packet(
|
||||
self.broadcast_packet_expect(
|
||||
&[player.client.token],
|
||||
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
||||
);
|
||||
self.broadcast_packet(&[player.client.token], &CRemoveEntities::new(&[id.into()]))
|
||||
self.remove_entity(&player.entity);
|
||||
}
|
||||
|
||||
pub fn remove_entity(&self, entity: &Entity) {
|
||||
self.broadcast_packet_all(&CRemoveEntities::new(&[entity.entity_id.into()]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_config::BASIC_CONFIG;
|
||||
use pumpkin_core::math::{
|
||||
get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3,
|
||||
@@ -5,29 +7,32 @@ use pumpkin_core::math::{
|
||||
use pumpkin_protocol::client::play::{CCenterChunk, CUnloadChunk};
|
||||
use pumpkin_world::cylindrical_chunk_iterator::Cylindrical;
|
||||
|
||||
use crate::entity::player::Player;
|
||||
use crate::entity::{player::Player, Entity};
|
||||
|
||||
use super::World;
|
||||
|
||||
fn get_view_distance(player: &Player) -> i8 {
|
||||
player
|
||||
.config
|
||||
.lock()
|
||||
.unwrap()
|
||||
.view_distance
|
||||
.clamp(2, BASIC_CONFIG.view_distance as i8)
|
||||
}
|
||||
|
||||
pub async fn player_join(world: &World, player: &mut Player) {
|
||||
let new_watched = chunk_section_from_pos(&player.entity.block_pos);
|
||||
player.watched_section = new_watched;
|
||||
let chunk_pos = player.entity.chunk_pos;
|
||||
pub async fn player_join(world: &World, player: Arc<Player>) {
|
||||
let new_watched = chunk_section_from_pos(&player.entity.block_pos.lock().unwrap());
|
||||
let mut watched_section = player.watched_section.lock().unwrap();
|
||||
*watched_section = new_watched;
|
||||
let chunk_pos = player.entity.chunk_pos.lock().unwrap();
|
||||
player.client.send_packet(&CCenterChunk {
|
||||
chunk_x: chunk_pos.x.into(),
|
||||
chunk_z: chunk_pos.z.into(),
|
||||
});
|
||||
let view_distance = get_view_distance(player) as i32;
|
||||
let view_distance = get_view_distance(&player) as i32;
|
||||
dbg!(view_distance);
|
||||
let old_cylindrical = Cylindrical::new(
|
||||
Vector2::new(player.watched_section.x, player.watched_section.z),
|
||||
Vector2::new(watched_section.x, watched_section.z),
|
||||
view_distance,
|
||||
);
|
||||
let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance);
|
||||
@@ -47,16 +52,16 @@ pub async fn player_join(world: &World, player: &mut Player) {
|
||||
);
|
||||
if !loading_chunks.is_empty() {
|
||||
world
|
||||
.spawn_world_chunks(&mut player.client, loading_chunks, view_distance)
|
||||
.spawn_world_chunks(&player.client, loading_chunks, view_distance)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_position(world: &World, player: &mut Player) {
|
||||
let current_watched = player.watched_section;
|
||||
let new_watched = chunk_section_from_pos(&player.entity.block_pos);
|
||||
if current_watched != new_watched {
|
||||
let chunk_pos = player.entity.chunk_pos;
|
||||
pub async fn update_position(entity: &Entity, player: &Player) {
|
||||
let mut current_watched = player.watched_section.lock().unwrap();
|
||||
let new_watched = chunk_section_from_pos(&entity.block_pos.lock().unwrap());
|
||||
if *current_watched != new_watched {
|
||||
let chunk_pos = entity.chunk_pos.lock().unwrap();
|
||||
player.client.send_packet(&CCenterChunk {
|
||||
chunk_x: chunk_pos.x.into(),
|
||||
chunk_z: chunk_pos.z.into(),
|
||||
@@ -64,12 +69,12 @@ pub async fn update_position(world: &World, player: &mut Player) {
|
||||
|
||||
let view_distance = get_view_distance(player) as i32;
|
||||
let old_cylindrical = Cylindrical::new(
|
||||
Vector2::new(player.watched_section.x, player.watched_section.z),
|
||||
Vector2::new(current_watched.x, current_watched.z),
|
||||
view_distance,
|
||||
);
|
||||
let new_cylindrical =
|
||||
Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance);
|
||||
player.watched_section = new_watched;
|
||||
*current_watched = new_watched;
|
||||
let mut loading_chunks = Vec::new();
|
||||
Cylindrical::for_each_changed_chunk(
|
||||
old_cylindrical,
|
||||
@@ -85,8 +90,9 @@ pub async fn update_position(world: &World, player: &mut Player) {
|
||||
false,
|
||||
);
|
||||
if !loading_chunks.is_empty() {
|
||||
world
|
||||
.spawn_world_chunks(&mut player.client, loading_chunks, view_distance)
|
||||
entity
|
||||
.world
|
||||
.spawn_world_chunks(&player.client, loading_chunks, view_distance)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user