This commit is contained in:
parent
21e7f1418a
commit
55219f870f
@ -35,8 +35,10 @@ pub mod resource_packs_info;
|
||||
pub mod scoreboard;
|
||||
pub mod set_actor_data;
|
||||
pub mod set_actor_motion;
|
||||
pub mod set_difficulty;
|
||||
pub mod set_health;
|
||||
pub mod set_player_gamemode;
|
||||
pub mod set_spawn_position;
|
||||
pub mod set_time;
|
||||
pub mod set_title;
|
||||
pub mod show_credits;
|
||||
@ -84,8 +86,10 @@ pub use resource_packs_info::*;
|
||||
pub use scoreboard::*;
|
||||
pub use set_actor_data::*;
|
||||
pub use set_actor_motion::*;
|
||||
pub use set_difficulty::*;
|
||||
pub use set_health::*;
|
||||
pub use set_player_gamemode::*;
|
||||
pub use set_spawn_position::*;
|
||||
pub use set_time::*;
|
||||
pub use set_title::*;
|
||||
pub use show_credits::*;
|
||||
|
||||
18
pumpkin-protocol/src/bedrock/client/set_difficulty.rs
Normal file
18
pumpkin-protocol/src/bedrock/client/set_difficulty.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use pumpkin_macros::packet;
|
||||
|
||||
use crate::{codec::var_uint::VarUInt, serial::PacketWrite};
|
||||
|
||||
#[derive(PacketWrite)]
|
||||
#[packet(60)]
|
||||
pub struct CSetDifficulty {
|
||||
pub difficulty: VarUInt,
|
||||
}
|
||||
|
||||
impl CSetDifficulty {
|
||||
#[must_use]
|
||||
pub const fn new(difficulty: u32) -> Self {
|
||||
Self {
|
||||
difficulty: VarUInt(difficulty),
|
||||
}
|
||||
}
|
||||
}
|
||||
42
pumpkin-protocol/src/bedrock/client/set_spawn_position.rs
Normal file
42
pumpkin-protocol/src/bedrock/client/set_spawn_position.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use std::io::{Error, Write};
|
||||
|
||||
use pumpkin_macros::packet;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use crate::{codec::var_int::VarInt, serial::PacketWrite};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[packet(43)]
|
||||
pub struct CSetSpawnPosition {
|
||||
pub spawn_type: VarInt,
|
||||
pub position: BlockPos,
|
||||
pub dimension: VarInt,
|
||||
pub spawn_position: BlockPos,
|
||||
}
|
||||
|
||||
impl CSetSpawnPosition {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
spawn_type: i32,
|
||||
position: BlockPos,
|
||||
dimension: i32,
|
||||
spawn_position: BlockPos,
|
||||
) -> Self {
|
||||
Self {
|
||||
spawn_type: VarInt(spawn_type),
|
||||
position,
|
||||
dimension: VarInt(dimension),
|
||||
spawn_position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketWrite for CSetSpawnPosition {
|
||||
fn write<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
|
||||
self.spawn_type.write(writer)?;
|
||||
self.position.write(writer)?;
|
||||
self.dimension.write(writer)?;
|
||||
self.spawn_position.write(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,10 @@ struct RawWeightedPoolElement {
|
||||
enum RawPoolElement {
|
||||
#[serde(rename = "minecraft:empty_pool_element")]
|
||||
Empty,
|
||||
#[serde(rename = "minecraft:single_pool_element")]
|
||||
#[serde(
|
||||
rename = "minecraft:single_pool_element",
|
||||
alias = "minecraft:legacy_single_pool_element"
|
||||
)]
|
||||
Single {
|
||||
location: String,
|
||||
processors: RawProcessorList,
|
||||
|
||||
@ -177,9 +177,17 @@ impl StructureTemplate {
|
||||
}
|
||||
|
||||
fn parse_palette(compound: &NbtCompound) -> Result<Vec<PaletteEntry>, TemplateError> {
|
||||
let palette_list = compound
|
||||
.get_list("palette")
|
||||
.ok_or(TemplateError::MissingField("palette"))?;
|
||||
let palette_list = if let Some(list) = compound.get_list("palette") {
|
||||
list
|
||||
} else if let Some(palettes) = compound.get_list("palettes") {
|
||||
if let Some(NbtTag::List(list)) = palettes.first() {
|
||||
list
|
||||
} else {
|
||||
return Err(TemplateError::MissingField("palette"));
|
||||
}
|
||||
} else {
|
||||
return Err(TemplateError::MissingField("palette"));
|
||||
};
|
||||
|
||||
let mut palette = Vec::with_capacity(palette_list.len());
|
||||
|
||||
|
||||
@ -90,8 +90,9 @@ impl FromStr for EntityFilter {
|
||||
|
||||
match key {
|
||||
"type" => {
|
||||
let name = value.strip_prefix("minecraft:").unwrap_or(value);
|
||||
let entity_type =
|
||||
EntityType::from_name(value).ok_or(format!("Invalid entity type {value}"))?;
|
||||
EntityType::from_name(name).ok_or(format!("Invalid entity type {value}"))?;
|
||||
Ok(Self::Type(if negate {
|
||||
ValueCondition::NotEquals(entity_type)
|
||||
} else {
|
||||
@ -496,4 +497,13 @@ mod test {
|
||||
assert_eq!(translate_key, translation::java::ARGUMENT_PLAYER_ENTITIES);
|
||||
assert_eq!(error.context.expect("Error should have context").cursor, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_and_inverted_type() {
|
||||
let s = "@e[type=!player,type=minecraft:zombie]"
|
||||
.parse::<TargetSelector>()
|
||||
.expect("should parse");
|
||||
assert!(s.includes_entities());
|
||||
assert_eq!(s.conditions.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,6 +382,8 @@ pub enum EntitySelectorPredicate {
|
||||
BoundingBox(BoundingBox),
|
||||
/// A predicate to check whether an entity is within a specified range from some position.
|
||||
Distance(DoubleBounds, Vector3<f64>),
|
||||
/// A predicate to check the entity type. This check can also be inverted.
|
||||
EntityType(&'static EntityType, bool),
|
||||
|
||||
/// Used to combine sub-predicates.
|
||||
AllOf(Vec<Self>),
|
||||
@ -437,6 +439,10 @@ impl EntitySelectorPredicate {
|
||||
Self::Distance(bounds, pos) => {
|
||||
bounds.matches_square(entity.get_entity().pos.load().squared_distance_to_vec(pos))
|
||||
}
|
||||
Self::EntityType(expected_type, invert) => {
|
||||
let actual_type = entity.get_entity().entity_type;
|
||||
(actual_type.id == expected_type.id) ^ invert
|
||||
}
|
||||
Self::AllOf(predicates) => predicates.iter().all(|predicate| predicate.test(entity)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ use crate::command::errors::command_syntax_error::CommandSyntaxError;
|
||||
use crate::command::errors::error_types::CommandErrorType;
|
||||
use crate::command::string_reader::StringReader;
|
||||
use crate::command::suggestion::suggestions::SuggestionsBuilder;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::math::bounds::{DoubleBounds, FloatDegreeBounds, IntBounds};
|
||||
@ -41,6 +42,10 @@ pub const GAMEMODE_INVALID_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::n
|
||||
translation::java::ARGUMENT_ENTITY_OPTIONS_MODE_INVALID,
|
||||
translation::java::ARGUMENT_ENTITY_OPTIONS_MODE_INVALID,
|
||||
);
|
||||
pub const TYPE_INVALID_ERROR_TYPE: CommandErrorType<1> = CommandErrorType::new(
|
||||
translation::java::ARGUMENT_ENTITY_OPTIONS_TYPE_INVALID,
|
||||
translation::java::ARGUMENT_ENTITY_OPTIONS_TYPE_INVALID,
|
||||
);
|
||||
|
||||
/// Options to customize an [`EntitySelectorParser`].
|
||||
///
|
||||
@ -187,6 +192,7 @@ impl EntitySelectorOption {
|
||||
/// Any required fields will be parsed by this method using [`StringReader`]
|
||||
/// methods, and any required predicates will be added.
|
||||
/// Any found errors will be returned.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn modify_parser(
|
||||
self,
|
||||
parser: &mut EntitySelectorParser,
|
||||
@ -278,6 +284,33 @@ impl EntitySelectorOption {
|
||||
.create(parser.reader, TextComponent::text(string)))
|
||||
}
|
||||
}
|
||||
Self::Type => {
|
||||
let start = parser.reader.cursor();
|
||||
let invert = parser.consume_inverted_start();
|
||||
if parser.has_flag(Flags::ENTITY_TYPE_INVERTED) && !invert {
|
||||
parser.reader.set_cursor(start);
|
||||
return Err(self.inapplicable_error(parser.reader));
|
||||
}
|
||||
let mut string = parser.reader.read_unquoted_string();
|
||||
if let Some(stripped) = string.strip_prefix("minecraft:") {
|
||||
string = stripped.to_string();
|
||||
}
|
||||
if let Some(entity_type) = EntityType::from_name(&string) {
|
||||
if entity_type.id == EntityType::PLAYER.id && !invert {
|
||||
parser.limit_to_players();
|
||||
}
|
||||
parser.add_predicate(EntitySelectorPredicate::EntityType(entity_type, invert));
|
||||
if invert {
|
||||
parser.set_flag(Flags::ENTITY_TYPE_INVERTED, true);
|
||||
} else {
|
||||
parser.entity_type = Some(entity_type);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
parser.reader.set_cursor(start);
|
||||
Err(TYPE_INVALID_ERROR_TYPE.create(parser.reader, TextComponent::text(string)))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!("Unimplemented entity selector option: {:?}", self);
|
||||
Err(UNKNOWN_OPTION_ERROR_TYPE.create_without_context(self.name_component()))
|
||||
|
||||
@ -59,7 +59,7 @@ impl CommandExecutor for DifficultySetExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
server.set_difficulty(difficulty, true);
|
||||
server.set_difficulty(difficulty, true).await;
|
||||
|
||||
context
|
||||
.source
|
||||
|
||||
@ -1298,13 +1298,26 @@ impl Player {
|
||||
return false;
|
||||
}
|
||||
|
||||
let bedrock_dimension = match dimension.minecraft_name {
|
||||
"minecraft:the_nether" => 1,
|
||||
"minecraft:the_end" => 2,
|
||||
_ => 0,
|
||||
};
|
||||
self.client
|
||||
.send_packet_now(&CPlayerSpawnPosition::new(
|
||||
block_pos,
|
||||
yaw,
|
||||
pitch,
|
||||
dimension.minecraft_name.to_owned(),
|
||||
))
|
||||
.send_packet_now_editioned(
|
||||
&CPlayerSpawnPosition::new(
|
||||
block_pos,
|
||||
yaw,
|
||||
pitch,
|
||||
dimension.minecraft_name.to_owned(),
|
||||
),
|
||||
&pumpkin_protocol::bedrock::client::CSetSpawnPosition::new(
|
||||
0, // Player spawn
|
||||
block_pos,
|
||||
bedrock_dimension,
|
||||
block_pos,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
*self.respawn_point.lock().await = Some(RespawnPoint {
|
||||
@ -1739,22 +1752,41 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn show_title(&self, text: &TextComponent, mode: &TitleMode) {
|
||||
match self.client.as_ref() {
|
||||
ClientPlatform::Java(client) => match mode {
|
||||
TitleMode::Title => client.enqueue_packet(&CTitleText::new(text)).await,
|
||||
TitleMode::SubTitle => client.enqueue_packet(&CSubtitle::new(text)).await,
|
||||
TitleMode::ActionBar => client.enqueue_packet(&CActionBar::new(text)).await,
|
||||
},
|
||||
ClientPlatform::Bedrock(client) => {
|
||||
let action_type = match mode {
|
||||
TitleMode::Title => 2,
|
||||
TitleMode::SubTitle => 3,
|
||||
TitleMode::ActionBar => 4,
|
||||
};
|
||||
client
|
||||
.send_game_packet(
|
||||
match mode {
|
||||
TitleMode::Title => {
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CTitleText::new(text),
|
||||
&pumpkin_protocol::bedrock::client::set_title::CSetTitle::new(
|
||||
action_type,
|
||||
2,
|
||||
text.clone().get_text(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
TitleMode::SubTitle => {
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CSubtitle::new(text),
|
||||
&pumpkin_protocol::bedrock::client::set_title::CSetTitle::new(
|
||||
3,
|
||||
text.clone().get_text(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
TitleMode::ActionBar => {
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CActionBar::new(text),
|
||||
&pumpkin_protocol::bedrock::client::set_title::CSetTitle::new(
|
||||
4,
|
||||
text.clone().get_text(),
|
||||
0,
|
||||
0,
|
||||
@ -2384,10 +2416,12 @@ impl Player {
|
||||
let world = self.world();
|
||||
let level_info = world.level_info.load();
|
||||
self.client
|
||||
.enqueue_packet(&CChangeDifficulty::new(
|
||||
level_info.difficulty as u8,
|
||||
level_info.difficulty_locked,
|
||||
))
|
||||
.enqueue_packet_editioned(
|
||||
&CChangeDifficulty::new(level_info.difficulty as u8, level_info.difficulty_locked),
|
||||
&pumpkin_protocol::bedrock::client::CSetDifficulty::new(
|
||||
level_info.difficulty as u32,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
@ -2412,24 +2446,12 @@ impl Player {
|
||||
/// Sends the world time to only this player.
|
||||
pub async fn send_time(&self, world: &World) {
|
||||
let l_world = world.level_time.lock().await;
|
||||
match self.client.as_ref() {
|
||||
ClientPlatform::Java(java_client) => {
|
||||
java_client
|
||||
.enqueue_packet(&CUpdateTime::new(
|
||||
l_world.world_age,
|
||||
l_world.time_of_day,
|
||||
true,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(bedrock_client) => {
|
||||
bedrock_client
|
||||
.send_game_packet(&CSetTime {
|
||||
time: VarInt(l_world.query_daytime() as _),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CUpdateTime::new(l_world.world_age, l_world.time_of_day, true),
|
||||
&CSetTime::new(l_world.query_daytime() as _),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn unload_watched_chunks(&self, world: &World) {
|
||||
@ -2652,26 +2674,18 @@ impl Player {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.client.as_ref() {
|
||||
ClientPlatform::Java(client) => {
|
||||
client
|
||||
.enqueue_packet(&CSetHealth::new(
|
||||
self.living_entity.health.load(),
|
||||
self.hunger_manager.level.load().into(),
|
||||
self.hunger_manager.saturation.load(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
ClientPlatform::Bedrock(client) => {
|
||||
client
|
||||
.send_game_packet(
|
||||
&pumpkin_protocol::bedrock::client::set_health::CSetHealth::new(
|
||||
self.living_entity.health.load() as i32,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CSetHealth::new(
|
||||
self.living_entity.health.load(),
|
||||
self.hunger_manager.level.load().into(),
|
||||
self.hunger_manager.saturation.load(),
|
||||
),
|
||||
&pumpkin_protocol::bedrock::client::set_health::CSetHealth::new(
|
||||
self.living_entity.health.load() as i32,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn tick_health(&self) {
|
||||
@ -2926,25 +2940,14 @@ impl Player {
|
||||
}],
|
||||
));
|
||||
|
||||
match self.client.as_ref() {
|
||||
crate::net::ClientPlatform::Java(client) => {
|
||||
client
|
||||
.enqueue_packet(&CGameEvent::new(
|
||||
GameEvent::ChangeGameMode,
|
||||
gamemode as i32 as f32,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
crate::net::ClientPlatform::Bedrock(client) => {
|
||||
client
|
||||
.send_game_packet(
|
||||
&pumpkin_protocol::bedrock::client::set_player_gamemode::CSetPlayerGamemode {
|
||||
gamemode,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
&CGameEvent::new(GameEvent::ChangeGameMode, gamemode as i32 as f32),
|
||||
&pumpkin_protocol::bedrock::client::set_player_gamemode::CSetPlayerGamemode {
|
||||
gamemode,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
true
|
||||
}
|
||||
@ -5134,7 +5137,18 @@ impl InventoryPlayer for Player {
|
||||
packet: &'a CSetSelectedSlot,
|
||||
) -> PlayerFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
self.client.enqueue_packet(packet).await;
|
||||
self.client
|
||||
.enqueue_packet_editioned(
|
||||
packet,
|
||||
&pumpkin_protocol::bedrock::client::CPlayerHotbar {
|
||||
selected_slot: pumpkin_protocol::codec::var_uint::VarUInt(
|
||||
packet.slot as u32,
|
||||
),
|
||||
container_id: 0,
|
||||
should_select_block: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -268,6 +268,17 @@ impl ClientPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_packet_now_editioned<J: ClientPacket, B: BClientPacket>(
|
||||
&self,
|
||||
je_packet: &J,
|
||||
be_packet: &B,
|
||||
) {
|
||||
match self {
|
||||
Self::Java(java) => java.send_packet_now(je_packet).await,
|
||||
Self::Bedrock(bedrock) => bedrock.send_game_packet(be_packet).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_packet_now_data(&self, data: Bytes) {
|
||||
match self {
|
||||
Self::Java(java) => java.send_packet_now_data(data).await,
|
||||
|
||||
@ -681,7 +681,7 @@ impl Server {
|
||||
/// # Note
|
||||
///
|
||||
/// This function does not handle the actual mob spawn options update, which is a TODO item for future implementation.
|
||||
pub fn set_difficulty(&self, difficulty: Difficulty, force_update: bool) {
|
||||
pub async fn set_difficulty(&self, difficulty: Difficulty, force_update: bool) {
|
||||
let current_info = self.level_info.load();
|
||||
if current_info.difficulty_locked && !force_update {
|
||||
return;
|
||||
@ -701,9 +701,13 @@ impl Server {
|
||||
|
||||
for world in self.worlds.load().iter() {
|
||||
world.set_difficulty(difficulty);
|
||||
world
|
||||
.broadcast_editioned(
|
||||
&CChangeDifficulty::new(difficulty as u8, locked),
|
||||
&pumpkin_protocol::bedrock::client::CSetDifficulty::new(difficulty as u32),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
self.broadcast_packet_all(&CChangeDifficulty::new(difficulty as u8, locked));
|
||||
}
|
||||
|
||||
/// Searches for a player by their username across all worlds.
|
||||
|
||||
@ -3473,14 +3473,27 @@ impl World {
|
||||
position.y.round() as i32,
|
||||
position.z.round() as i32,
|
||||
));
|
||||
let bedrock_dimension = match target_world.dimension.minecraft_name {
|
||||
"minecraft:the_nether" => 1,
|
||||
"minecraft:the_end" => 2,
|
||||
_ => 0,
|
||||
};
|
||||
player
|
||||
.client
|
||||
.send_packet_now(&CPlayerSpawnPosition::new(
|
||||
spawn_block_pos,
|
||||
yaw,
|
||||
pitch,
|
||||
target_world.dimension.minecraft_name.to_string(),
|
||||
))
|
||||
.send_packet_now_editioned(
|
||||
&CPlayerSpawnPosition::new(
|
||||
spawn_block_pos,
|
||||
yaw,
|
||||
pitch,
|
||||
target_world.dimension.minecraft_name.to_string(),
|
||||
),
|
||||
&pumpkin_protocol::bedrock::client::CSetSpawnPosition::new(
|
||||
1, // World spawn
|
||||
spawn_block_pos,
|
||||
bedrock_dimension,
|
||||
spawn_block_pos,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
player.living_entity.reset_state().await;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user