mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
implement reverting block state predictions on ack
This commit is contained in:
parent
067ec06f26
commit
1b348ceeff
13 changed files with 277 additions and 65 deletions
|
@ -51,7 +51,7 @@ use crate::{
|
|||
connection::RawConnection,
|
||||
disconnect::DisconnectEvent,
|
||||
events::Event,
|
||||
interact::CurrentSequenceNumber,
|
||||
interact::BlockStatePredictionHandler,
|
||||
inventory::Inventory,
|
||||
join::{ConnectOpts, StartJoinServerEvent},
|
||||
local_player::{Hunger, InstanceHolder, PermissionLevel, PlayerAbilities, TabList},
|
||||
|
@ -586,7 +586,7 @@ pub struct JoinedClientBundle {
|
|||
pub physics_state: PhysicsState,
|
||||
pub inventory: Inventory,
|
||||
pub tab_list: TabList,
|
||||
pub current_sequence_number: CurrentSequenceNumber,
|
||||
pub current_sequence_number: BlockStatePredictionHandler,
|
||||
pub last_sent_direction: LastSentLookDirection,
|
||||
pub abilities: PlayerAbilities,
|
||||
pub permission_level: PermissionLevel,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use azalea_block::BlockState;
|
||||
use azalea_core::{
|
||||
direction::Direction,
|
||||
|
@ -96,17 +98,95 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
/// A component that contains the number of changes this client has made to
|
||||
/// blocks.
|
||||
#[derive(Component, Copy, Clone, Debug, Default, Deref)]
|
||||
pub struct CurrentSequenceNumber(u32);
|
||||
/// A component that contains information about our local block state
|
||||
/// predictions.
|
||||
#[derive(Component, Clone, Debug, Default)]
|
||||
pub struct BlockStatePredictionHandler {
|
||||
/// The total number of changes that this client has made to blocks.
|
||||
seq: u32,
|
||||
server_state: HashMap<BlockPos, ServerVerifiedState>,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
struct ServerVerifiedState {
|
||||
seq: u32,
|
||||
block_state: BlockState,
|
||||
/// Used for teleporting the player back if we're colliding with the block
|
||||
/// that got placed back.
|
||||
#[allow(unused)]
|
||||
player_pos: Vec3,
|
||||
}
|
||||
|
||||
impl CurrentSequenceNumber {
|
||||
impl BlockStatePredictionHandler {
|
||||
/// Get the next sequence number that we're going to use and increment the
|
||||
/// value.
|
||||
pub fn get_next(&mut self) -> u32 {
|
||||
self.0 += 1;
|
||||
self.0
|
||||
pub fn start_predicting(&mut self) -> u32 {
|
||||
self.seq += 1;
|
||||
self.seq
|
||||
}
|
||||
|
||||
/// Should be called right before the client updates a block with its
|
||||
/// prediction.
|
||||
///
|
||||
/// This is used to make sure that we can rollback to this state if the
|
||||
/// server acknowledges the sequence number (with
|
||||
/// [`ClientboundBlockChangedAck`]) without having sent a block update.
|
||||
///
|
||||
/// [`ClientboundBlockChangedAck`]: azalea_protocol::packets::game::ClientboundBlockChangedAck
|
||||
pub fn retain_known_server_state(
|
||||
&mut self,
|
||||
pos: BlockPos,
|
||||
old_state: BlockState,
|
||||
player_pos: Vec3,
|
||||
) {
|
||||
self.server_state
|
||||
.entry(pos)
|
||||
.and_modify(|s| s.seq = self.seq)
|
||||
.or_insert(ServerVerifiedState {
|
||||
seq: self.seq,
|
||||
block_state: old_state,
|
||||
player_pos: player_pos,
|
||||
});
|
||||
}
|
||||
|
||||
/// Save this update as the correct server state so when the server sends a
|
||||
/// [`ClientboundBlockChangedAck`] we don't roll back this new update.
|
||||
///
|
||||
/// This should be used when we receive a block update from the server.
|
||||
///
|
||||
/// [`ClientboundBlockChangedAck`]: azalea_protocol::packets::game::ClientboundBlockChangedAck
|
||||
pub fn update_known_server_state(&mut self, pos: BlockPos, state: BlockState) -> bool {
|
||||
if let Some(s) = self.server_state.get_mut(&pos) {
|
||||
s.block_state = state;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_prediction_up_to(&mut self, seq: u32, world: &Instance) {
|
||||
let mut to_remove = Vec::new();
|
||||
for (pos, state) in &self.server_state {
|
||||
if state.seq > seq {
|
||||
continue;
|
||||
}
|
||||
to_remove.push(*pos);
|
||||
|
||||
// syncBlockState
|
||||
let client_block_state = world.get_block_state(*pos).unwrap_or_default();
|
||||
let server_block_state = state.block_state;
|
||||
if client_block_state == server_block_state {
|
||||
continue;
|
||||
}
|
||||
world.set_block_state(*pos, server_block_state);
|
||||
// TODO: implement these two functions
|
||||
// if is_colliding(player, *pos, server_block_state) {
|
||||
// abs_snap_to(state.player_pos);
|
||||
// }
|
||||
}
|
||||
|
||||
for pos in to_remove {
|
||||
self.server_state.remove(&pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,13 +243,15 @@ pub fn handle_start_use_item_queued(
|
|||
query: Query<(
|
||||
Entity,
|
||||
&StartUseItemQueued,
|
||||
&mut CurrentSequenceNumber,
|
||||
&mut BlockStatePredictionHandler,
|
||||
&HitResultComponent,
|
||||
&LookDirection,
|
||||
Option<&Mining>,
|
||||
)>,
|
||||
) {
|
||||
for (entity, start_use_item, mut sequence_number, hit_result, look_direction, mining) in query {
|
||||
for (entity, start_use_item, mut prediction_handler, hit_result, look_direction, mining) in
|
||||
query
|
||||
{
|
||||
commands.entity(entity).remove::<StartUseItemQueued>();
|
||||
|
||||
if mining.is_some() {
|
||||
|
@ -203,12 +285,13 @@ pub fn handle_start_use_item_queued(
|
|||
|
||||
match &hit_result {
|
||||
HitResult::Block(block_hit_result) => {
|
||||
let seq = prediction_handler.start_predicting();
|
||||
if block_hit_result.miss {
|
||||
commands.trigger(SendPacketEvent::new(
|
||||
entity,
|
||||
ServerboundUseItem {
|
||||
hand: start_use_item.hand,
|
||||
sequence: sequence_number.get_next(),
|
||||
seq,
|
||||
x_rot: look_direction.x_rot,
|
||||
y_rot: look_direction.y_rot,
|
||||
},
|
||||
|
@ -219,7 +302,7 @@ pub fn handle_start_use_item_queued(
|
|||
ServerboundUseItemOn {
|
||||
hand: start_use_item.hand,
|
||||
block_hit: block_hit_result.into(),
|
||||
sequence: sequence_number.get_next(),
|
||||
seq,
|
||||
},
|
||||
));
|
||||
// TODO: depending on the result of useItemOn, this might
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use azalea_block::{BlockState, BlockTrait, fluid_state::FluidState};
|
||||
use azalea_core::{direction::Direction, game_type::GameMode, position::BlockPos, tick::GameTick};
|
||||
use azalea_entity::{FluidOnEyes, Physics, mining::get_mine_progress};
|
||||
use azalea_entity::{FluidOnEyes, Physics, Position, mining::get_mine_progress};
|
||||
use azalea_inventory::ItemStack;
|
||||
use azalea_physics::{PhysicsSet, collision::BlockWithShape};
|
||||
use azalea_protocol::packets::game::s_player_action::{self, ServerboundPlayerAction};
|
||||
|
@ -13,7 +13,7 @@ use tracing::trace;
|
|||
use crate::{
|
||||
Client,
|
||||
interact::{
|
||||
CurrentSequenceNumber, HitResultComponent, SwingArmEvent, can_use_game_master_blocks,
|
||||
BlockStatePredictionHandler, HitResultComponent, SwingArmEvent, can_use_game_master_blocks,
|
||||
check_is_interaction_restricted,
|
||||
},
|
||||
inventory::{Inventory, InventorySet},
|
||||
|
@ -216,7 +216,7 @@ fn handle_mining_queued(
|
|||
&FluidOnEyes,
|
||||
&Physics,
|
||||
Option<&Mining>,
|
||||
&mut CurrentSequenceNumber,
|
||||
&mut BlockStatePredictionHandler,
|
||||
&mut MineDelay,
|
||||
&mut MineProgress,
|
||||
&mut MineTicks,
|
||||
|
@ -280,7 +280,7 @@ fn handle_mining_queued(
|
|||
pos: current_mining_pos
|
||||
.expect("IsMining is true so MineBlockPos must be present"),
|
||||
direction: mining_queued.direction,
|
||||
sequence: 0,
|
||||
seq: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ fn handle_mining_queued(
|
|||
action: s_player_action::Action::StartDestroyBlock,
|
||||
pos: mining_queued.position,
|
||||
direction: mining_queued.direction,
|
||||
sequence: sequence_number.get_next(),
|
||||
seq: sequence_number.start_predicting(),
|
||||
},
|
||||
));
|
||||
// vanilla really does send two swing arm packets
|
||||
|
@ -440,14 +440,22 @@ pub fn handle_finish_mining_block_observer(
|
|||
&Inventory,
|
||||
&PlayerAbilities,
|
||||
&PermissionLevel,
|
||||
&mut CurrentSequenceNumber,
|
||||
&Position,
|
||||
&mut BlockStatePredictionHandler,
|
||||
)>,
|
||||
instances: Res<InstanceContainer>,
|
||||
) {
|
||||
let event = trigger.event();
|
||||
|
||||
let (instance_name, game_mode, inventory, abilities, permission_level, _sequence_number) =
|
||||
query.get_mut(trigger.target()).unwrap();
|
||||
let (
|
||||
instance_name,
|
||||
game_mode,
|
||||
inventory,
|
||||
abilities,
|
||||
permission_level,
|
||||
player_pos,
|
||||
mut prediction_handler,
|
||||
) = query.get_mut(trigger.target()).unwrap();
|
||||
let instance_lock = instances.get(instance_name).unwrap();
|
||||
let instance = instance_lock.read();
|
||||
if check_is_interaction_restricted(&instance, event.position, &game_mode.current, inventory) {
|
||||
|
@ -469,7 +477,8 @@ pub fn handle_finish_mining_block_observer(
|
|||
return;
|
||||
};
|
||||
|
||||
let registry_block = Box::<dyn BlockTrait>::from(block_state).as_registry_block();
|
||||
let registry_block: azalea_registry::Block =
|
||||
Box::<dyn BlockTrait>::from(block_state).as_registry_block();
|
||||
if !can_use_game_master_blocks(abilities, permission_level)
|
||||
&& matches!(
|
||||
registry_block,
|
||||
|
@ -485,7 +494,10 @@ pub fn handle_finish_mining_block_observer(
|
|||
// when we break a waterlogged block we want to keep the water there
|
||||
let fluid_state = FluidState::from(block_state);
|
||||
let block_state_for_fluid = BlockState::from(fluid_state);
|
||||
instance.set_block_state(event.position, block_state_for_fluid);
|
||||
let old_state = instance
|
||||
.set_block_state(event.position, block_state_for_fluid)
|
||||
.unwrap_or_default();
|
||||
prediction_handler.retain_known_server_state(event.position, old_state, **player_pos);
|
||||
}
|
||||
|
||||
/// Abort mining a block.
|
||||
|
@ -510,7 +522,7 @@ pub fn handle_stop_mining_block_event(
|
|||
action: s_player_action::Action::AbortDestroyBlock,
|
||||
pos: mine_block_pos,
|
||||
direction: Direction::Down,
|
||||
sequence: 0,
|
||||
seq: 0,
|
||||
},
|
||||
));
|
||||
commands.entity(event.entity).remove::<Mining>();
|
||||
|
@ -538,7 +550,7 @@ pub fn continue_mining_block(
|
|||
&mut MineDelay,
|
||||
&mut MineProgress,
|
||||
&mut MineTicks,
|
||||
&mut CurrentSequenceNumber,
|
||||
&mut BlockStatePredictionHandler,
|
||||
)>,
|
||||
mut commands: Commands,
|
||||
mut mine_block_progress_events: EventWriter<MineBlockProgressEvent>,
|
||||
|
@ -557,7 +569,7 @@ pub fn continue_mining_block(
|
|||
mut mine_delay,
|
||||
mut mine_progress,
|
||||
mut mine_ticks,
|
||||
mut sequence_number,
|
||||
mut prediction_handler,
|
||||
) in query.iter_mut()
|
||||
{
|
||||
if **mine_delay > 0 {
|
||||
|
@ -580,7 +592,7 @@ pub fn continue_mining_block(
|
|||
action: s_player_action::Action::StartDestroyBlock,
|
||||
pos: mining.pos,
|
||||
direction: mining.dir,
|
||||
sequence: sequence_number.get_next(),
|
||||
seq: prediction_handler.start_predicting(),
|
||||
},
|
||||
));
|
||||
commands.trigger(SwingArmEvent { entity });
|
||||
|
@ -634,7 +646,7 @@ pub fn continue_mining_block(
|
|||
action: s_player_action::Action::StopDestroyBlock,
|
||||
pos: mining.pos,
|
||||
direction: mining.dir,
|
||||
sequence: sequence_number.get_next(),
|
||||
seq: prediction_handler.start_predicting(),
|
||||
},
|
||||
));
|
||||
**mine_progress = 0.;
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
connection::RawConnection,
|
||||
declare_packet_handlers,
|
||||
disconnect::DisconnectEvent,
|
||||
interact::BlockStatePredictionHandler,
|
||||
inventory::{
|
||||
ClientSideCloseContainerEvent, Inventory, MenuOpenedEvent, SetContainerContentEvent,
|
||||
},
|
||||
|
@ -1061,13 +1062,17 @@ impl GamePacketHandler<'_> {
|
|||
pub fn block_update(&mut self, p: &ClientboundBlockUpdate) {
|
||||
debug!("Got block update packet {p:?}");
|
||||
|
||||
as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
|
||||
let local_player = query.get_mut(self.player).unwrap();
|
||||
as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
|
||||
self.ecs,
|
||||
|mut query| {
|
||||
let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
|
||||
|
||||
let world = local_player.instance.write();
|
||||
|
||||
world.chunks.set_block_state(p.pos, p.block_state);
|
||||
});
|
||||
let world = local_player.instance.read();
|
||||
if !prediction_handler.update_known_server_state(p.pos, p.block_state) {
|
||||
world.chunks.set_block_state(p.pos, p.block_state);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn animate(&mut self, p: &ClientboundAnimate) {
|
||||
|
@ -1077,15 +1082,19 @@ impl GamePacketHandler<'_> {
|
|||
pub fn section_blocks_update(&mut self, p: &ClientboundSectionBlocksUpdate) {
|
||||
debug!("Got section blocks update packet {p:?}");
|
||||
|
||||
as_system::<Query<&InstanceHolder>>(self.ecs, |mut query| {
|
||||
let local_player = query.get_mut(self.player).unwrap();
|
||||
let world = local_player.instance.write();
|
||||
for state in &p.states {
|
||||
world
|
||||
.chunks
|
||||
.set_block_state(p.section_pos + state.pos, state.state);
|
||||
}
|
||||
});
|
||||
as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
|
||||
self.ecs,
|
||||
|mut query| {
|
||||
let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
|
||||
let world = local_player.instance.read();
|
||||
for new_state in &p.states {
|
||||
let pos = p.section_pos + new_state.pos;
|
||||
if !prediction_handler.update_known_server_state(pos, new_state.state) {
|
||||
world.chunks.set_block_state(pos, new_state.state);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn game_event(&mut self, p: &ClientboundGameEvent) {
|
||||
|
@ -1125,7 +1134,16 @@ impl GamePacketHandler<'_> {
|
|||
|
||||
pub fn award_stats(&mut self, _p: &ClientboundAwardStats) {}
|
||||
|
||||
pub fn block_changed_ack(&mut self, _p: &ClientboundBlockChangedAck) {}
|
||||
pub fn block_changed_ack(&mut self, p: &ClientboundBlockChangedAck) {
|
||||
as_system::<Query<(&InstanceHolder, &mut BlockStatePredictionHandler)>>(
|
||||
self.ecs,
|
||||
|mut query| {
|
||||
let (local_player, mut prediction_handler) = query.get_mut(self.player).unwrap();
|
||||
let world = local_player.instance.read();
|
||||
prediction_handler.end_prediction_up_to(p.seq, &world);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn block_destruction(&mut self, _p: &ClientboundBlockDestruction) {}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use azalea_buf::AzaleaWrite;
|
|||
use azalea_core::{
|
||||
delta::PositionDelta8,
|
||||
game_type::{GameMode, OptionalGameType},
|
||||
position::{ChunkPos, Vec3},
|
||||
position::{BlockPos, ChunkPos, Vec3},
|
||||
resource_location::ResourceLocation,
|
||||
tick::GameTick,
|
||||
};
|
||||
|
@ -102,6 +102,9 @@ impl Simulation {
|
|||
raw_conn.injected_clientbound_packets.push(buf);
|
||||
});
|
||||
}
|
||||
pub fn send_event(&mut self, event: impl bevy_ecs::event::Event) {
|
||||
self.app.world_mut().send_event(event);
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
tick_app(&mut self.app);
|
||||
|
@ -151,6 +154,12 @@ impl Simulation {
|
|||
.chunks
|
||||
.get(&chunk_pos)
|
||||
}
|
||||
pub fn get_block_state(&self, pos: BlockPos) -> Option<BlockState> {
|
||||
self.component::<InstanceHolder>()
|
||||
.instance
|
||||
.read()
|
||||
.get_block_state(pos)
|
||||
}
|
||||
|
||||
pub fn disconnect(&mut self) {
|
||||
// send DisconnectEvent
|
||||
|
|
49
azalea-client/tests/mine_block_rollback.rs
Normal file
49
azalea-client/tests/mine_block_rollback.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use azalea_client::{mining::StartMiningBlockEvent, test_utils::prelude::*};
|
||||
use azalea_core::{
|
||||
position::{BlockPos, ChunkPos},
|
||||
resource_location::ResourceLocation,
|
||||
};
|
||||
use azalea_protocol::packets::{
|
||||
ConnectionProtocol,
|
||||
game::{ClientboundBlockChangedAck, ClientboundBlockUpdate},
|
||||
};
|
||||
use azalea_registry::{Block, DataRegistry, DimensionType};
|
||||
|
||||
#[test]
|
||||
fn test_mine_block_rollback() {
|
||||
init_tracing();
|
||||
|
||||
let mut simulation = Simulation::new(ConnectionProtocol::Game);
|
||||
simulation.receive_packet(make_basic_login_packet(
|
||||
DimensionType::new_raw(0),
|
||||
ResourceLocation::new("azalea:overworld"),
|
||||
));
|
||||
|
||||
simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
|
||||
simulation.tick();
|
||||
|
||||
let pos = BlockPos::new(1, 2, 3);
|
||||
simulation.receive_packet(ClientboundBlockUpdate {
|
||||
pos,
|
||||
// tnt is used for this test because it's insta-mineable so we don't have to waste ticks
|
||||
// waiting
|
||||
block_state: Block::Tnt.into(),
|
||||
});
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Tnt.into()));
|
||||
println!("set serverside tnt");
|
||||
|
||||
simulation.send_event(StartMiningBlockEvent {
|
||||
entity: simulation.entity,
|
||||
position: pos,
|
||||
});
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Air.into()));
|
||||
println!("set clientside air");
|
||||
|
||||
// server didn't send the new block, so the change should be rolled back
|
||||
simulation.receive_packet(ClientboundBlockChangedAck { seq: 1 });
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Tnt.into()));
|
||||
println!("reset serverside tnt");
|
||||
}
|
51
azalea-client/tests/mine_block_without_rollback.rs
Normal file
51
azalea-client/tests/mine_block_without_rollback.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use azalea_client::{mining::StartMiningBlockEvent, test_utils::prelude::*};
|
||||
use azalea_core::{
|
||||
position::{BlockPos, ChunkPos},
|
||||
resource_location::ResourceLocation,
|
||||
};
|
||||
use azalea_protocol::packets::{
|
||||
ConnectionProtocol,
|
||||
game::{ClientboundBlockChangedAck, ClientboundBlockUpdate},
|
||||
};
|
||||
use azalea_registry::{Block, DataRegistry, DimensionType};
|
||||
|
||||
#[test]
|
||||
fn test_mine_block_without_rollback() {
|
||||
init_tracing();
|
||||
|
||||
let mut simulation = Simulation::new(ConnectionProtocol::Game);
|
||||
simulation.receive_packet(make_basic_login_packet(
|
||||
DimensionType::new_raw(0),
|
||||
ResourceLocation::new("azalea:overworld"),
|
||||
));
|
||||
|
||||
simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
|
||||
simulation.tick();
|
||||
|
||||
let pos = BlockPos::new(1, 2, 3);
|
||||
simulation.receive_packet(ClientboundBlockUpdate {
|
||||
pos,
|
||||
// tnt is used for this test because it's insta-mineable so we don't have to waste ticks
|
||||
// waiting
|
||||
block_state: Block::Tnt.into(),
|
||||
});
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Tnt.into()));
|
||||
|
||||
simulation.send_event(StartMiningBlockEvent {
|
||||
entity: simulation.entity,
|
||||
position: pos,
|
||||
});
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Air.into()));
|
||||
|
||||
// server acknowledged our change by sending a BlockUpdate + BlockChangedAck, so
|
||||
// no rollback
|
||||
simulation.receive_packet(ClientboundBlockUpdate {
|
||||
pos,
|
||||
block_state: Block::Air.into(),
|
||||
});
|
||||
simulation.receive_packet(ClientboundBlockChangedAck { seq: 1 });
|
||||
simulation.tick();
|
||||
assert_eq!(simulation.get_block_state(pos), Some(Block::Air.into()));
|
||||
}
|
|
@ -4,5 +4,5 @@ use azalea_protocol_macros::ClientboundGamePacket;
|
|||
#[derive(Clone, Debug, AzBuf, ClientboundGamePacket)]
|
||||
pub struct ClientboundBlockChangedAck {
|
||||
#[var]
|
||||
pub sequence: i32,
|
||||
pub seq: u32,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use azalea_buf::AzBuf;
|
||||
use azalea_core::direction::Direction;
|
||||
use azalea_core::position::BlockPos;
|
||||
use azalea_core::{direction::Direction, position::BlockPos};
|
||||
use azalea_protocol_macros::ServerboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, AzBuf, ServerboundGamePacket)]
|
||||
|
@ -9,7 +8,7 @@ pub struct ServerboundPlayerAction {
|
|||
pub pos: BlockPos,
|
||||
pub direction: Direction,
|
||||
#[var]
|
||||
pub sequence: u32,
|
||||
pub seq: u32,
|
||||
}
|
||||
|
||||
#[derive(AzBuf, Clone, Copy, Debug)]
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::packets::game::s_interact::InteractionHand;
|
|||
pub struct ServerboundUseItem {
|
||||
pub hand: InteractionHand,
|
||||
#[var]
|
||||
pub sequence: u32,
|
||||
pub seq: u32,
|
||||
pub y_rot: f32,
|
||||
pub x_rot: f32,
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub struct ServerboundUseItemOn {
|
|||
pub hand: InteractionHand,
|
||||
pub block_hit: BlockHit,
|
||||
#[var]
|
||||
pub sequence: u32,
|
||||
pub seq: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -192,19 +192,10 @@ impl<S: PalletedContainerKind> PalettedContainer<S> {
|
|||
/// Sets the id at the given coordinates and return the previous id
|
||||
pub fn get_and_set(&mut self, pos: S::SectionPos, value: S) -> S {
|
||||
let paletted_value = self.id_for(value);
|
||||
let block_state_id = self
|
||||
let old_paletted_value = self
|
||||
.storage
|
||||
.get_and_set(self.index_from_coords(pos), paletted_value as u64);
|
||||
// error in debug mode
|
||||
#[cfg(debug_assertions)]
|
||||
if block_state_id > BlockState::MAX_STATE.into() {
|
||||
warn!(
|
||||
"Old block state from get_and_set {block_state_id} was greater than max state {}",
|
||||
BlockState::MAX_STATE
|
||||
);
|
||||
}
|
||||
|
||||
S::try_from(block_state_id as u32).unwrap_or_default()
|
||||
self.palette.value_for(old_paletted_value as usize)
|
||||
}
|
||||
|
||||
/// Sets the id at the given index and return the previous id. You probably
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use azalea_client::{
|
||||
PhysicsState, interact::CurrentSequenceNumber, inventory::Inventory,
|
||||
PhysicsState, interact::BlockStatePredictionHandler, inventory::Inventory,
|
||||
local_player::LocalGameMode, mining::MineBundle, packet::game::SendPacketEvent,
|
||||
};
|
||||
use azalea_core::{
|
||||
|
@ -113,7 +113,7 @@ fn create_simulation_player_complete_bundle(
|
|||
Inventory::default(),
|
||||
LocalGameMode::from(GameMode::Survival),
|
||||
MineBundle::default(),
|
||||
CurrentSequenceNumber::default(),
|
||||
BlockStatePredictionHandler::default(),
|
||||
azalea_client::local_player::PermissionLevel::default(),
|
||||
azalea_client::local_player::PlayerAbilities::default(),
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue