From 2f76d0f60bc6314d7156b1ab51680e65a0b5796b Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Wed, 7 Oct 2026 11:53:00 +0000 Subject: [PATCH 01/26] feat: Add Vanilla-compatible destructive explosions and TNT Add the shared server explosion, TNT block behavior, the Primed TNT entity, the explode packet, and the block/entity effects and physics paths needed for large TNT chains. Rebased onto master: explosion damage sources now retain the strong source entities from #692, and TNT owners use EntityReference with the same-domain lookup from #737. The PR's earlier ID/cached-position damage sources, weak damage-source references, owner-reference refactor and damage-command changes are dropped. Changes to master's existing World::clip and World::raytrace paths are left out; exposure uses its own collision traversal. --- steel-core/src/behavior/block/context.rs | 13 + steel-core/src/behavior/block/mod.rs | 77 +- steel-core/src/behavior/blocks/mod.rs | 4 +- .../src/behavior/blocks/redstone/mod.rs | 2 + .../src/behavior/blocks/redstone/tnt_block.rs | 237 ++++ .../blocks/redstone/tnt_block/tests.rs | 54 + steel-core/src/chunk/data.rs | 8 +- steel-core/src/chunk/full_chunk/mod.rs | 12 +- .../src/chunk/gameplay_chunk_lookup_cache.rs | 302 ++++- steel-core/src/chunk/heightmap.rs | 4 +- steel-core/src/chunk/light/sky_sources.rs | 2 +- steel-core/src/chunk/light/workset.rs | 2 +- steel-core/src/chunk/section.rs | 350 +++++- steel-core/src/chunk_saver/storage/chunk.rs | 4 +- steel-core/src/entity/base/mod.rs | 42 +- steel-core/src/entity/base/tests.rs | 12 +- steel-core/src/entity/block_effects.rs | 109 +- steel-core/src/entity/entities/mod.rs | 2 +- .../entities/objects/display_ui/item_frame.rs | 9 +- .../objects/display_ui/leash_fence_knot.rs | 9 +- .../entity/entities/objects/explosives/mod.rs | 2 + .../entities/objects/explosives/primed_tnt.rs | 400 ++++++ .../objects/explosives/primed_tnt/tests.rs | 228 ++++ .../entities/objects/items/item_entity.rs | 59 +- .../objects/vehicles/chest_minecart.rs | 10 +- steel-core/src/entity/entity/mod.rs | 105 +- steel-core/src/entity/manager/mod.rs | 447 ++++++- steel-core/src/entity/manager/tests/mod.rs | 22 + .../entity/manager/tests/queries_and_order.rs | 324 ++++- .../src/entity/manager/tests/ticking.rs | 132 ++ steel-core/src/entity/mod.rs | 62 +- steel-core/src/entity/tests/movement.rs | 59 + steel-core/src/entity/ticking.rs | 52 +- steel-core/src/physics/collision.rs | 613 +++++++-- steel-core/src/physics/mod.rs | 33 + steel-core/src/player/movement/mod.rs | 2 - steel-core/src/poi/poi_storage.rs | 2 +- steel-core/src/world/block_region.rs | 180 ++- steel-core/src/world/block_updates/mod.rs | 18 +- steel-core/src/world/entity_management.rs | 26 + steel-core/src/world/explosion/mod.rs | 523 ++++++++ steel-core/src/world/explosion/server.rs | 178 +++ .../world/explosion/server/block_effects.rs | 181 +++ .../src/world/explosion/server/block_rays.rs | 350 ++++++ .../explosion/server/block_rays/cache.rs | 496 ++++++++ .../server/block_rays/java_block_pos_set.rs | 159 +++ .../explosion/server/block_rays/tests.rs | 570 +++++++++ .../world/explosion/server/entity_effects.rs | 132 ++ .../src/world/explosion/server/exposure.rs | 240 ++++ .../src/world/explosion/server/tests.rs | 671 ++++++++++ steel-core/src/world/level_effects.rs | 3 + steel-core/src/world/mod.rs | 23 +- steel-core/src/world/raycast.rs | 1113 ++++++++++++++++- steel-core/src/worldgen/chunk_stage_hashes.rs | 12 +- steel-core/src/worldgen/region.rs | 35 +- steel-protocol/src/packets/game/c_explode.rs | 220 ++++ steel-protocol/src/packets/game/mod.rs | 4 + 57 files changed, 8537 insertions(+), 403 deletions(-) create mode 100644 steel-core/src/behavior/blocks/redstone/tnt_block.rs create mode 100644 steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs create mode 100644 steel-core/src/entity/entities/objects/explosives/primed_tnt.rs create mode 100644 steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs create mode 100644 steel-core/src/world/explosion/mod.rs create mode 100644 steel-core/src/world/explosion/server.rs create mode 100644 steel-core/src/world/explosion/server/block_effects.rs create mode 100644 steel-core/src/world/explosion/server/block_rays.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/cache.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/tests.rs create mode 100644 steel-core/src/world/explosion/server/entity_effects.rs create mode 100644 steel-core/src/world/explosion/server/exposure.rs create mode 100644 steel-core/src/world/explosion/server/tests.rs create mode 100644 steel-protocol/src/packets/game/c_explode.rs diff --git a/steel-core/src/behavior/block/context.rs b/steel-core/src/behavior/block/context.rs index a7636862928..b8db1a6aced 100644 --- a/steel-core/src/behavior/block/context.rs +++ b/steel-core/src/behavior/block/context.rs @@ -111,6 +111,7 @@ pub struct BlockLootContext<'a> { entity: Option<&'a dyn Entity>, block_entity: Option<&'a dyn BlockEntity>, tool: Option<&'a ItemStack>, + explosion_radius: Option, luck: f32, } @@ -124,6 +125,7 @@ impl<'a> BlockLootContext<'a> { entity: None, block_entity: None, tool: None, + explosion_radius: None, luck: 0.0, } } @@ -149,6 +151,13 @@ impl<'a> BlockLootContext<'a> { self } + /// Adds the explosion radius used by explosion-decay loot functions. + #[must_use] + pub const fn with_explosion(mut self, radius: f32) -> Self { + self.explosion_radius = Some(radius); + self + } + /// Adds the luck used to evaluate the loot table. #[must_use] pub const fn with_luck(mut self, luck: f32) -> Self { @@ -186,6 +195,10 @@ impl<'a> BlockLootContext<'a> { self.tool } + pub(crate) const fn explosion_radius(&self) -> Option { + self.explosion_radius + } + pub(crate) const fn luck(&self) -> f32 { self.luck } diff --git a/steel-core/src/behavior/block/mod.rs b/steel-core/src/behavior/block/mod.rs index 415737c0044..0abb86dc432 100644 --- a/steel-core/src/behavior/block/mod.rs +++ b/steel-core/src/behavior/block/mod.rs @@ -43,8 +43,8 @@ use crate::physics::collide; use crate::player::Player; use crate::world::game_event::SharedGameEventListener; use crate::world::{ - ClipHitResult, ConditionalBlockSetResult, LevelAccessor, LevelReader, ScheduledTickAccess, - SignalQueryContext, World, + BlockInteraction, ClipHitResult, ConditionalBlockSetResult, Explosion, LevelAccessor, + LevelReader, ScheduledTickAccess, SignalQueryContext, World, }; use steel_registry::vanilla_fluids; @@ -377,6 +377,79 @@ pub trait BlockBehavior: Send + Sync { ) { } + /// Applies Vanilla's default block response to an explosion. + fn on_explosion_hit( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + explosion: &dyn Explosion, + on_hit: &mut dyn FnMut(ItemStack, BlockPos), + ) { + self.default_on_explosion_hit(state, world, pos, explosion, on_hit); + } + + /// Applies the default response from an overriding explosion hook. + fn default_on_explosion_hit( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + explosion: &dyn Explosion, + on_hit: &mut dyn FnMut(ItemStack, BlockPos), + ) { + if state.is_air() { + return; + } + + if self.drop_from_explosion(explosion) { + let empty_tool = ItemStack::empty(); + let block_entity = if state.has_block_entity() { + world.get_block_entity(pos) + } else { + None + }; + let context = BlockLootContext::new(world, pos) + .with_block_entity(block_entity.as_deref()) + .with_entity(explosion.direct_source_entity()) + .with_tool(&empty_tool); + let context = if explosion.block_interaction() == BlockInteraction::DestroyWithDecay { + context.with_explosion(explosion.radius()) + } else { + context + }; + let drop_experience = explosion + .indirect_source_entity() + .is_some_and(|entity| entity.as_player().is_some()); + self.spawn_after_break(state, world, pos, &empty_tool, drop_experience); + for stack in World::block_drops(state, &context) { + on_hit(stack, pos); + } + } + + world.set_block( + pos, + vanilla_blocks::AIR.default_state(), + UpdateFlags::UPDATE_ALL, + ); + self.was_exploded(state, world, pos, explosion); + } + + /// Called after this block is removed by an explosion. + fn was_exploded( + &self, + _state: BlockStateId, + _world: &Arc, + _pos: BlockPos, + _explosion: &dyn Explosion, + ) { + } + + /// Returns whether this block contributes normal explosion loot. + fn drop_from_explosion(&self, _explosion: &dyn Explosion) -> bool { + true + } + /// Called after this block is removed from the world, to affect neighbors. /// /// This is used for things like rails notifying neighbors when removed. diff --git a/steel-core/src/behavior/blocks/mod.rs b/steel-core/src/behavior/blocks/mod.rs index dcaae33768a..e0f7b64ae82 100644 --- a/steel-core/src/behavior/blocks/mod.rs +++ b/steel-core/src/behavior/blocks/mod.rs @@ -50,8 +50,8 @@ pub use redstone::{ DetectorRailBlock, LeverBlock, LightningRodBlock, MovingPistonBlock, NoteBlock, ObserverBlock, PistonBaseBlock, PistonHeadBlock, PoweredBlock, PoweredRailBlock, PressurePlateBlock, PressurePlateSensitivity, RailBlock, RedStoneOreBlock, RedStoneWireBlock, RedstoneLampBlock, - RedstoneTorchBlock, RedstoneWallTorchBlock, RepeaterBlock, TargetBlock, TripWireBlock, - TripWireHookBlock, WeatheringCopperBulbBlock, WeatheringLightningRodBlock, + RedstoneTorchBlock, RedstoneWallTorchBlock, RepeaterBlock, TargetBlock, TntBlock, + TripWireBlock, TripWireHookBlock, WeatheringCopperBulbBlock, WeatheringLightningRodBlock, WeightedPressurePlateBlock, }; pub(crate) use redstone::{MAX_REDSTONE_SIGNAL, MIN_REDSTONE_SIGNAL}; diff --git a/steel-core/src/behavior/blocks/redstone/mod.rs b/steel-core/src/behavior/blocks/redstone/mod.rs index 423211ef4c2..366024b91c4 100644 --- a/steel-core/src/behavior/blocks/redstone/mod.rs +++ b/steel-core/src/behavior/blocks/redstone/mod.rs @@ -16,6 +16,7 @@ mod redstone_lamp_block; mod redstone_ore_block; mod redstone_torch_block; mod target_block; +mod tnt_block; mod tripwire; mod weathering_lightning_rod_block; mod wire; @@ -45,6 +46,7 @@ pub use redstone_lamp_block::RedstoneLampBlock; pub use redstone_ore_block::RedStoneOreBlock; pub use redstone_torch_block::{RedstoneTorchBlock, RedstoneWallTorchBlock}; pub use target_block::TargetBlock; +pub use tnt_block::TntBlock; pub use tripwire::{TripWireBlock, TripWireHookBlock}; pub use weathering_lightning_rod_block::WeatheringLightningRodBlock; pub use wire::RedStoneWireBlock; diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block.rs b/steel-core/src/behavior/blocks/redstone/tnt_block.rs new file mode 100644 index 00000000000..459c6bfa408 --- /dev/null +++ b/steel-core/src/behavior/blocks/redstone/tnt_block.rs @@ -0,0 +1,237 @@ +//! TNT block behavior and priming entry points. + +use std::sync::Arc; + +use glam::DVec3; +use steel_macros::block_behavior; +use steel_protocol::packets::game::SoundSource; +use steel_registry::blocks::BlockRef; +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_registry::blocks::properties::BlockStateProperties; +use steel_registry::stat::vanilla_stat_types; +use steel_registry::vanilla_game_rules::TNT_EXPLODES; +use steel_registry::{ + sound_events, vanilla_blocks, vanilla_entities, vanilla_game_events, vanilla_items, +}; +use steel_utils::types::{InteractionHand, UpdateFlags}; +use steel_utils::{BlockPos, BlockStateId, translations}; +use text_components::TextComponent; + +use crate::behavior::{ + BlockBehavior, BlockHitResult, BlockPlaceContext, InteractionResult, InventoryAccess, +}; +use crate::entity::projectile::Projectile; +use crate::entity::{Entity, entities::PrimedTntEntity, next_entity_id}; +use crate::player::Player; +use crate::world::game_event::GameEventContext; +use crate::world::{ClipHitResult, Explosion, SignalGetter as _, World}; + +const PRIMING_SOUND_VOLUME: f32 = 1.0; +const PRIMING_SOUND_PITCH: f32 = 1.0; +const FLINT_AND_STEEL_DAMAGE_PER_USE: i32 = 1; +const FIRE_CHARGE_ITEMS_PER_USE: i32 = 1; + +/// Vanilla TNT block behavior. +#[block_behavior] +pub struct TntBlock { + block: BlockRef, +} + +impl TntBlock { + /// Creates TNT block behavior. + #[must_use] + pub const fn new(block: BlockRef) -> Self { + Self { block } + } + + fn primed_entity( + world: &Arc, + pos: BlockPos, + owner: Option<&dyn Entity>, + ) -> Arc { + let (x, y, z) = pos.get_bottom_center(); + Arc::new(PrimedTntEntity::primed( + &vanilla_entities::TNT, + next_entity_id(), + DVec3::new(x, y, z), + world, + owner, + )) + } + + /// Primes TNT at `pos`, returning whether a primed entity was added to the world. + /// + /// This is crate-visible so shared fire burn ticking can call the same entry point when that + /// foundation is implemented. + pub(crate) fn prime(world: &Arc, pos: BlockPos, owner: Option<&dyn Entity>) -> bool { + if !world.get_game_rule(&TNT_EXPLODES) { + return false; + } + + let entity = Self::primed_entity(world, pos, owner); + let position = entity.position(); + if let Err(error) = world.try_add_entity(entity) { + log::debug!("failed to add primed TNT: {error}"); + return false; + } + world.play_sound_at( + &sound_events::ENTITY_TNT_PRIMED, + SoundSource::Blocks, + position, + PRIMING_SOUND_VOLUME, + PRIMING_SOUND_PITCH, + None, + ); + world.game_event( + &vanilla_game_events::PRIME_FUSE, + pos, + &GameEventContext::new(owner, None), + ); + true + } + + fn send_disabled_message(player: &Player) { + player.send_overlay_message(&TextComponent::translated( + translations::BLOCK_MINECRAFT_TNT_DISABLED.msg(), + )); + } +} + +impl BlockBehavior for TntBlock { + fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option { + Some(self.block.default_state()) + } + + fn on_place( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + old_state: BlockStateId, + _moved_by_piston: bool, + ) { + if old_state.get_block() != state.get_block() + && world.has_neighbor_signal(pos) + && Self::prime(world, pos, None) + { + world.remove_block(pos, false); + } + } + + fn handle_neighbor_changed( + &self, + _state: BlockStateId, + world: &Arc, + pos: BlockPos, + _source_block: BlockRef, + _moved_by_piston: bool, + ) { + if world.has_neighbor_signal(pos) && Self::prime(world, pos, None) { + world.remove_block(pos, false); + } + } + + fn player_will_destroy( + &self, + state: BlockStateId, + world: &Arc, + pos: BlockPos, + player: &Player, + ) -> BlockStateId { + if !player.has_infinite_materials() && state.get_value(&BlockStateProperties::UNSTABLE) { + Self::prime(world, pos, None); + } + state + } + + fn was_exploded( + &self, + _state: BlockStateId, + world: &Arc, + pos: BlockPos, + explosion: &dyn Explosion, + ) { + if !world.get_game_rule(&TNT_EXPLODES) { + return; + } + + let entity = Self::primed_entity(world, pos, explosion.indirect_source_entity()); + entity.set_fuse(PrimedTntEntity::get_random_short_fuse(world, entity.fuse())); + if let Err(error) = world.try_add_entity(entity) { + log::debug!("failed to add primed TNT: {error}"); + } + } + + fn use_item_on( + &self, + _state: BlockStateId, + world: &Arc, + pos: BlockPos, + player: &Player, + _hand: InteractionHand, + _hit_result: &BlockHitResult, + inv: &mut InventoryAccess, + ) -> InteractionResult { + let (is_flint_and_steel, is_fire_charge) = inv.with_item(|item| { + ( + item.is(&vanilla_items::FLINT_AND_STEEL), + item.is(&vanilla_items::FIRE_CHARGE), + ) + }); + if !is_flint_and_steel && !is_fire_charge { + return InteractionResult::TryEmptyHandInteraction; + } + + if !Self::prime(world, pos, Some(player)) { + if !world.get_game_rule(&TNT_EXPLODES) { + Self::send_disabled_message(player); + return InteractionResult::Pass; + } + return InteractionResult::Success; + } + + world.set_block( + pos, + vanilla_blocks::AIR.default_state(), + UpdateFlags::UPDATE_ALL_IMMEDIATE, + ); + let has_infinite_materials = player.has_infinite_materials(); + let used_item = inv.with_item(|item| { + let used_item = item.item(); + if is_flint_and_steel { + item.hurt_and_break(FLINT_AND_STEEL_DAMAGE_PER_USE, has_infinite_materials); + } else if !has_infinite_materials { + item.shrink(FIRE_CHARGE_ITEMS_PER_USE); + } + used_item + }); + player.award_stat(&vanilla_stat_types::ITEM_USED, used_item); + InteractionResult::Success + } + + fn on_projectile_hit( + &self, + _state: BlockStateId, + world: &Arc, + hit: &ClipHitResult, + projectile: &dyn Projectile, + ) { + if !projectile.is_on_fire() || !projectile.projectile_may_interact(world, hit.block_pos) { + return; + } + + let owner = projectile.get_owner(); + let living_owner = owner.as_deref().filter(|owner| owner.is_living_entity()); + if Self::prime(world, hit.block_pos, living_owner) { + world.remove_block(hit.block_pos, false); + } + } + + fn drop_from_explosion(&self, _explosion: &dyn Explosion) -> bool { + false + } +} + +#[cfg(test)] +#[path = "tnt_block/tests.rs"] +mod tests; diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs b/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs new file mode 100644 index 00000000000..c3ec3320108 --- /dev/null +++ b/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs @@ -0,0 +1,54 @@ +use steel_registry::init_vanilla_registry; +use steel_utils::{ChunkPos, Downcast as _, WorldAabb}; + +use super::*; +use crate::behavior::init_behaviors; +use crate::entity::SharedEntity; +use crate::player::ResetReason; +use crate::test_support::{TestPlayerBuilder, fresh_test_world, insert_ready_full_chunk}; +use crate::world::{ExplosionInteraction, ExplosionOptions}; + +#[test] +fn explosion_replaces_tnt_with_a_short_fuse_entity_owned_by_the_igniter() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("tnt_chain_reaction"); + let world = &fixture.world; + let pos = BlockPos::new(8, 64, 8); + insert_ready_full_chunk(world, ChunkPos::from_block_pos(pos)); + assert!(world.set_block( + pos, + vanilla_blocks::TNT.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let owner = TestPlayerBuilder::new(Arc::clone(world), "ChainOwner", next_entity_id()).build(); + let (x, y, z) = pos.offset(4, 0, 0).get_bottom_center(); + owner.base().set_position_local(DVec3::new(x, y, z)); + assert!(world.add_player(Arc::clone(&owner), ResetReason::InitialJoin)); + let (x, y, z) = pos.get_center(); + let mut options = ExplosionOptions::new(DVec3::new(x, y, z), 4.0, ExplosionInteraction::Tnt); + options.source = Some(Arc::clone(&owner) as SharedEntity); + + world.explode(options); + + assert!(world.get_block_state(pos).is_air()); + let (x, y, z) = (f64::from(pos.x()), f64::from(pos.y()), f64::from(pos.z())); + let primed = world + .get_entities_in_aabb(&WorldAabb::new(x, y, z, x + 1.0, y + 1.0, z + 1.0)) + .into_iter() + .filter(|entity| entity.downcast_ref::().is_some()) + .collect::>(); + let [primed] = primed.as_slice() else { + panic!("expected exactly one primed TNT, found {}", primed.len()); + }; + let fuse = primed + .downcast_ref::() + .map(PrimedTntEntity::fuse) + .expect("spawned entity should remain primed TNT"); + // `getRandomShortFuse(80)` is `nextInt(20) + 10`. + assert!((10..30).contains(&fuse), "fuse={fuse}"); + assert_eq!( + primed.explosion_indirect_source().map(|source| source.id()), + Some(owner.id()) + ); +} diff --git a/steel-core/src/chunk/data.rs b/steel-core/src/chunk/data.rs index d4a60b51a6c..6b1216c0317 100644 --- a/steel-core/src/chunk/data.rs +++ b/steel-core/src/chunk/data.rs @@ -1066,7 +1066,7 @@ impl Chunk { let scan_local_y = ((scan_y - min_y) % 16) as usize; sections.sections[scan_section_index] .read() - .states + .states() .get(lx, scan_local_y, lz) }; @@ -1101,7 +1101,7 @@ impl Chunk { let scan_local_y = ((scan_y - min_y) % 16) as usize; sections.sections[scan_section_index] .read() - .states + .states() .get(lx, scan_local_y, lz) }; @@ -1138,7 +1138,7 @@ impl Chunk { let local_y = (y & 15) as usize; let local_z = (pos.0.z & 15) as usize; - section_guard.states.get(local_x, local_y, local_z) + section_guard.states().get(local_x, local_y, local_z) } fn with_locked_block_state(&self, pos: BlockPos, f: impl FnOnce(BlockStateId) -> R) -> R { @@ -1148,7 +1148,7 @@ impl Chunk { } let section = self.sections.sections[self.get_section_index(y)].read(); - let state = section.states.get( + let state = section.states().get( (pos.x() & 15) as usize, (y & 15) as usize, (pos.z() & 15) as usize, diff --git a/steel-core/src/chunk/full_chunk/mod.rs b/steel-core/src/chunk/full_chunk/mod.rs index 522923b5314..4b532598434 100644 --- a/steel-core/src/chunk/full_chunk/mod.rs +++ b/steel-core/src/chunk/full_chunk/mod.rs @@ -220,7 +220,7 @@ impl FullChunkRef<'_> { let (local_x, local_y, local_z) = random_positions.next_local(); let state = section_guard .get_or_insert_with(|| section.read()) - .states + .states() .get(local_x, local_y, local_z); let Some((tick_block, tick_fluid)) = random_tick_kinds(state) else { continue; @@ -1417,7 +1417,7 @@ impl FullChunkRef<'_> { let state = if section.is_empty() { REGISTRY.blocks.get_base_state_id(&vanilla_blocks::AIR) } else { - section.states.get( + section.states().get( (pos.x() & 15) as usize, (y & 15) as usize, (pos.z() & 15) as usize, @@ -1609,7 +1609,7 @@ impl FullChunkRef<'_> { let mut keep_block_entity_decision = None; let (old_state, was_empty, is_empty, detached_block_entity) = loop { let mut section_guard = section.write(); - let observed_state = section_guard.states.get(local_x, local_y, local_z); + let observed_state = section_guard.states().get(local_x, local_y, local_z); if expected_state.is_some_and(|expected| observed_state != expected) { return Some(FullChunkBlockSetResult::Stale(observed_state)); } @@ -1663,7 +1663,7 @@ impl FullChunkRef<'_> { let scan_local_y = ((scan_y - min_y) % 16) as usize; sections.sections[scan_section_index] .read() - .states + .states() .get(lx, scan_local_y, lz) }); @@ -1738,7 +1738,7 @@ impl FullChunkRef<'_> { // Removal callbacks may synchronously replace this position. Vanilla does not run // placement callbacks for the stale request. - let current_state = section.read().states.get(local_x, local_y, local_z); + let current_state = section.read().states().get(local_x, local_y, local_z); if current_state.get_block() != new_block { return Some(FullChunkBlockSetResult::Stale(current_state)); } @@ -1821,7 +1821,7 @@ impl FullChunkRef<'_> { let local_y = (y & 15) as usize; let local_z = (pos.0.z & 15) as usize; - section_guard.states.get(local_x, local_y, local_z) + section_guard.states().get(local_x, local_y, local_z) } /// Returns the index of the highest filled section in this chunk, if any. diff --git a/steel-core/src/chunk/gameplay_chunk_lookup_cache.rs b/steel-core/src/chunk/gameplay_chunk_lookup_cache.rs index e9a7c5d312d..c5e801dca9a 100644 --- a/steel-core/src/chunk/gameplay_chunk_lookup_cache.rs +++ b/steel-core/src/chunk/gameplay_chunk_lookup_cache.rs @@ -4,11 +4,18 @@ //! cache retains holder identity (including map absence), but never generation //! permission or published status; those remain live per lookup. -use std::{cell::RefCell, marker::PhantomData, ptr, rc::Rc, sync::Arc}; +use std::{ + cell::{Cell, RefCell}, + marker::PhantomData, + num::NonZeroU64, + ptr, + rc::Rc, + sync::Arc, +}; -use steel_utils::ChunkPos; +use steel_utils::{BlockPos, BlockStateId, ChunkPos}; -use super::{chunk_holder::ChunkHolder, chunk_map::ChunkMap}; +use super::{chunk_holder::ChunkHolder, chunk_map::ChunkMap, status::ChunkStatus}; // Vanilla's ServerChunkCache also keeps four recent synchronous lookups. Steel // caches holders rather than a status-specific chunk view so concurrent publication stays visible. @@ -63,6 +70,7 @@ struct CacheEntry { struct ActiveCache { owner: CacheOwner, + scope_id: Option, entries: [Option; CACHE_ENTRY_COUNT], stats: GameplayChunkLookupCacheStats, } @@ -73,9 +81,10 @@ enum CacheEntryProbe { } impl ActiveCache { - fn new(owner: CacheOwner) -> Self { + fn new(owner: CacheOwner, scope_id: Option) -> Self { Self { owner, + scope_id, entries: [const { None }; CACHE_ENTRY_COUNT], stats: GameplayChunkLookupCacheStats::default(), } @@ -136,6 +145,15 @@ impl ActiveCache { thread_local! { static ACTIVE_CACHE: RefCell> = const { RefCell::new(None) }; + static NEXT_SCOPE_ID: Cell = const { Cell::new(1) }; +} + +fn next_scope_id() -> Option { + NEXT_SCOPE_ID.with(|next| { + let current = NonZeroU64::new(next.get())?; + next.set(current.get().checked_add(1).unwrap_or(0)); + Some(current) + }) } enum CacheProbe { @@ -178,7 +196,8 @@ impl<'map> GameplayChunkLookupCacheScope<'map> { } fn enter_key(owner: CacheOwner) -> Self { - let previous = ACTIVE_CACHE.with(|cache| cache.replace(Some(ActiveCache::new(owner)))); + let previous = ACTIVE_CACHE + .with(|cache| cache.replace(Some(ActiveCache::new(owner, next_scope_id())))); Self { previous, active: true, @@ -201,6 +220,147 @@ impl<'map> GameplayChunkLookupCacheScope<'map> { } } +/// A tiny holder cache for repeated, live Full-chunk block-state reads. +/// +/// Entries are usable only inside the exact gameplay lookup-cache scope that populated them. That +/// scope is the proof that active-map membership is stable. The holder's current Full permission +/// and publication are rechecked for every read, and the block section itself is locked only long +/// enough to copy the state. Missing and unpublished chunks are never retained. +pub(crate) struct LocalFullChunkHolderCache { + scope_id: Option, + entries: [Option; CACHE_ENTRY_COUNT], + #[cfg(test)] + stats: LocalFullChunkHolderCacheStats, +} + +struct LocalFullChunkHolderCacheEntry { + pos: ChunkPos, + holder: Arc, +} + +#[cfg(test)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub(crate) struct LocalFullChunkHolderCacheStats { + pub(crate) holder_hits: usize, + pub(crate) active_holder_lookups: usize, + pub(crate) fallback_reads: usize, +} + +impl LocalFullChunkHolderCache { + pub(crate) const fn new() -> Self { + Self { + scope_id: None, + entries: [const { None }; CACHE_ENTRY_COUNT], + #[cfg(test)] + stats: LocalFullChunkHolderCacheStats { + holder_hits: 0, + active_holder_lookups: 0, + fallback_reads: 0, + }, + } + } + + /// Reads the current state without retaining a section guard or a missing chunk result. + pub(crate) fn block_state( + &mut self, + chunk_map: &ChunkMap, + pos: BlockPos, + ) -> Option { + let owner = CacheOwner::for_chunk_map(chunk_map); + let active_scope_id = active_scope_id(owner); + if active_scope_id != self.scope_id { + self.entries.fill_with(|| None); + self.scope_id = active_scope_id; + } + + let chunk_pos = ChunkPos::from_block_pos(pos); + let Some(_) = active_scope_id else { + #[cfg(test)] + { + self.stats.fallback_reads += 1; + } + return chunk_map.with_full_chunk(chunk_pos, |chunk| chunk.get_block_state(pos)); + }; + + if let Some(index) = self + .entries + .iter() + .position(|entry| entry.as_ref().is_some_and(|entry| entry.pos == chunk_pos)) + { + let state = { + let entry = self.entries[index].as_ref()?; + live_full_block_state(&entry.holder, pos) + }; + let Some(state) = state else { + self.remove(index); + return None; + }; + #[cfg(test)] + { + self.stats.holder_hits += 1; + } + self.promote(index); + return Some(state); + } + + #[cfg(test)] + { + self.stats.active_holder_lookups += 1; + } + let holder = chunk_map.active_full_chunk_holder(chunk_pos)?; + let state = live_full_block_state(&holder, pos)?; + self.insert(chunk_pos, holder); + Some(state) + } + + fn insert(&mut self, pos: ChunkPos, holder: Arc) { + for index in (1..CACHE_ENTRY_COUNT).rev() { + self.entries[index] = self.entries[index - 1].take(); + } + self.entries[0] = Some(LocalFullChunkHolderCacheEntry { pos, holder }); + } + + fn promote(&mut self, index: usize) { + if index == 0 { + return; + } + let entry = self.entries[index].take(); + for target in (1..=index).rev() { + self.entries[target] = self.entries[target - 1].take(); + } + self.entries[0] = entry; + } + + fn remove(&mut self, index: usize) { + for target in index..CACHE_ENTRY_COUNT - 1 { + self.entries[target] = self.entries[target + 1].take(); + } + self.entries[CACHE_ENTRY_COUNT - 1] = None; + } + + #[cfg(test)] + pub(crate) const fn stats(&self) -> LocalFullChunkHolderCacheStats { + self.stats + } +} + +fn active_scope_id(owner: CacheOwner) -> Option { + ACTIVE_CACHE.with(|cache| { + let cache = cache.borrow(); + let cache = cache.as_ref()?; + (cache.owner == owner).then_some(cache.scope_id).flatten() + }) +} + +fn live_full_block_state(holder: &ChunkHolder, pos: BlockPos) -> Option { + if holder.is_status_disallowed(ChunkStatus::Full) { + return None; + } + holder + .try_full_chunk() + .map(|chunk| chunk.get_block_state(pos)) +} + impl Drop for GameplayChunkLookupCacheScope<'_> { fn drop(&mut self) { drop(self.restore()); @@ -267,7 +427,15 @@ where #[cfg(test)] mod tests { use super::*; + use steel_registry::{blocks::BlockRef, vanilla_blocks}; + use steel_utils::types::UpdateFlags; + + use crate::behavior::init_behaviors; + use crate::chunk::Chunk; use crate::chunk::chunk_ticket_manager::ChunkTicketLevel; + use crate::chunk::section::{ChunkSection, Sections}; + use crate::test_support::{fresh_test_world, insert_ready_full_chunk}; + use crate::world::World; fn holder(pos: ChunkPos) -> Arc { Arc::new(ChunkHolder::new( @@ -279,6 +447,10 @@ mod tests { )) } + fn set_test_block(world: &Arc, pos: BlockPos, block: BlockRef) { + assert!(world.set_block(pos, block.default_state(), UpdateFlags::UPDATE_NONE)); + } + #[test] fn four_entry_cache_uses_most_recently_used_eviction() { let owner = 0_u8; @@ -454,4 +626,124 @@ mod tests { assert_eq!(stats.foreign_map_bypasses, 2); assert_eq!(stats.scc_lookups, 0); } + + #[test] + fn local_full_holder_cache_rechecks_full_permission() { + let fixture = fresh_test_world("local_full_holder_cache_permission"); + let world = &fixture.world; + init_behaviors(); + let pos = BlockPos::new(3, 64, 7); + let holder = insert_ready_full_chunk(world, ChunkPos::from_block_pos(pos)); + set_test_block(world, pos, &vanilla_blocks::STONE); + + let scope = GameplayChunkLookupCacheScope::enter(&world.chunk_map); + let mut local = LocalFullChunkHolderCache::new(); + assert_eq!( + local.block_state(&world.chunk_map, pos), + Some(vanilla_blocks::STONE.default_state()) + ); + + holder.update_highest_allowed_status(None); + assert_eq!(local.block_state(&world.chunk_map, pos), None); + + holder.update_highest_allowed_status(Some(ChunkTicketLevel::FULL_CHUNK)); + assert_eq!( + local.block_state(&world.chunk_map, pos), + Some(vanilla_blocks::STONE.default_state()) + ); + assert_eq!(local.stats().active_holder_lookups, 2); + drop(scope); + } + + #[test] + fn local_full_holder_cache_does_not_retain_unpublished_data() { + let fixture = fresh_test_world("local_full_holder_cache_publication"); + let world = &fixture.world; + let chunk_pos = ChunkPos::new(-2, 3); + let pos = BlockPos::new(-31, 64, 49); + let min_y = world.chunk_map.world_gen_context.min_y(); + let height = world.chunk_map.world_gen_context.height(); + let holder = Arc::new(ChunkHolder::new( + chunk_pos, + ChunkTicketLevel::FULL_CHUNK, + None, + min_y, + height, + )); + let _ = world + .chunk_map + .chunks + .insert_sync(chunk_pos, Arc::clone(&holder)); + let scope = GameplayChunkLookupCacheScope::enter(&world.chunk_map); + let mut local = LocalFullChunkHolderCache::new(); + + assert_eq!(local.block_state(&world.chunk_map, pos), None); + + let sections = (0..height / 16) + .map(|_| ChunkSection::new_empty()) + .collect::>() + .into_boxed_slice(); + let chunk = Chunk::new( + Sections::from_owned(sections), + chunk_pos, + min_y, + height, + Arc::downgrade(world), + ); + assert!( + chunk + .set_block_state_for_generation( + ChunkStatus::Empty, + pos, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + ) + .is_some() + ); + drop(chunk.promote_to_full()); + holder.insert_chunk(chunk, ChunkStatus::Full); + + assert_eq!( + local.block_state(&world.chunk_map, pos), + Some(vanilla_blocks::STONE.default_state()) + ); + assert_eq!(local.stats().holder_hits, 0); + assert_eq!(local.stats().active_holder_lookups, 2); + drop(scope); + } + + #[test] + fn local_full_holder_cache_does_not_cross_scope_boundaries() { + let fixture = fresh_test_world("local_full_holder_cache_scope_boundary"); + let world = &fixture.world; + init_behaviors(); + let pos = BlockPos::new(5, 64, 5); + let chunk_pos = ChunkPos::from_block_pos(pos); + insert_ready_full_chunk(world, chunk_pos); + set_test_block(world, pos, &vanilla_blocks::STONE); + let mut local = LocalFullChunkHolderCache::new(); + + let first_scope = GameplayChunkLookupCacheScope::enter(&world.chunk_map); + assert_eq!( + local.block_state(&world.chunk_map, pos), + Some(vanilla_blocks::STONE.default_state()) + ); + drop(first_scope); + + let Some((_, old_holder)) = world.chunk_map.chunks.remove_sync(&chunk_pos) else { + panic!("fixture holder should remain active"); + }; + drop(old_holder); + world.unregister_full_chunk_ticks(chunk_pos); + insert_ready_full_chunk(world, chunk_pos); + set_test_block(world, pos, &vanilla_blocks::DIRT); + + let second_scope = GameplayChunkLookupCacheScope::enter(&world.chunk_map); + assert_eq!( + local.block_state(&world.chunk_map, pos), + Some(vanilla_blocks::DIRT.default_state()) + ); + assert_eq!(local.stats().active_holder_lookups, 2); + drop(second_scope); + } } diff --git a/steel-core/src/chunk/heightmap.rs b/steel-core/src/chunk/heightmap.rs index 5f0995a03f3..0dac613e9f3 100644 --- a/steel-core/src/chunk/heightmap.rs +++ b/steel-core/src/chunk/heightmap.rs @@ -591,7 +591,7 @@ impl ChunkHeightmaps { 'sections: for section_idx in (0..sections.len()).rev() { let guard = sections[section_idx].read(); if matches!( - &guard.states, + guard.states(), super::paletted_container::BlockPalette::Homogeneous(state) if state.is_air() ) { continue; @@ -607,7 +607,7 @@ impl ChunkHeightmaps { continue; } - let state = guard.states.get_at_index(layer_start + column_index); + let state = guard.states().get_at_index(layer_start + column_index); let matched_mask = heightmap_opacity_mask(state, *pending_mask); if matched_mask == 0 { continue; diff --git a/steel-core/src/chunk/light/sky_sources.rs b/steel-core/src/chunk/light/sky_sources.rs index e0ab0f07a9a..9498ba0d6b5 100644 --- a/steel-core/src/chunk/light/sky_sources.rs +++ b/steel-core/src/chunk/light/sky_sources.rs @@ -163,7 +163,7 @@ impl ChunkSkyLightSources { } for y in (0..CHUNK_EDGE).rev() { - let bottom_state = section.states.get(x, y, z); + let bottom_state = section.states().get(x, y, z); if Self::is_edge_occluded(top_state, bottom_state) { return top_y; } diff --git a/steel-core/src/chunk/light/workset.rs b/steel-core/src/chunk/light/workset.rs index d171d82b10d..b70315033e4 100644 --- a/steel-core/src/chunk/light/workset.rs +++ b/steel-core/src/chunk/light/workset.rs @@ -304,7 +304,7 @@ impl LightSectionReadCache<'_> { } let (local_x, local_y, local_z) = local_block_coords(cached_block.local_index); - section.states.get(local_x, local_y, local_z) + section.states().get(local_x, local_y, local_z) } /// Returns whether a cached section exists and is non-empty. diff --git a/steel-core/src/chunk/section.rs b/steel-core/src/chunk/section.rs index 01e64ff4ae4..b5679e0ff28 100644 --- a/steel-core/src/chunk/section.rs +++ b/steel-core/src/chunk/section.rs @@ -11,11 +11,14 @@ use std::{ use parking_lot::{RwLockReadGuard, RwLockWriteGuard}; use steel_registry::blocks::block_state_ext::BlockStateExt; -use steel_registry::vanilla_biomes; use steel_registry::{REGISTRY, RegistryEntry}; +use steel_registry::{vanilla_biomes, vanilla_blocks}; use steel_utils::{BlockPos, BlockStateId, ChunkPos, locks::SyncRwLock, serial::WriteTo}; use crate::chunk::paletted_container::{BiomePalette, BlockPalette}; +use crate::physics::{ + block_state_has_extensible_collision_behavior, block_state_may_expand_collision_cursor, +}; /// Lock-free index of sections containing randomly-ticking blocks or fluids. /// @@ -191,6 +194,42 @@ impl<'a> SectionWriteGuard<'a> { was_randomly_ticking, } } + + /// Sets one block through the tracked mutation path. + /// + /// This updates section counters and stable-air occupancy without triggering the conservative + /// invalidation used for arbitrary mutable access. + pub fn set_block_state( + &mut self, + x: usize, + y: usize, + z: usize, + new_state: BlockStateId, + ) -> BlockStateId { + self.guard.set_block_state(x, y, z, new_state) + } + + /// Sets one world-generation block while retaining a conservative exposure certificate. + pub(crate) fn set_block_state_for_generation( + &mut self, + x: usize, + y: usize, + z: usize, + new_state: BlockStateId, + ) -> BlockStateId { + self.guard + .set_block_state_for_generation(x, y, z, new_state) + } + + /// Finalizes deferred world-generation state while keeping derived metadata synchronized. + pub(crate) fn finalize_generation_counts_if_needed(&mut self) { + self.guard.finalize_generation_counts_if_needed(); + } + + /// Rebuilds all section-derived metadata from the current block palette. + pub fn recalculate_counts(&mut self) { + self.guard.recalculate_counts(); + } } impl Deref for SectionWriteGuard<'_> { @@ -203,6 +242,9 @@ impl Deref for SectionWriteGuard<'_> { impl DerefMut for SectionWriteGuard<'_> { fn deref_mut(&mut self) -> &mut Self::Target { + // Arbitrary mutable access can reach the block palette without going through a + // tracked setter. Keep the exposure certificate conservative until a recount rebuilds it. + self.guard.invalidate_stable_air_occupancy(); &mut self.guard } } @@ -234,7 +276,150 @@ pub(crate) struct BlockStateSectionCounts { randomly_ticking_fluid: bool, } -const BLOCKS_PER_SECTION: u16 = 16 * 16 * 16; +const BLOCKS_PER_SECTION: u16 = BlockPalette::VOLUME as u16; +const STABLE_AIR_OCCUPANCY_ROW_COUNT: usize = BlockPalette::SIZE * BlockPalette::SIZE; + +/// Section-local occupancy of every state except Vanilla's three immutable air blocks. +/// +/// `Unknown` is deliberately treated as occupied. It is used after arbitrary mutable palette +/// access so an optimization can never hide plugin or future mutation paths that bypass the +/// tracked setters. +#[derive(Debug)] +enum StableAirOccupancy { + Unknown, + Empty, + Full, + Mixed { + rows: Box<[u16; STABLE_AIR_OCCUPANCY_ROW_COUNT]>, + occupied_count: u16, + }, +} + +impl StableAirOccupancy { + fn from_palette(states: &BlockPalette) -> Self { + let has_stable_air = states.maybe_has(is_stable_vanilla_air); + if !has_stable_air { + return Self::Full; + } + if !states.maybe_has(|state| !is_stable_vanilla_air(state)) { + return Self::Empty; + } + + let mut rows = Box::new([0; STABLE_AIR_OCCUPANCY_ROW_COUNT]); + let mut occupied_count = 0_u16; + for index in 0..BlockPalette::VOLUME { + if is_stable_vanilla_air(states.get_at_index(index)) { + continue; + } + let x = index % BlockPalette::SIZE; + let yz_index = index / BlockPalette::SIZE; + let z = yz_index % BlockPalette::SIZE; + let y = yz_index / BlockPalette::SIZE; + rows[y * BlockPalette::SIZE + z] |= 1_u16 << x; + occupied_count += 1; + } + debug_assert!(occupied_count > 0 && occupied_count < BLOCKS_PER_SECTION); + Self::Mixed { + rows, + occupied_count, + } + } + + fn record_change( + &mut self, + x: usize, + y: usize, + z: usize, + old_state: BlockStateId, + new_state: BlockStateId, + ) { + let old_occupied = !is_stable_vanilla_air(old_state); + let new_occupied = !is_stable_vanilla_air(new_state); + if old_occupied == new_occupied { + return; + } + + let row_index = y * BlockPalette::SIZE + z; + let bit = 1_u16 << x; + match self { + Self::Unknown => {} + Self::Empty => { + debug_assert!(new_occupied); + let mut rows = Box::new([0; STABLE_AIR_OCCUPANCY_ROW_COUNT]); + rows[row_index] = bit; + *self = Self::Mixed { + rows, + occupied_count: 1, + }; + } + Self::Full => { + debug_assert!(old_occupied); + let mut rows = Box::new([u16::MAX; STABLE_AIR_OCCUPANCY_ROW_COUNT]); + rows[row_index] &= !bit; + *self = Self::Mixed { + rows, + occupied_count: BLOCKS_PER_SECTION - 1, + }; + } + Self::Mixed { + rows, + occupied_count, + } => { + if new_occupied { + rows[row_index] |= bit; + *occupied_count += 1; + if *occupied_count == BLOCKS_PER_SECTION { + *self = Self::Full; + } + } else { + rows[row_index] &= !bit; + *occupied_count -= 1; + if *occupied_count == 0 { + *self = Self::Empty; + } + } + } + } + } + + fn box_is_clear( + &self, + min_x: usize, + max_x: usize, + min_y: usize, + max_y: usize, + min_z: usize, + max_z: usize, + ) -> bool { + debug_assert!(min_x <= max_x && max_x < BlockPalette::SIZE); + debug_assert!(min_y <= max_y && max_y < BlockPalette::SIZE); + debug_assert!(min_z <= max_z && max_z < BlockPalette::SIZE); + + let Self::Mixed { rows, .. } = self else { + return matches!(self, Self::Empty); + }; + let below_max = if max_x == u16::BITS as usize - 1 { + u16::MAX + } else { + (1_u16 << (max_x + 1)) - 1 + }; + let x_mask = below_max & (u16::MAX << min_x); + for y in min_y..=max_y { + for z in min_z..=max_z { + if rows[y * BlockPalette::SIZE + z] & x_mask != 0 { + return false; + } + } + } + true + } +} + +fn is_stable_vanilla_air(state: BlockStateId) -> bool { + state == vanilla_blocks::AIR.default_state() + || state == vanilla_blocks::CAVE_AIR.default_state() + || state == vanilla_blocks::VOID_AIR.default_state() +} impl Sections { /// Creates a new `Sections` from a box of owned `ChunkSection`s. @@ -492,7 +677,7 @@ impl Sections { #[derive(Debug)] pub struct ChunkSection { /// The block states in the section. - pub states: BlockPalette, + states: BlockPalette, /// The biomes in the section. pub biomes: BiomePalette, /// Number of non-air blocks in this section (0-4096). @@ -505,6 +690,8 @@ pub struct ChunkSection { pub ticking_block_count: u16, /// Number of randomly-ticking fluids in this section (0-4096). ticking_fluid_count: u16, + /// Conservative spatial certificate for immutable Vanilla air. + stable_air_occupancy: StableAirOccupancy, } impl ChunkSection { @@ -521,6 +708,7 @@ impl ChunkSection { fluid_count: 0, ticking_block_count: 0, ticking_fluid_count: 0, + stable_air_occupancy: StableAirOccupancy::Unknown, } } @@ -535,9 +723,16 @@ impl ChunkSection { fluid_count: 0, ticking_block_count: 0, ticking_fluid_count: 0, + stable_air_occupancy: StableAirOccupancy::Empty, } } + /// Returns the section's read-only block palette. + #[must_use] + pub(crate) const fn states(&self) -> &BlockPalette { + &self.states + } + /// Returns true if this section contains no non-air blocks. #[must_use] pub const fn is_empty(&self) -> bool { @@ -571,6 +766,47 @@ impl ChunkSection { .maybe_has(|state| state.get_light_emission() > 0) } + /// Returns whether this section may need Vanilla's expanded collision cursor boundary. + /// + /// Dynamic Vanilla states have no cached collision shape, so Vanilla treats all of them as + /// potentially large. Plugin-owned states receive the same conservative treatment because + /// their behavior may supply a collision shape that is not represented by extracted data. + #[must_use] + pub(crate) fn maybe_has_special_colliding_blocks(&self) -> bool { + self.states + .maybe_has(block_state_may_expand_collision_cursor) + } + + /// Returns whether collision behavior calls for this section must observe live block states. + /// + /// Steel owns and audits the frozen `minecraft` behavior table. Plugin-owned block behavior + /// may mutate other positions while resolving a shape, so those sections cannot use a block + /// state snapshot across callbacks. + #[must_use] + pub(crate) fn maybe_has_extensible_collision_behavior(&self) -> bool { + self.states + .maybe_has(block_state_has_extensible_collision_behavior) + } + + /// Returns whether every block in one inclusive local box is immutable Vanilla air. + #[must_use] + pub(crate) fn stable_air_box_is_clear( + &self, + min_x: usize, + max_x: usize, + min_y: usize, + max_y: usize, + min_z: usize, + max_z: usize, + ) -> bool { + self.stable_air_occupancy + .box_is_clear(min_x, max_x, min_y, max_y, min_z, max_z) + } + + fn invalidate_stable_air_occupancy(&mut self) { + self.stable_air_occupancy = StableAirOccupancy::Unknown; + } + /// Appends block-light source positions in `ScalableLux` local-index order. pub fn append_block_light_sources( &self, @@ -644,6 +880,7 @@ impl ChunkSection { mut counts_for_state: impl FnMut(BlockStateId) -> BlockStateSectionCounts, ) { self.states.finalize_building(); + let stable_air_occupancy = StableAirOccupancy::from_palette(&self.states); let mut non_empty: u16 = 0; let mut fluid: u16 = 0; @@ -682,6 +919,7 @@ impl ChunkSection { self.fluid_count = fluid; self.ticking_block_count = ticking_blocks; self.ticking_fluid_count = ticking_fluids; + self.stable_air_occupancy = stable_air_occupancy; } const fn accumulate_counter_traits( @@ -745,6 +983,8 @@ impl ChunkSection { let old_counts = Self::block_state_section_counts(old_state); let new_counts = Self::block_state_section_counts(new_state); self.apply_count_change(old_counts, new_counts); + self.stable_air_occupancy + .record_change(x, y, z, old_state, new_state); } old_state @@ -762,6 +1002,7 @@ impl ChunkSection { z: usize, new_state: BlockStateId, ) -> BlockStateId { + self.invalidate_stable_air_occupancy(); self.states.enter_building_mode(); self.states.set(x, y, z, new_state) } @@ -851,6 +1092,8 @@ mod tests { use super::*; + const SECTION_MAX_LOCAL_COORDINATE: usize = BlockPalette::SIZE - 1; + fn plains_biomes() -> BiomePalette { BiomePalette::Homogeneous(vanilla_biomes::PLAINS.id() as u16) } @@ -860,6 +1103,107 @@ mod tests { init_behaviors(); } + fn entire_section_is_stable_air(section: &ChunkSection) -> bool { + section.stable_air_box_is_clear( + 0, + SECTION_MAX_LOCAL_COORDINATE, + 0, + SECTION_MAX_LOCAL_COORDINATE, + 0, + SECTION_MAX_LOCAL_COORDINATE, + ) + } + + #[test] + fn stable_air_occupancy_tracks_canonical_air_and_non_air_transitions() { + init_test_behaviors(); + let mut section = ChunkSection::new_empty(); + + assert!(entire_section_is_stable_air(§ion)); + section.set_block_state(2, 3, 4, vanilla_blocks::CAVE_AIR.default_state()); + section.set_block_state(5, 6, 7, vanilla_blocks::VOID_AIR.default_state()); + assert!(entire_section_is_stable_air(§ion)); + + section.set_block_state(2, 3, 4, vanilla_blocks::STONE.default_state()); + assert!(!section.stable_air_box_is_clear(2, 2, 3, 3, 4, 4)); + assert!(section.stable_air_box_is_clear(3, 3, 3, 3, 4, 4)); + section.set_block_state(2, 3, 4, vanilla_blocks::AIR.default_state()); + assert!(entire_section_is_stable_air(§ion)); + + let mut full = ChunkSection::new_with_biomes( + BlockPalette::Homogeneous(vanilla_blocks::STONE.default_state()), + plains_biomes(), + ); + full.recalculate_counts(); + assert!(!full.stable_air_box_is_clear(0, 0, 0, 0, 0, 0)); + full.set_block_state(0, 0, 0, vanilla_blocks::AIR.default_state()); + assert!(full.stable_air_box_is_clear(0, 0, 0, 0, 0, 0)); + assert!(!full.stable_air_box_is_clear(1, 1, 0, 0, 0, 0)); + full.set_block_state(0, 0, 0, vanilla_blocks::STONE.default_state()); + assert!(!full.stable_air_box_is_clear(0, 0, 0, 0, 0, 0)); + } + + #[test] + fn arbitrary_palette_mutation_invalidates_stable_air_certificate_until_recount() { + init_test_behaviors(); + let holder = SectionHolder::new(ChunkSection::new_empty()); + { + let mut guard = holder.write(); + guard.states.enter_building_mode(); + } + assert!(!entire_section_is_stable_air(&holder.read())); + + holder.write().recalculate_counts(); + assert!(entire_section_is_stable_air(&holder.read())); + } + + #[test] + fn write_guard_setter_keeps_stable_air_occupancy_tracked() { + const LOCAL_X: usize = 2; + const LOCAL_Y: usize = 3; + const LOCAL_Z: usize = 4; + + init_test_behaviors(); + let holder = SectionHolder::new(ChunkSection::new_empty()); + + holder.write().set_block_state( + LOCAL_X, + LOCAL_Y, + LOCAL_Z, + vanilla_blocks::STONE.default_state(), + ); + assert!( + !holder + .read() + .stable_air_box_is_clear(LOCAL_X, LOCAL_X, LOCAL_Y, LOCAL_Y, LOCAL_Z, LOCAL_Z,) + ); + + holder.write().set_block_state( + LOCAL_X, + LOCAL_Y, + LOCAL_Z, + vanilla_blocks::AIR.default_state(), + ); + assert!(entire_section_is_stable_air(&holder.read())); + } + + #[test] + fn raw_bulk_writes_invalidate_stable_air_certificate_until_recount() { + init_test_behaviors(); + let sections = Sections::from_owned(vec![ChunkSection::new_empty()].into_boxed_slice()); + let holder = §ions.sections[0]; + + sections.write_block_batch(&[(1, 2, 3, vanilla_blocks::STONE.default_state())]); + assert!(!entire_section_is_stable_air(&holder.read())); + holder.write().recalculate_counts(); + assert!(!holder.read().stable_air_box_is_clear(1, 1, 2, 2, 3, 3)); + + sections.write_column_blocks(1, 3, &[(2, vanilla_blocks::AIR.default_state())]); + assert!(!entire_section_is_stable_air(&holder.read())); + holder.write().recalculate_counts(); + assert!(entire_section_is_stable_air(&holder.read())); + } + #[test] fn recount_uses_homogeneous_palette_frequency() { init_test_behaviors(); diff --git a/steel-core/src/chunk_saver/storage/chunk.rs b/steel-core/src/chunk_saver/storage/chunk.rs index c88cf964f77..c16e6668328 100644 --- a/steel-core/src/chunk_saver/storage/chunk.rs +++ b/steel-core/src/chunk_saver/storage/chunk.rs @@ -271,12 +271,12 @@ impl ChunkStorage { builder: &mut ChunkBuilder, ) -> PersistentSection { let mut section = section.write(); - if matches!(§ion.states, PalettedContainer::Building(_)) { + if matches!(section.states(), PalettedContainer::Building(_)) { section.recalculate_counts(); } let biomes = Self::biomes_to_persistent(§ion.biomes, builder); - match §ion.states { + match section.states() { PalettedContainer::Homogeneous(block_id) => { let block_idx = builder.ensure_block_state(*block_id); PersistentSection::Homogeneous { diff --git a/steel-core/src/entity/base/mod.rs b/steel-core/src/entity/base/mod.rs index 9e8b49d5d64..64218144e89 100644 --- a/steel-core/src/entity/base/mod.rs +++ b/steel-core/src/entity/base/mod.rs @@ -17,13 +17,11 @@ pub use persistence::{EntityBaseLoad, EntityBaseSaveData}; pub use relationships::PendingWorldChangeToken; use relationships::{EntityLifecycleState, EntityRelationshipState}; -use std::{ - collections::VecDeque, - sync::{Arc, Weak}, -}; +use std::sync::{Arc, Weak}; use glam::DVec3; use simdnbt::owned::NbtCompound; +use smallvec::SmallVec; use steel_math::{DEGREE_90, DEGREE_360}; use steel_registry::entity_data::EntityPose; use steel_registry::entity_type::EntityDimensions; @@ -89,29 +87,27 @@ pub(crate) struct EntityPhysicsStateInput { #[derive(Debug, Default)] struct EntityMovementTrace { - movement_this_tick: VecDeque, - final_movements_this_tick: Vec, + // One segment is the common per-tick case. `EntityMovement` is large and both buffers live on + // every entity, so larger inline capacities would optimize uncommon multi-movement ticks at a + // persistent per-entity memory cost. + movement_this_tick: SmallVec<[EntityMovement; 1]>, + final_movements_this_tick: SmallVec<[EntityMovement; 1]>, } impl EntityMovementTrace { fn record(&mut self, movement: EntityMovement) { if self.movement_this_tick.len() >= MOVEMENT_TRACE_LIMIT { - let first = self.movement_this_tick.pop_front(); - let second = self.movement_this_tick.pop_front(); - match (first, second) { - (Some(first), Some(second)) => self - .movement_this_tick - .push_front(EntityMovement::new(first.from(), second.to())), - (Some(first), None) => self.movement_this_tick.push_front(first), - (None, _) => {} - } + let first = self.movement_this_tick.remove(0); + let second = self.movement_this_tick.remove(0); + self.movement_this_tick + .insert(0, EntityMovement::new(first.from(), second.to())); } - self.movement_this_tick.push_back(movement); + self.movement_this_tick.push(movement); } fn remove_latest_recording(&mut self) { - self.movement_this_tick.pop_back(); + self.movement_this_tick.pop(); } fn reset(&mut self) { @@ -123,7 +119,7 @@ impl EntityMovementTrace { &mut self, old_position: DVec3, position: DVec3, - ) -> Vec { + ) -> SmallVec<[EntityMovement; 1]> { self.final_movements_this_tick.clear(); self.final_movements_this_tick .extend(self.movement_this_tick.drain(..)); @@ -139,11 +135,11 @@ impl EntityMovementTrace { .push(EntityMovement::new(old_position, position)); } - self.final_movements_this_tick.as_slice().to_vec() + self.final_movements_this_tick.iter().copied().collect() } - fn last_for_block_effects(&self) -> Vec { - self.final_movements_this_tick.as_slice().to_vec() + fn last_for_block_effects(&self) -> SmallVec<[EntityMovement; 1]> { + self.final_movements_this_tick.iter().copied().collect() } } @@ -1171,7 +1167,7 @@ impl EntityBase { } /// Takes and finalizes this tick's movement segments for block-contact effects. - pub fn take_movements_for_block_effects(&self) -> Vec { + pub fn take_movements_for_block_effects(&self) -> SmallVec<[EntityMovement; 1]> { let (old_position, position) = { let state = self.state.lock(); (state.old_position, state.position) @@ -1183,7 +1179,7 @@ impl EntityBase { } /// Returns the last finalized movement segments for vanilla block-contact effects. - pub fn last_movements_for_block_effects(&self) -> Vec { + pub fn last_movements_for_block_effects(&self) -> SmallVec<[EntityMovement; 1]> { self.movement_trace.lock().last_for_block_effects() } diff --git a/steel-core/src/entity/base/tests.rs b/steel-core/src/entity/base/tests.rs index 45eaa3617b4..0cef5b28905 100644 --- a/steel-core/src/entity/base/tests.rs +++ b/steel-core/src/entity/base/tests.rs @@ -1083,8 +1083,8 @@ fn movement_trace_falls_back_to_old_position_when_no_moves_were_recorded() { let movements = base.take_movements_for_block_effects(); assert_eq!( - movements, - vec![EntityMovement::new( + movements.as_slice(), + &[EntityMovement::new( DVec3::new(-1.0, 2.0, -3.0), DVec3::new(1.0, 2.0, 3.0) )] @@ -1131,8 +1131,8 @@ fn movement_trace_appends_direct_position_change_after_recorded_moves() { let movements = base.take_movements_for_block_effects(); assert_eq!( - movements, - vec![ + movements.as_slice(), + &[ EntityMovement::with_axis_dependent_original_movement( DVec3::new(0.0, 64.0, 0.0), DVec3::new(1.0, 64.0, 0.0), @@ -1163,8 +1163,8 @@ fn movement_trace_removes_latest_movement_recording() { let movements = base.take_movements_for_block_effects(); assert_eq!( - movements, - vec![EntityMovement::new(DVec3::ZERO, DVec3::new(1.0, 0.0, 0.0))] + movements.as_slice(), + &[EntityMovement::new(DVec3::ZERO, DVec3::new(1.0, 0.0, 0.0))] ); } diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index 8a0fdbd8a07..d3037cce92b 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -2,6 +2,7 @@ use std::mem; use glam::{DVec3, IVec3}; use rustc_hash::FxHashSet; +use smallvec::SmallVec; use steel_registry::blocks::shapes::VoxelShape; use steel_utils::{BlockPos, WorldAabb, axis::Axis}; @@ -10,6 +11,44 @@ const CLIP_EPSILON: f64 = 1.0e-7; const CORNER_HIT_EPSILON: f64 = 1.0e-5; const ENTITY_INSIDE_SWEEP_INFLATE_EPSILON: f64 = 1.0e-7; +/// Deduplicates candidate positions within one geometric sweep of one movement segment. +/// Ordinary sweeps use inline storage; unusually large sweeps switch to a hash set. +enum MovementVisitedBlocks { + Inline(SmallVec<[BlockPos; 16]>), + Hashed(FxHashSet), +} + +impl Default for MovementVisitedBlocks { + fn default() -> Self { + Self::Inline(SmallVec::new()) + } +} + +impl MovementVisitedBlocks { + fn insert(&mut self, pos: BlockPos) -> bool { + match self { + Self::Hashed(visited) => visited.insert(pos), + Self::Inline(inline) => { + if inline.contains(&pos) { + return false; + } + if inline.len() < inline.inline_size() { + inline.push(pos); + return true; + } + + let mut visited = FxHashSet::default(); + visited.reserve(inline.len() + 1); + visited.extend(inline.drain(..)); + let inserted = visited.insert(pos); + debug_assert!(inserted, "inline duplicate check must precede the fallback"); + *self = Self::Hashed(visited); + true + } + } + } +} + pub(super) fn for_each_block_intersected_between( from: DVec3, to: DVec3, @@ -29,14 +68,17 @@ pub(super) fn for_each_block_intersected_between( return Some(last_iteration + 1); } - let mut visited = FxHashSet::default(); + let mut visited = MovementVisitedBlocks::default(); let aabb_at_start = aabb_at_target.translate(-travel); - for pos in between_corners_in_direction(aabb_at_start, travel) { + if !for_each_between_corners_in_direction(aabb_at_start, travel, |pos| { last_iteration = 0; if !visitor(pos, 0) { - return None; + return false; } visited.insert(pos); + true + }) { + return None; } let iterations = ({ @@ -47,13 +89,16 @@ pub(super) fn for_each_block_intersected_between( add_collisions_along_travel(&mut visited, travel, aabb_at_target, &mut traced_visitor) })?; - for pos in between_corners_in_direction(aabb_at_target, travel) { + if !for_each_between_corners_in_direction(aabb_at_target, travel, |pos| { if visited.insert(pos) { last_iteration = iterations + 1; if !visitor(pos, iterations + 1) { - return None; + return false; } } + true + }) { + return None; } Some(last_iteration + 1) @@ -94,7 +139,7 @@ pub(super) fn collided_with_aabb_moving_from( reason = "keeps the vanilla BlockGetter.addCollisionsAlongTravel port auditable" )] fn add_collisions_along_travel( - visited: &mut FxHashSet, + visited: &mut MovementVisitedBlocks, travel: DVec3, aabb_at_target: WorldAabb, visitor: &mut impl FnMut(BlockPos, i32) -> bool, @@ -197,10 +242,18 @@ fn add_collisions_along_travel( (corner_hit_z - box_size.z * f64::from(corner_dir.z)).floor() as i32, ); - for pos in between_corners_in_direction_between(corner_block, opposite_corner, travel) { - if visited.insert(pos) && !visitor(pos, iterations) { - return None; - } + if !for_each_between_corners_in_direction_between( + corner_block, + opposite_corner, + travel, + |pos| { + if visited.insert(pos) && !visitor(pos, iterations) { + return false; + } + true + }, + ) { + return None; } } } @@ -232,7 +285,11 @@ pub(super) fn for_each_block_in_aabb( true } -fn between_corners_in_direction(aabb: WorldAabb, direction: DVec3) -> Vec { +fn for_each_between_corners_in_direction( + aabb: WorldAabb, + direction: DVec3, + visitor: impl FnMut(BlockPos) -> bool, +) -> bool { let first_corner = IVec3::new( aabb.min(Axis::X).floor() as i32, aabb.min(Axis::Y).floor() as i32, @@ -243,14 +300,15 @@ fn between_corners_in_direction(aabb: WorldAabb, direction: DVec3) -> Vec Vec { + mut visitor: impl FnMut(BlockPos) -> bool, +) -> bool { let min_corner = first_corner.min(second_corner); let max_corner = first_corner.max(second_corner); let diff = max_corner - min_corner; @@ -281,8 +339,6 @@ fn between_corners_in_direction_between( let first_max = axis_value(diff, first_axis); let second_max = axis_value(diff, second_axis); let third_max = axis_value(diff, third_axis); - let mut positions = Vec::new(); - for first_index in 0..=first_max { for second_index in 0..=second_max { for third_index in 0..=third_max { @@ -290,12 +346,14 @@ fn between_corners_in_direction_between( + first_step * first_index + second_step * second_index + third_step * third_index; - positions.push(BlockPos::new(position.x, position.y, position.z)); + if !visitor(BlockPos::new(position.x, position.y, position.z)) { + return false; + } } } } - positions + true } fn clip_block(pos: BlockPos, from: DVec3, to: DVec3) -> Option { @@ -562,4 +620,19 @@ mod tests { assert!(completed.is_none()); assert_eq!(visited.len(), 1); } + + #[test] + fn long_trace_uses_spill_path_without_duplicates() { + let positions = visited_positions( + DVec3::new(0.5, 64.0, 0.5), + DVec3::new(24.5, 64.0, 0.5), + WorldAabb::new(24.2, 64.0, 0.2, 24.8, 64.9, 0.8), + ); + let expected = (0..=24) + .map(|x| BlockPos::new(x, 64, 0)) + .collect::>(); + + assert!(positions.len() > 16); + assert_eq!(positions, expected); + } } diff --git a/steel-core/src/entity/entities/mod.rs b/steel-core/src/entity/entities/mod.rs index 27994ec261c..c6d261be7ca 100644 --- a/steel-core/src/entity/entities/mod.rs +++ b/steel-core/src/entity/entities/mod.rs @@ -6,7 +6,7 @@ pub mod objects; pub use mobs::hostile::EndermiteEntity; pub use mobs::passive::{ChickenEntity, CowEntity, PigEntity, SheepEntity}; pub use objects::display_ui::{BlockDisplayEntity, ItemFrameEntity, LeashFenceKnotEntity}; -pub use objects::explosives::EndCrystalEntity; +pub use objects::explosives::{EndCrystalEntity, PrimedTntEntity}; pub use objects::items::{ExperienceOrbEntity, FallingBlockEntity, ItemEntity}; pub use objects::projectiles::{ EnderPearlEntity, EyeOfEnderEntity, FireworkRocketEntity, FishingHookEntity, SnowballEntity, diff --git a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs index f43f4e11442..9bbad22aebc 100644 --- a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs +++ b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs @@ -18,7 +18,7 @@ use steel_utils::{BlockPos, Direction, DowncastType, DowncastTypeKey, WorldAabb, use crate::entity::{ Entity, EntityBase, EntityBaseLoad, EntityBaseState, EntitySyncedData, ItemFrame, }; -use crate::world::World; +use crate::world::{Explosion, World}; /// Item frame state needed by end-city structure markers. /// @@ -248,6 +248,13 @@ impl Entity for ItemFrameEntity { true } + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + explosion + .direct_source_entity() + .is_some_and(Entity::is_in_water) + || !explosion.should_affect_blocklike_entities() + } + fn synced_data(&self) -> Option<&dyn EntitySyncedData> { Some(&self.entity_data) } diff --git a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs index c1459e1cb06..9912041fa7c 100644 --- a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs +++ b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs @@ -9,7 +9,7 @@ use crate::entity::{ next_entity_id, }; use crate::player::Player; -use crate::world::World; +use crate::world::{Explosion, World}; use glam::DVec3; use steel_macros::entity_behavior; use steel_registry::blocks::block_state_ext::BlockStateExt as _; @@ -322,6 +322,13 @@ impl Entity for LeashFenceKnotEntity { true } + + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + explosion + .direct_source_entity() + .is_some_and(Entity::is_in_water) + || !explosion.should_affect_blocklike_entities() + } } #[cfg(test)] diff --git a/steel-core/src/entity/entities/objects/explosives/mod.rs b/steel-core/src/entity/entities/objects/explosives/mod.rs index 9ab6f0635d2..989652455d0 100644 --- a/steel-core/src/entity/entities/objects/explosives/mod.rs +++ b/steel-core/src/entity/entities/objects/explosives/mod.rs @@ -1,5 +1,7 @@ //! Explosive entity implementations. mod end_crystal; +mod primed_tnt; pub use end_crystal::EndCrystalEntity; +pub use primed_tnt::PrimedTntEntity; diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs new file mode 100644 index 00000000000..194db14b56a --- /dev/null +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -0,0 +1,400 @@ +//! Primed TNT entity behavior. + +use std::{ + f32::consts::TAU, + sync::{Arc, Weak}, +}; + +use glam::DVec3; +use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView; +use simdnbt::owned::NbtCompound; +use steel_macros::entity_behavior; +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_registry::entity_type::EntityTypeRef; +use steel_registry::fluid::FluidState; +use steel_registry::vanilla_blocks; +use steel_registry::vanilla_entity_data::PrimedTntEntityData; +use steel_registry::vanilla_game_rules::TNT_EXPLODES; +use steel_utils::locks::SyncMutex; +use steel_utils::random::Random; +use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey}; + +use crate::block_entity::block_state_nbt; +use crate::entity::damage::DamageSource; +use crate::entity::{ + Entity, EntityBase, EntityBaseLoad, EntityMovementEmission, EntityReference, EntitySyncedData, + RemovalReason, SharedEntity, +}; +use crate::physics::MoverType; +use crate::world::{ + DefaultExplosionDamageCalculator, Explosion, ExplosionBlockReader, ExplosionDamageCalculator, + ExplosionInteraction, ExplosionOptions, ImmutableExplosionBlockCalculator, World, +}; + +const DEFAULT_EXPLOSION_POWER: f32 = 4.0; +const MAX_EXPLOSION_POWER: f32 = 128.0; +const GRAVITY: f64 = 0.04; +const AIR_DRAG: f32 = 0.98; +const INITIAL_HORIZONTAL_SPEED: f64 = 0.02; +const INITIAL_VERTICAL_SPEED: f32 = 0.2; +const SHORT_FUSE_RANDOM_RANGE_DIVISOR: i32 = 4; +const SHORT_FUSE_MINIMUM_DIVISOR: i32 = 8; +const MIN_SHORT_FUSE_RANDOM_RANGE: i32 = 1; +const EXPLOSION_HEIGHT_FRACTION: f32 = 0.0625; +const GROUND_HORIZONTAL_DRAG: f64 = 0.7; +const GROUND_VERTICAL_BOUNCE: f64 = -0.5; + +struct PrimedTntState { + owner: Option, + used_portal: bool, + explosion_power: f32, +} + +impl PrimedTntState { + const fn new() -> Self { + Self { + owner: None, + used_portal: false, + explosion_power: DEFAULT_EXPLOSION_POWER, + } + } +} + +struct NetherPortalPreservingExplosionDamageCalculator; + +impl ExplosionDamageCalculator for NetherPortalPreservingExplosionDamageCalculator { + fn block_explosion_resistance( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + if state.get_block() == &vanilla_blocks::NETHER_PORTAL { + return None; + } + DefaultExplosionDamageCalculator + .block_explosion_resistance(explosion, world, pos, state, fluid) + } + + fn should_block_explode( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + power: f32, + ) -> bool { + if state.get_block() == &vanilla_blocks::NETHER_PORTAL { + return false; + } + DefaultExplosionDamageCalculator.should_block_explode(explosion, world, pos, state, power) + } +} + +impl ImmutableExplosionBlockCalculator for NetherPortalPreservingExplosionDamageCalculator { + fn bounded_block_read_radius(&self) -> Option { + Some(0) + } + + fn can_cache_explosion_resistance(&self) -> bool { + true + } + + fn explosion_resistance( + &self, + reader: &dyn ExplosionBlockReader, + pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + if state.get_block() == &vanilla_blocks::NETHER_PORTAL { + return None; + } + ::explosion_resistance( + &DefaultExplosionDamageCalculator, + reader, + pos, + state, + fluid, + ) + } + + fn should_explode( + &self, + reader: &dyn ExplosionBlockReader, + pos: BlockPos, + state: BlockStateId, + power: f32, + ) -> bool { + if state.get_block() == &vanilla_blocks::NETHER_PORTAL { + return false; + } + ::should_explode( + &DefaultExplosionDamageCalculator, + reader, + pos, + state, + power, + ) + } +} + +static USED_PORTAL_DAMAGE_CALCULATOR: NetherPortalPreservingExplosionDamageCalculator = + NetherPortalPreservingExplosionDamageCalculator; +static DEFAULT_TNT_BLOCK_CALCULATOR: DefaultExplosionDamageCalculator = + DefaultExplosionDamageCalculator; + +/// Vanilla primed TNT entity. +#[entity_behavior(class = "PrimedTnt")] +pub struct PrimedTntEntity { + base: EntityBase, + entity_type: EntityTypeRef, + entity_data: SyncMutex, + state: SyncMutex, +} + +// SAFETY: This key is owned by Steel and uniquely identifies `PrimedTntEntity`. +unsafe impl DowncastType for PrimedTntEntity { + const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:entity/primed_tnt"); +} + +impl PrimedTntEntity { + /// Default fuse duration in ticks. + pub const DEFAULT_FUSE_TIME: i32 = 80; + /// Sentinel used by Vanilla callers for an absent fuse. + pub const NO_FUSE: i32 = -1; + + /// Creates an unprimed TNT entity for the entity factory. + #[must_use] + pub fn new(entity_type: EntityTypeRef, id: i32, position: DVec3, world: Weak) -> Self { + Self { + base: EntityBase::new(id, position, entity_type.dimensions, world), + entity_type, + entity_data: SyncMutex::new(PrimedTntEntityData::new()), + state: SyncMutex::new(PrimedTntState::new()), + } + } + + /// Creates a normally primed TNT entity with Vanilla's randomized launch motion. + #[must_use] + pub fn primed( + entity_type: EntityTypeRef, + id: i32, + position: DVec3, + world: &Arc, + owner: Option<&dyn Entity>, + ) -> Self { + let entity = Self::new(entity_type, id, position, Arc::downgrade(world)); + // Vanilla narrows 2Ï€ to `float` before Java widens it for this multiplication. + let angle = world.with_random(Random::next_f64) * f64::from(TAU); + entity.set_velocity(DVec3::new( + -angle.sin() * INITIAL_HORIZONTAL_SPEED, + f64::from(INITIAL_VERTICAL_SPEED), + -angle.cos() * INITIAL_HORIZONTAL_SPEED, + )); + entity.state.lock().owner = owner.map(|owner| EntityReference::from_uuid(owner.uuid())); + entity + } + + /// Creates a primed TNT entity from saved data. + #[must_use] + pub fn from_saved(entity_type: EntityTypeRef, load: EntityBaseLoad) -> Self { + Self { + base: EntityBase::from_load(load, entity_type.dimensions), + entity_type, + entity_data: SyncMutex::new(PrimedTntEntityData::new()), + state: SyncMutex::new(PrimedTntState::new()), + } + } + + /// Returns the remaining fuse time in ticks. + #[must_use] + pub fn fuse(&self) -> i32 { + *self.entity_data.lock().fuse.get() + } + + /// Sets the remaining fuse time in ticks. + pub fn set_fuse(&self, fuse: i32) { + self.entity_data.lock().fuse.set(fuse); + } + + fn decrement_fuse(&self) -> i32 { + let mut entity_data = self.entity_data.lock(); + let fuse = *entity_data.fuse.get() - 1; + entity_data.fuse.set(fuse); + fuse + } + + /// Returns the block state rendered by the client. + #[must_use] + pub fn block_state(&self) -> BlockStateId { + *self.entity_data.lock().block_state.get() + } + + /// Sets the block state rendered by the client. + pub fn set_block_state(&self, state: BlockStateId) { + self.entity_data.lock().block_state.set(state); + } + + /// Returns Vanilla's shortened chain-reaction fuse for `fuse`. + #[must_use] + pub fn get_random_short_fuse(world: &World, fuse: i32) -> i32 { + let random_range = + (fuse / SHORT_FUSE_RANDOM_RANGE_DIVISOR).max(MIN_SHORT_FUSE_RANDOM_RANGE); + world.with_random(|random| random.next_i32_bounded(random_range)) + + fuse / SHORT_FUSE_MINIMUM_DIVISOR + } + + fn owner_reference(&self) -> Option { + self.state.lock().owner.clone() + } + + fn owner(&self) -> Option { + let world = self.level()?; + self.owner_reference()? + .get_entity(&world) + .filter(|owner| owner.is_living_entity()) + } + + fn explode(self: &Arc, world: &Arc) { + if !world.get_game_rule(&TNT_EXPLODES) { + return; + } + + let position = self.position(); + let center = DVec3::new( + position.x, + position.y + + f64::from(self.base.dimensions().height) * f64::from(EXPLOSION_HEIGHT_FRACTION), + position.z, + ); + let (explosion_power, used_portal) = { + let state = self.state.lock(); + (state.explosion_power, state.used_portal) + }; + let mut options = ExplosionOptions::new(center, explosion_power, ExplosionInteraction::Tnt); + options.source = Some(Arc::clone(self) as SharedEntity); + options.immutable_block_calculator = Some(if used_portal { + &USED_PORTAL_DAMAGE_CALCULATOR + } else { + &DEFAULT_TNT_BLOCK_CALCULATOR + }); + if used_portal { + options.damage_calculator = Some(&USED_PORTAL_DAMAGE_CALCULATOR); + } + world.explode(options); + } +} + +impl Entity for PrimedTntEntity { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + self.entity_type + } + + fn tick(self: Arc) { + self.handle_portal(); + self.apply_gravity(); + Arc::clone(&self).move_entity(MoverType::SelfMovement, self.velocity()); + self.apply_effects_from_blocks(); + let mut velocity = self.velocity() * f64::from(AIR_DRAG); + if self.on_ground() { + velocity = DVec3::new( + velocity.x * GROUND_HORIZONTAL_DRAG, + velocity.y * GROUND_VERTICAL_BOUNCE, + velocity.z * GROUND_HORIZONTAL_DRAG, + ); + } + self.set_velocity(velocity); + + let fuse = self.decrement_fuse(); + if fuse <= 0 { + self.set_removed(RemovalReason::Discarded); + if let Some(world) = self.level() { + self.explode(&world); + } + } else { + self.refresh_fluid_contact_with_currents(); + self.base.reset_fall_distance_in_water(); + // Vanilla's remaining-fuse smoke particle is client-local. + } + } + + fn blocks_building(&self) -> bool { + true + } + + fn is_pickable(&self) -> bool { + !self.is_removed() + } + + fn is_hard_collision_relevant(&self) -> bool { + false + } + + fn movement_emission(&self) -> EntityMovementEmission { + EntityMovementEmission::None + } + + fn get_default_gravity(&self) -> f64 { + GRAVITY + } + + fn synced_data(&self) -> Option<&dyn EntitySyncedData> { + Some(&self.entity_data) + } + + fn explosion_indirect_source(&self) -> Option { + self.owner() + } + + fn explosion_damage_origin(&self) -> DVec3 { + self.position() + } + + fn on_teleported(&self) { + self.state.lock().used_portal = true; + } + + fn save_additional(&self, nbt: &mut NbtCompound) { + nbt.insert("fuse", self.fuse() as i16); + nbt.insert("block_state", block_state_nbt::save(self.block_state())); + let state = self.state.lock(); + if state.explosion_power != DEFAULT_EXPLOSION_POWER { + nbt.insert("explosion_power", state.explosion_power); + } + if let Some(owner) = &state.owner { + owner.store(nbt, "owner"); + } + } + + fn load_additional(&self, nbt: BorrowedNbtCompoundView<'_, '_>) { + self.set_fuse(i32::from( + nbt.short("fuse").unwrap_or(Self::DEFAULT_FUSE_TIME as i16), + )); + self.set_block_state( + nbt.compound("block_state") + .and_then(block_state_nbt::load) + .unwrap_or_else(|| vanilla_blocks::TNT.default_state()), + ); + let mut state = self.state.lock(); + state.explosion_power = nbt + .float("explosion_power") + .unwrap_or(DEFAULT_EXPLOSION_POWER) + .clamp(0.0, MAX_EXPLOSION_POWER); + state.owner = EntityReference::read(&nbt, "owner"); + } + + fn hurt(&self, _world: &World, _source: &DamageSource, _amount: f32) -> bool { + false + } +} + +#[cfg(test)] +#[path = "primed_tnt/tests.rs"] +mod tests; diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs new file mode 100644 index 00000000000..7ec0ac25438 --- /dev/null +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs @@ -0,0 +1,228 @@ +use steel_protocol::packets::game::RelativeMovement; +use steel_registry::blocks::properties::BlockStateProperties; +use steel_registry::{init_vanilla_registry, vanilla_damage_types, vanilla_entities}; +use steel_utils::random::legacy_random::LegacyRandom; +use steel_utils::types::UpdateFlags; +use steel_utils::{ChunkPos, Identifier}; + +use super::*; +use crate::behavior::init_behaviors; +use crate::entity::entities::PigEntity; +use crate::entity::{LivingEntity, change_entity_world, init_entities, next_entity_id}; +use crate::player::ResetReason; +use crate::portal::{TeleportPostTransition, TeleportTransition}; +use crate::test_support::{ + TestPlayerBuilder, TestWorld, fresh_test_world, insert_ready_full_chunk, test_domain, +}; + +// Center the fixture in the only loaded chunk so movement stays in Full chunk data. +const TEST_TNT_BLOCK_POS: BlockPos = BlockPos::new(8, 64, 8); + +fn test_tnt_position() -> DVec3 { + let (x, y, z) = TEST_TNT_BLOCK_POS.get_bottom_center(); + DVec3::new(x, y, z) +} + +fn ready_tnt_world(key: &'static str) -> TestWorld { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world(key); + insert_ready_full_chunk(&fixture.world, ChunkPos::from_block_pos(TEST_TNT_BLOCK_POS)); + fixture +} + +fn primed_tnt(world: &Arc, position: DVec3) -> Arc { + Arc::new(PrimedTntEntity::new( + &vanilla_entities::TNT, + next_entity_id(), + position, + Arc::downgrade(world), + )) +} + +#[test] +fn priming_applies_vanilla_float_tau_launch_motion() { + const SEED: i64 = 0x7A71; + const EXPECTED_INITIAL_MOTION_X: f64 = -0.003_337_386_497_296_714_6; + const EXPECTED_INITIAL_MOTION_Z: f64 = -0.019_719_580_405_466_58; + // Java's `Math.sin` may differ from Rust's in the final bits. + const INITIAL_MOTION_TOLERANCE: f64 = 1.0e-12; + + let fixture = fresh_test_world("primed_tnt_initial_motion"); + let world = &fixture.world; + world.set_random_seed_for_test(SEED); + + let entity = PrimedTntEntity::primed( + &vanilla_entities::TNT, + next_entity_id(), + test_tnt_position(), + world, + None, + ); + + let velocity = entity.velocity(); + assert!((velocity.x - EXPECTED_INITIAL_MOTION_X).abs() <= INITIAL_MOTION_TOLERANCE); + assert_eq!(velocity.y.to_bits(), f64::from(0.2_f32).to_bits()); + assert!((velocity.z - EXPECTED_INITIAL_MOTION_Z).abs() <= INITIAL_MOTION_TOLERANCE); + let mut expected = LegacyRandom::from_seed(SEED as u64); + let _ = expected.next_f64(); + assert_eq!(world.with_random(Random::next_i64), expected.next_i64()); +} + +#[test] +fn cross_world_recreation_preserves_owner_and_explosion_attribution() { + init_vanilla_registry(); + init_behaviors(); + init_entities(); + let domain = test_domain("primed_tnt_recreation", &["source", "target"]); + let source = domain + .get(&Identifier::new_static("primed_tnt_recreation", "source")) + .expect("source world"); + let target = domain + .get(&Identifier::new_static("primed_tnt_recreation", "target")) + .expect("target world"); + let position = test_tnt_position(); + let chunk = ChunkPos::from_entity_pos(position); + insert_ready_full_chunk(source, chunk); + insert_ready_full_chunk(target, chunk); + let player = TestPlayerBuilder::new(Arc::clone(source), "Owner", next_entity_id()).build(); + assert!(source.add_player(Arc::clone(&player), ResetReason::InitialJoin)); + let owner: SharedEntity = player; + let previous = Arc::new(PrimedTntEntity::primed( + &vanilla_entities::TNT, + next_entity_id(), + position, + source, + Some(owner.as_ref()), + )); + let previous_entity: SharedEntity = Arc::::clone(&previous); + source + .try_add_entity(Arc::clone(&previous_entity)) + .expect("source TNT should enter the loaded test chunk"); + let transition = TeleportTransition { + target_world: Arc::clone(target), + position, + rotation: previous.rotation(), + velocity: DVec3::ZERO, + relatives: RelativeMovement::NONE, + portal_cooldown: 0, + as_passenger: false, + post_transition: TeleportPostTransition::do_nothing(), + }; + + let recreated = change_entity_world(previous_entity, &transition) + .expect("primed TNT should recreate in the loaded target world"); + let recreated_tnt = steel_utils::Downcast::downcast_ref::(recreated.as_ref()) + .expect("recreated entity should remain primed TNT"); + assert!(recreated_tnt.state.lock().used_portal); + + let victim = Arc::new(PigEntity::new( + &vanilla_entities::PIG, + next_entity_id(), + position + DVec3::X, + Arc::downgrade(target), + )); + target + .try_add_entity(Arc::::clone(&victim)) + .expect("victim should enter the loaded target chunk"); + recreated_tnt.set_fuse(1); + Arc::clone(&recreated).tick(); + + // The damage source retains the discarded TNT and the owner resolved from another world. + let source = victim.last_damage_source().expect("TNT damage source"); + assert_eq!(source.damage_type, &vanilla_damage_types::PLAYER_EXPLOSION); + let direct = source.direct_entity().expect("discarded primed TNT"); + assert!(Arc::ptr_eq(direct, &recreated)); + assert!(direct.is_removed()); + assert!(Arc::ptr_eq( + source.causing_entity().expect("cross-world owner"), + &owner + )); + assert!(Arc::ptr_eq( + &victim.last_hurt_by_mob().expect("responsible owner"), + &owner + )); + assert_eq!(victim.last_hurt_by_player_uuid(), Some(owner.uuid())); +} + +#[test] +fn flowing_water_pushes_primed_tnt_trajectory() { + let fixture = ready_tnt_world("primed_tnt_fluid_current"); + let world = &fixture.world; + let flags = UpdateFlags::UPDATE_NONE + | UpdateFlags::UPDATE_KNOWN_SHAPE + | UpdateFlags::UPDATE_SKIP_ON_PLACE; + assert!(world.set_block( + TEST_TNT_BLOCK_POS.below(), + vanilla_blocks::STONE.default_state(), + flags, + )); + assert!(world.set_block( + TEST_TNT_BLOCK_POS, + vanilla_blocks::WATER.default_state(), + flags, + )); + assert!( + world.set_block( + TEST_TNT_BLOCK_POS.east(), + vanilla_blocks::WATER + .default_state() + .set_value(&BlockStateProperties::LEVEL, 4), + flags, + ) + ); + let entity = primed_tnt(world, test_tnt_position()); + + // TNT skips the base tick, so currents come only from its post-move fluid update. + Arc::clone(&entity).tick(); + assert!(entity.velocity().x > 0.0); + Arc::clone(&entity).tick(); + assert!(entity.position().x > test_tnt_position().x); +} + +#[test] +fn teleported_tnt_preserves_nether_portal_but_explodes_other_blocks() { + let fixture = ready_tnt_world("primed_tnt_portal_explosion"); + let world = &fixture.world; + let portal_pos = TEST_TNT_BLOCK_POS; + let glass_pos = portal_pos.south(); + let flags = UpdateFlags::UPDATE_NONE + | UpdateFlags::UPDATE_KNOWN_SHAPE + | UpdateFlags::UPDATE_SKIP_ON_PLACE; + assert!(world.set_block( + portal_pos, + vanilla_blocks::NETHER_PORTAL.default_state(), + flags, + )); + assert!(world.set_block(glass_pos, vanilla_blocks::GLASS.default_state(), flags)); + let entity = primed_tnt(world, test_tnt_position()); + + entity.on_teleported(); + entity.explode(world); + + assert_eq!( + world.get_block_state(portal_pos).get_block(), + &vanilla_blocks::NETHER_PORTAL + ); + assert!(world.get_block_state(glass_pos).is_air()); +} + +#[test] +fn horizontally_aligned_explosion_does_not_push_primed_tnt_upward() { + let fixture = ready_tnt_world("primed_tnt_explosion_origin"); + let world = &fixture.world; + let entity = primed_tnt(world, test_tnt_position() + DVec3::X); + world + .try_add_entity(Arc::::clone(&entity)) + .expect("primed TNT should enter the loaded test chunk"); + + // Vanilla pushes TNT from its feet rather than its eye position. + world.explode(ExplosionOptions::new( + test_tnt_position(), + 2.0, + ExplosionInteraction::None, + )); + + assert!(entity.velocity().x > 0.0); + assert!(entity.velocity().y.abs() <= f64::EPSILON); +} diff --git a/steel-core/src/entity/entities/objects/items/item_entity.rs b/steel-core/src/entity/entities/objects/items/item_entity.rs index 2f9cfa09509..66ace554e80 100644 --- a/steel-core/src/entity/entities/objects/items/item_entity.rs +++ b/steel-core/src/entity/entities/objects/items/item_entity.rs @@ -12,6 +12,7 @@ use steel_math::DEGREE_360; use steel_registry::entity_type::EntityTypeRef; use steel_registry::item_stack::ItemStack; use steel_registry::vanilla_entity_data::ItemEntityData; +use steel_registry::vanilla_game_rules::MOB_GRIEFING; use steel_registry::{vanilla_custom_stats, vanilla_damage_types}; use steel_utils::UuidExt; use steel_utils::locks::SyncMutex; @@ -26,7 +27,7 @@ use crate::entity::{ use crate::inventory::container::Container; use crate::physics::MoverType; use crate::player::Player; -use crate::world::World; +use crate::world::{Explosion, World}; use simdnbt::ToNbtTag; use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView; @@ -533,6 +534,10 @@ impl Entity for ItemEntity { self.entity_type } + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + !explosion.should_affect_blocklike_entities() + } + fn tick(self: Arc) { // Check if item is empty if self.get_item().is_empty() { @@ -629,6 +634,12 @@ impl Entity for ItemEntity { self.merge_with_neighbors(&world); } + let fluid_contact = self.refresh_fluid_contact_with_currents(); + self.base.reset_fall_distance_in_water(); + if fluid_contact.water_height() > 0.0 || fluid_contact.lava_height() > 0.0 { + self.mark_velocity_sync(); + } + // Check if velocity changed significantly -> set needsSync (vanilla: ItemEntity.tick lines 160-164) let new_movement = self.velocity(); let diff = DVec3::new( @@ -693,8 +704,15 @@ impl Entity for ItemEntity { self.try_pickup(player); } - fn hurt(&self, _world: &World, source: &DamageSource, amount: f32) -> bool { + fn hurt(&self, world: &World, source: &DamageSource, amount: f32) -> bool { // TODO: Check isInvulnerableToBase once the shared non-living entity hook is ported. + if !world.get_game_rule(&MOB_GRIEFING) + && source + .causing_entity() + .is_some_and(|entity| entity.as_mob().is_some()) + { + return false; + } if !self.get_item().can_be_hurt_by(source.damage_type) { return false; } @@ -771,17 +789,19 @@ impl Entity for ItemEntity { #[cfg(test)] mod tests { - use std::sync::Weak; + use std::sync::{Arc, Weak}; use glam::DVec3; use steel_registry::{ - init_vanilla_registry, item_stack::ItemStack, vanilla_damage_types, vanilla_entities, - vanilla_items, + init_vanilla_registry, item_stack::ItemStack, vanilla_blocks, vanilla_damage_types, + vanilla_entities, vanilla_items, }; + use steel_utils::{BlockPos, ChunkPos, types::UpdateFlags}; + use crate::behavior::init_behaviors; use crate::entity::{Entity, damage::DamageSource}; - use crate::test_support::test_world; + use crate::test_support::{fresh_test_world, insert_ready_full_chunk, test_world}; use crate::world::World; use super::ItemEntity; @@ -839,6 +859,33 @@ mod tests { assert!(velocity.z < 0.1); } + #[test] + fn item_tick_refreshes_fluid_after_movement() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("item_post_move_fluid_update"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + assert!(world.set_block( + BlockPos::new(8, 64, 8), + vanilla_blocks::WATER.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let item = Arc::new(ItemEntity::with_item( + &vanilla_entities::ITEM, + 1, + DVec3::new(8.5, 65.1, 8.5), + ItemStack::new(&vanilla_items::STONE), + Arc::downgrade(world), + )); + item.set_velocity(DVec3::new(0.0, -0.2, 0.0)); + + Arc::clone(&item).tick(); + + assert!(item.fluid_contact().water_height() > 0.0); + assert!(item.needs_velocity_sync()); + } + #[test] fn fake_item_is_never_pickable_and_expires_on_its_next_tick() { let item = ItemEntity::with_item( diff --git a/steel-core/src/entity/entities/objects/vehicles/chest_minecart.rs b/steel-core/src/entity/entities/objects/vehicles/chest_minecart.rs index d248d1c27bb..eb8d17b8a38 100644 --- a/steel-core/src/entity/entities/objects/vehicles/chest_minecart.rs +++ b/steel-core/src/entity/entities/objects/vehicles/chest_minecart.rs @@ -8,6 +8,7 @@ use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView; use simdnbt::owned::{NbtCompound, NbtTag}; use steel_macros::entity_behavior; use steel_registry::entity_type::EntityTypeRef; +use steel_registry::vanilla_game_rules::MOB_GRIEFING; use steel_utils::Identifier; use steel_utils::axis::Axis; use steel_utils::block_util::FoundRectangle; @@ -18,7 +19,7 @@ use crate::entity::{ Entity, EntityBase, EntityBaseLoad, reset_forward_direction_of_relative_portal_position, }; use crate::portal::portal_shape::PortalShape; -use crate::world::World; +use crate::world::{Explosion, World}; /// Chest minecart entity state used by mineshaft generation. /// @@ -108,6 +109,13 @@ impl Entity for ChestMinecartEntity { true } + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + !explosion.world().get_game_rule(&MOB_GRIEFING) + && explosion + .indirect_source_entity() + .is_some_and(|entity| entity.as_mob().is_some()) + } + fn dimension_changing_delay(&self) -> i32 { 10 } diff --git a/steel-core/src/entity/entity/mod.rs b/steel-core/src/entity/entity/mod.rs index 9f4da2aaee4..cd2bd42c22e 100644 --- a/steel-core/src/entity/entity/mod.rs +++ b/steel-core/src/entity/entity/mod.rs @@ -467,6 +467,56 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { false } + /// Returns the living entity credited as this explosion's indirect source. + fn explosion_indirect_source(&self) -> Option { + self.projectile_owner() + .filter(|owner| owner.as_living_entity().is_some()) + } + + /// Returns the point used to calculate this entity's explosion direction. + fn explosion_damage_origin(&self) -> DVec3 { + let position = self.position(); + DVec3::new(position.x, self.get_eye_y(), position.z) + } + + /// Returns whether this entity is wholly ignored by an explosion. + fn ignore_explosion(&self, _explosion: &dyn Explosion) -> bool { + false + } + + /// Lets an entity source modify effective block or fluid resistance. + #[expect( + unused_variables, + reason = "default implementation preserves resistance" + )] + fn block_explosion_resistance( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + resistance: f32, + ) -> f32 { + resistance + } + + /// Lets an entity source veto one affected block position. + #[expect(unused_variables, reason = "default implementation allows destruction")] + fn should_block_explode( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + power: f32, + ) -> bool { + true + } + + /// Called after this entity is processed by an explosion. + fn on_explosion_hit(&self, _explosion_source: Option<&dyn Entity>) {} + /// Returns how vanilla lets this entity respond to piston movement. fn piston_push_reaction(&self) -> PushReaction { if self.is_marker_armor_stand() { @@ -535,6 +585,19 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { false } + /// Returns whether this entity must participate in conservative hard-collision queries. + /// + /// Hard collisions are the entity shapes consulted by vanilla movement collision, not + /// ordinary entity pushing. Returning `false` is an immutable capability promise that this + /// entity can never return `true` from [`Self::can_be_collided_with`] for any source and that + /// its [`Self::can_collide_with`] implementation never broadens the default rule as a source. + /// Implementations are conservative by default so plugin-defined collision rules cannot be + /// omitted from the movement broad phase. The world entity manager may cache this capability + /// for the entity's registered lifetime, including chunk-unload retention. + fn is_hard_collision_relevant(&self) -> bool { + true + } + /// Returns whether projectile collision may interact with this entity. fn can_be_hit_by_projectile(&self) -> bool { !self.is_removed() && self.is_pickable() @@ -1173,6 +1236,9 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { /// entities. Most entities do not store owner references. fn restore_owner_reference(&self, _owner: &SharedEntity) {} + /// Called after a teleport completes successfully. + fn on_teleported(&self) {} + /// Sets the level callback for lifecycle events (movement, removal). fn set_level_callback(&self, callback: Arc) { self.base().set_level_callback(callback); @@ -2123,16 +2189,26 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { /// Refreshes cached fluid contact from this entity's current bounding box. fn refresh_fluid_contact(&self) -> EntityFluidContact { - self.scan_and_store_fluid_contact(false) + self.scan_and_store_fluid_contact(false, false) + } + + /// Refreshes cached fluid contact and applies currents without advancing + /// base-tick-only eye-water history. + fn refresh_fluid_contact_with_currents(&self) -> EntityFluidContact { + self.scan_and_store_fluid_contact(false, true) } /// Refreshes cached fluid contact with vanilla base-tick eye-water history. fn refresh_fluid_contact_for_base_tick(&self) -> EntityFluidContact { - self.scan_and_store_fluid_contact(true) + self.scan_and_store_fluid_contact(true, true) } /// Scans current fluid contact and stores it on the entity base. - fn scan_and_store_fluid_contact(&self, advance_eye_water_history: bool) -> EntityFluidContact { + fn scan_and_store_fluid_contact( + &self, + advance_eye_water_history: bool, + include_currents: bool, + ) -> EntityFluidContact { let Some(world) = self.level() else { let contact = EntityFluidContact::default(); if advance_eye_water_history { @@ -2143,7 +2219,7 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { return contact; }; - let contact = if advance_eye_water_history { + let contact = if include_currents { EntityFluidContact::scan_with_currents( &world, self.position(), @@ -2161,15 +2237,17 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { }; if advance_eye_water_history { self.base().set_fluid_contact_for_base_tick(contact); - self.apply_fluid_current_for_base_tick(&world, contact); } else { self.base().set_fluid_contact(contact); } + if include_currents { + self.apply_fluid_current_impulses(&world, contact); + } contact } - /// Applies vanilla water/lava current impulses from the base-tick fluid scan. - fn apply_fluid_current_for_base_tick(&self, world: &Arc, contact: EntityFluidContact) { + /// Applies vanilla water/lava current impulses from a fluid interaction update. + fn apply_fluid_current_impulses(&self, world: &Arc, contact: EntityFluidContact) { if !self.is_pushed_by_fluid() { return; } @@ -2397,7 +2475,6 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { ) -> Result { if let Some(position) = accepted.position { self.try_set_position(position)?; - self.refresh_fluid_contact(); } self.set_rotation(accepted.rotation); @@ -3213,7 +3290,6 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { return None; } self.base().clear_collision_flags(); - self.refresh_fluid_contact(); Some(MoveResult { final_position, @@ -3230,10 +3306,6 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { /// Moves the entity with collision detection. /// /// Updates position, `on_ground`, velocity (on collision), and returns collision info. - #[expect( - clippy::too_many_lines, - reason = "keeps vanilla movement phases in order" - )] fn move_entity(self: Arc, mover_type: MoverType, delta: DVec3) -> Option { let world = self.level()?; if self.no_physics() { @@ -3298,7 +3370,6 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { }; self.base() .set_movement_flags(movement_flags, ground_contact); - self.refresh_fluid_contact(); if self.is_server_driven_movement() && Arc::clone(&self).apply_fall_damage_after_move(&result, &world) @@ -3418,6 +3489,12 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { pos: BlockPos, world: &Arc, ) { + // LivingEntity refreshes fluid interaction after movement, before it evaluates fall state. + if self.as_living_entity().is_some() && !self.is_in_water() { + self.refresh_fluid_contact_with_currents(); + self.base().reset_fall_distance_in_water(); + } + if !self.is_in_water() && vertical_movement < 0.0 { self.base().accumulate_fall_distance(vertical_movement); } diff --git a/steel-core/src/entity/manager/mod.rs b/steel-core/src/entity/manager/mod.rs index 8b3bc215b3a..768d6f50616 100644 --- a/steel-core/src/entity/manager/mod.rs +++ b/steel-core/src/entity/manager/mod.rs @@ -5,7 +5,10 @@ //! of chunk load state; chunks are still the persistence boundary, and only //! full simulated chunks tick entities. -use std::{collections::BTreeMap, error::Error, fmt, mem, slice, sync::Arc}; +use std::{ + collections::BTreeMap, error::Error, fmt, iter::FusedIterator, mem, ops::ControlFlow, slice, + sync::Arc, +}; use glam::DVec3; use rustc_hash::{FxHashMap, FxHashSet}; @@ -17,7 +20,7 @@ use uuid::Uuid; use super::{ Entity, NullEntityCallback, RemovalReason, SharedEntity, snapshot_old_pos_and_rot_for_tick, - tick_vehicle_passengers_with_ticked_if, + tick_vehicle_passengers_if, }; // Vanilla treats four blocks as the largest ordinary entity search extent. @@ -50,6 +53,27 @@ struct EntityQueryOrder { insertion: u64, } +/// Candidate set used by vanilla movement's entity-shape collision query. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum EntityCollisionCandidates { + /// Scan every spatially indexed entity because the source may broaden the default rule. + All, + /// Scan only entities that may be hard-collision targets. + HardCollisionRelevant, +} + +impl EntityCollisionCandidates { + /// Selects the exact-safe broad phase for an optional movement source. + #[must_use] + pub(crate) fn for_source(source: Option<&dyn Entity>) -> Self { + if source.is_some_and(Entity::is_hard_collision_relevant) { + Self::All + } else { + Self::HardCollisionRelevant + } + } +} + #[derive(Debug, Clone, Copy)] struct EntitySpatialCellBounds { minimum: EntitySpatialCell, @@ -360,6 +384,7 @@ struct EntityEntry { chunk: ChunkPos, bounding_box: WorldAabb, spatial_cells: SmallVec<[EntitySpatialCell; 8]>, + hard_collision_relevant: bool, section_order: u64, ownership: EntityOwnership, } @@ -369,6 +394,7 @@ impl EntityEntry { let section = SectionPos::from_entity_pos(entity.position()); let chunk = ChunkPos::new(section.x(), section.z()); let bounding_box = entity.bounding_box(); + let hard_collision_relevant = entity.is_hard_collision_relevant(); Self { _damage_history_owner: entity.base().damage_history().retain_owner(), uuid: entity.uuid(), @@ -377,6 +403,7 @@ impl EntityEntry { chunk, bounding_box, spatial_cells: EntitySpatialCellBounds::from_aabb(&bounding_box).cells(), + hard_collision_relevant, section_order: 0, ownership, } @@ -408,9 +435,10 @@ struct ManagerState { chunk_visibility: FxHashMap, live_by_id: FxHashMap, live_by_uuid: FxHashMap, - accessible_order: OrderedEntityIds, + accessible_order: IndexedOrderedEntityIds, by_section: BTreeMap, by_spatial_cell: FxHashMap, + by_hard_collision_cell: FxHashMap, by_chunk: FxHashMap>, unloading_by_chunk: FxHashMap>, save_pending_by_chunk: FxHashMap>, @@ -449,15 +477,171 @@ impl OrderedEntityIds { self.ids.is_empty() } - fn iter(&self) -> impl Iterator { + fn iter(&self) -> slice::Iter<'_, i32> { self.ids.iter() } } +/// Append-ordered IDs with indexed unlinking for large lifecycle lists. +/// +/// Free nodes are reused, and geometric compaction keeps removals amortized constant-time. +#[derive(Default)] +struct IndexedOrderedEntityIds { + nodes: Vec, + node_by_id: FxHashMap, + first: Option, + last: Option, + free: Option, +} + +struct OrderedEntityIdNode { + entity_id: i32, + previous: Option, + next: Option, +} + +impl IndexedOrderedEntityIds { + const MIN_COMPACTION_SIZE: usize = 64; + + fn insert(&mut self, entity_id: i32) -> bool { + if self.node_by_id.contains_key(&entity_id) { + return false; + } + let previous = self.last; + let index = if let Some(free) = self.free { + self.free = self.nodes[free].next; + free + } else { + let index = self.nodes.len(); + self.nodes.push(OrderedEntityIdNode { + entity_id, + previous: None, + next: None, + }); + index + }; + + self.nodes[index] = OrderedEntityIdNode { + entity_id, + previous, + next: None, + }; + let replaced = self.node_by_id.insert(entity_id, index); + debug_assert!(replaced.is_none()); + + if let Some(previous) = previous { + self.nodes[previous].next = Some(index); + } else { + self.first = Some(index); + } + self.last = Some(index); + true + } + + fn remove(&mut self, entity_id: i32) -> bool { + let Some(index) = self.node_by_id.remove(&entity_id) else { + return false; + }; + + let previous = self.nodes[index].previous; + let next = self.nodes[index].next; + if let Some(previous) = previous { + self.nodes[previous].next = next; + } else { + self.first = next; + } + if let Some(next) = next { + self.nodes[next].previous = previous; + } else { + self.last = previous; + } + + self.nodes[index] = OrderedEntityIdNode { + entity_id, + previous: None, + next: self.free, + }; + self.free = Some(index); + self.compact_if_sparse(); + true + } + + fn len(&self) -> usize { + self.node_by_id.len() + } + + fn iter(&self) -> IndexedOrderedEntityIdsIter<'_> { + IndexedOrderedEntityIdsIter { + nodes: &self.nodes, + next: self.first, + remaining: self.len(), + } + } + + fn compact_if_sparse(&mut self) { + let live_len = self.len(); + if self.nodes.len() < Self::MIN_COMPACTION_SIZE || live_len > self.nodes.len() / 2 { + return; + } + + let mut compacted_nodes: Vec = Vec::with_capacity(live_len); + let mut compacted_node_by_id = FxHashMap::default(); + compacted_node_by_id.reserve(live_len); + let mut current = self.first; + while let Some(index) = current { + let node = &self.nodes[index]; + let compacted_index = compacted_nodes.len(); + let previous = compacted_index.checked_sub(1); + if let Some(previous) = previous { + compacted_nodes[previous].next = Some(compacted_index); + } + compacted_nodes.push(OrderedEntityIdNode { + entity_id: node.entity_id, + previous, + next: None, + }); + compacted_node_by_id.insert(node.entity_id, compacted_index); + current = node.next; + } + debug_assert_eq!(compacted_nodes.len(), live_len); + + self.nodes = compacted_nodes; + self.node_by_id = compacted_node_by_id; + self.first = (!self.nodes.is_empty()).then_some(0); + self.last = self.nodes.len().checked_sub(1); + self.free = None; + } +} + +struct IndexedOrderedEntityIdsIter<'a> { + nodes: &'a [OrderedEntityIdNode], + next: Option, + remaining: usize, +} + +impl<'a> Iterator for IndexedOrderedEntityIdsIter<'a> { + type Item = &'a i32; + + fn next(&mut self) -> Option { + let index = self.next?; + let node = &self.nodes[index]; + self.next = node.next; + self.remaining -= 1; + Some(&node.entity_id) + } + + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) + } +} + +impl ExactSizeIterator for IndexedOrderedEntityIdsIter<'_> {} +impl FusedIterator for IndexedOrderedEntityIdsIter<'_> {} + #[derive(Default)] struct EntityTickList { active: FxHashMap, - order: Vec, + order: IndexedOrderedEntityIds, } impl EntityTickList { @@ -466,13 +650,14 @@ impl EntityTickList { if self.active.insert(entity_id, Arc::clone(entity)).is_some() { return false; } - self.order.push(entity_id); + self.order.insert(entity_id); true } fn remove(&mut self, entity_id: i32) -> Option { let removed = self.active.remove(&entity_id)?; - self.order.retain(|id| *id != entity_id); + let removed_from_order = self.order.remove(entity_id); + debug_assert!(removed_from_order); Some(removed) } @@ -481,11 +666,13 @@ impl EntityTickList { } fn snapshot(&self) -> Vec { - self.order - .iter() - .filter_map(|id| self.active.get(id)) - .cloned() - .collect() + let mut snapshot = Vec::with_capacity(self.order.len()); + for entity_id in self.order.iter() { + if let Some(entity) = self.active.get(entity_id) { + snapshot.push(Arc::clone(entity)); + } + } + snapshot } } @@ -955,6 +1142,7 @@ impl WorldEntityManager { let spatial_cells_changed = current.spatial_cells != new_spatial_cells; let section_changed = old_section != new_section; let spatial_index_changed = spatial_cells_changed || section_changed; + let hard_collision_relevant = current.hard_collision_relevant; let current_section_order = current.section_order; if !section_changed && !spatial_cells_changed { @@ -994,13 +1182,34 @@ impl WorldEntityManager { return Err(EntityMoveError::NotLive { entity_id }); }; let previous_cells = mem::take(&mut entry.spatial_cells); - Self::remove_from_spatial_cells(&mut state, &previous_cells, entity_id); + Self::remove_from_spatial_cells( + &mut state, + &previous_cells, + entity_id, + EntityCollisionCandidates::All, + ); Self::insert_into_spatial_cells( &mut state, &new_spatial_cells, entity_id, new_query_order, + EntityCollisionCandidates::All, ); + if hard_collision_relevant { + Self::remove_from_spatial_cells( + &mut state, + &previous_cells, + entity_id, + EntityCollisionCandidates::HardCollisionRelevant, + ); + Self::insert_into_spatial_cells( + &mut state, + &new_spatial_cells, + entity_id, + new_query_order, + EntityCollisionCandidates::HardCollisionRelevant, + ); + } } if let Some(entry) = state.live_by_id.get_mut(&entity_id) { @@ -1065,6 +1274,7 @@ impl WorldEntityManager { let bounding_box = current.entity.bounding_box(); let new_spatial_cells = EntitySpatialCellBounds::from_aabb(&bounding_box).cells(); let query_order = current.query_order(); + let hard_collision_relevant = current.hard_collision_relevant; if current.spatial_cells == new_spatial_cells { if let Some(entry) = state.live_by_id.get_mut(&entity_id) { @@ -1077,8 +1287,34 @@ impl WorldEntityManager { return; }; let previous_cells = mem::take(&mut entry.spatial_cells); - Self::remove_from_spatial_cells(&mut state, &previous_cells, entity_id); - Self::insert_into_spatial_cells(&mut state, &new_spatial_cells, entity_id, query_order); + Self::remove_from_spatial_cells( + &mut state, + &previous_cells, + entity_id, + EntityCollisionCandidates::All, + ); + Self::insert_into_spatial_cells( + &mut state, + &new_spatial_cells, + entity_id, + query_order, + EntityCollisionCandidates::All, + ); + if hard_collision_relevant { + Self::remove_from_spatial_cells( + &mut state, + &previous_cells, + entity_id, + EntityCollisionCandidates::HardCollisionRelevant, + ); + Self::insert_into_spatial_cells( + &mut state, + &new_spatial_cells, + entity_id, + query_order, + EntityCollisionCandidates::HardCollisionRelevant, + ); + } if let Some(entry) = state.live_by_id.get_mut(&entity_id) { entry.bounding_box = bounding_box; entry.spatial_cells = new_spatial_cells; @@ -1229,6 +1465,61 @@ impl WorldEntityManager { result } + /// Gets movement-collision bounding boxes from the selected conservative broad phase. + /// + /// Entity callbacks run after the manager read lock is released. This permits plugin-defined + /// collision rules to perform ordinary entity-manager lookups without lock reentrancy. + #[must_use] + pub(crate) fn get_movement_collision_boxes_in_aabb_matching( + &self, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + mut predicate: impl FnMut(&dyn Entity) -> bool, + ) -> Vec { + let colliding_entities = self + .movement_collision_candidate_snapshot(aabb, candidates) + .into_iter() + .filter(|entity| entity.bounding_box().intersects(*aabb) && predicate(entity.as_ref())) + .collect::>(); + + // Vanilla first completes `getEntities`, including every selector callback, and only then + // reads the accepted entities' boxes. Keep those phases separate so a plugin callback that + // moves an entity cannot leave a stale collision shape in the result. + colliding_entities + .into_iter() + .map(|entity| entity.bounding_box()) + .collect() + } + + /// Returns whether a movement-collision candidate matches after the broad phase. + #[must_use] + pub(crate) fn has_movement_collision_in_aabb_matching( + &self, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + mut predicate: impl FnMut(&dyn Entity) -> bool, + ) -> bool { + self.movement_collision_candidate_snapshot(aabb, candidates) + .into_iter() + .any(|entity| entity.bounding_box().intersects(*aabb) && predicate(entity.as_ref())) + } + + fn movement_collision_candidate_snapshot( + &self, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + ) -> SmallVec<[SharedEntity; 8]> { + let state = self.state.read(); + let mut snapshot = SmallVec::new(); + let _ = Self::visit_entity_query_entries(&state, aabb, candidates, |entry| { + if Self::is_accessible(&state, entry) { + snapshot.push(Arc::clone(&entry.entity)); + } + ControlFlow::<()>::Continue(()) + }); + snapshot + } + #[must_use] /// Gets the nearest live entity whose bounding box intersects `aabb` and matches `predicate`. pub fn nearest_entity_in_aabb_matching( @@ -1275,23 +1566,44 @@ impl WorldEntityManager { } fn entity_query_entries<'a>(state: &'a ManagerState, aabb: &WorldAabb) -> Vec<&'a EntityEntry> { + let mut entries = Vec::new(); + let _ = Self::visit_entity_query_entries( + state, + aabb, + EntityCollisionCandidates::All, + |entry| { + entries.push(entry); + ControlFlow::<()>::Continue(()) + }, + ); + entries + } + + fn visit_entity_query_entries<'a, B>( + state: &'a ManagerState, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + mut visit: impl FnMut(&'a EntityEntry) -> ControlFlow, + ) -> ControlFlow { let bounds = EntitySpatialCellBounds::from_aabb(aabb); let mut populated_cells = SmallVec::<[&OrderedEntityIds; 8]>::new(); + let spatial_index = match candidates { + EntityCollisionCandidates::All => &state.by_spatial_cell, + EntityCollisionCandidates::HardCollisionRelevant => &state.by_hard_collision_cell, + }; if bounds.direct_probe_count() <= MAX_DIRECT_SPATIAL_CELL_PROBES { for x in bounds.minimum.x..=bounds.maximum.x { for y in bounds.minimum.y..=bounds.maximum.y { for z in bounds.minimum.z..=bounds.maximum.z { - if let Some(cell_ids) = - state.by_spatial_cell.get(&EntitySpatialCell { x, y, z }) - { + if let Some(cell_ids) = spatial_index.get(&EntitySpatialCell { x, y, z }) { populated_cells.push(cell_ids); } } } } } else { - for (cell, cell_ids) in &state.by_spatial_cell { + for (cell, cell_ids) in spatial_index { if bounds.contains(*cell) { populated_cells.push(cell_ids); } @@ -1299,10 +1611,12 @@ impl WorldEntityManager { } if let [cell] = populated_cells.as_slice() { - return cell - .iter() - .filter_map(|entity_id| state.live_by_id.get(entity_id)) - .collect(); + for entity_id in cell.iter() { + if let Some(entry) = state.live_by_id.get(entity_id) { + visit(entry)?; + } + } + return ControlFlow::Continue(()); } let mut entity_ids = FxHashSet::default(); @@ -1314,7 +1628,10 @@ impl WorldEntityManager { .filter_map(|entity_id| state.live_by_id.get(&entity_id)) .collect::>(); entries.sort_unstable_by_key(|entry| entry.query_order()); - entries + for entry in entries { + visit(entry)?; + } + ControlFlow::Continue(()) } fn entity_ids_in_chunk_order(state: &ManagerState, chunk: ChunkPos) -> Vec { @@ -1408,7 +1725,6 @@ impl WorldEntityManager { /// Ticks live entities currently in the ticking visibility set. pub fn tick_entities(&self, _tick_count: i32, runs_normally: bool) -> FxHashSet { let mut dirty_chunks = FxHashSet::default(); - let mut ticked_entities = FxHashSet::default(); let tick_candidates = self.ticking_entities_snapshot(); for entity in tick_candidates { if !self.can_tick_entity_now(entity.id()) { @@ -1436,11 +1752,9 @@ impl WorldEntityManager { continue; } - if !ticked_entities.insert(entity.id()) { - continue; - } - - self.tick_non_passenger(&entity, &mut ticked_entities, &mut dirty_chunks); + // Vanilla deliberately has no same-tick de-duplication here. A relationship change + // may make an entity tick once as a passenger and once from its later root-list slot. + self.tick_non_passenger(&entity, &mut dirty_chunks); } dirty_chunks } @@ -1624,34 +1938,25 @@ impl WorldEntityManager { } } - fn tick_non_passenger( - &self, - entity: &SharedEntity, - ticked_entities: &mut FxHashSet, - dirty_chunks: &mut FxHashSet, - ) { + fn tick_non_passenger(&self, entity: &SharedEntity, dirty_chunks: &mut FxHashSet) { snapshot_old_pos_and_rot_for_tick(entity.as_ref()); entity.advance_tick_count(); Arc::clone(entity).tick(); self.mark_dirty_after_tick(entity, dirty_chunks); - self.tick_vehicle_passengers_with_ticked(entity.as_ref(), ticked_entities, dirty_chunks); + self.tick_vehicle_passengers(entity.as_ref(), dirty_chunks); } - fn tick_vehicle_passengers_with_ticked( + fn tick_vehicle_passengers( &self, vehicle: &dyn Entity, - ticked_entities: &mut FxHashSet, dirty_chunks: &mut FxHashSet, ) { let mut post_tick = |entity: &SharedEntity| { self.mark_dirty_after_tick(entity, dirty_chunks); }; - tick_vehicle_passengers_with_ticked_if( - vehicle, - ticked_entities, - &mut post_tick, - &mut |entity| self.can_tick_entity_now(entity.id()), - ); + tick_vehicle_passengers_if(vehicle, &mut post_tick, &mut |entity| { + self.can_tick_entity_now(entity.id()) + }); } fn mark_dirty_after_tick(&self, entity: &SharedEntity, dirty_chunks: &mut FxHashSet) { @@ -1703,7 +2008,17 @@ impl WorldEntityManager { &entry.spatial_cells, entity_id, entry.query_order(), + EntityCollisionCandidates::All, ); + if entry.hard_collision_relevant { + Self::insert_into_spatial_cells( + state, + &entry.spatial_cells, + entity_id, + entry.query_order(), + EntityCollisionCandidates::HardCollisionRelevant, + ); + } state .by_chunk .entry(entry.chunk) @@ -1786,7 +2101,20 @@ impl WorldEntityManager { state.live_by_uuid.remove(&entry.uuid); state.accessible_order.remove(entity_id); Self::remove_from_section(state, entry.section, entity_id); - Self::remove_from_spatial_cells(state, &entry.spatial_cells, entity_id); + Self::remove_from_spatial_cells( + state, + &entry.spatial_cells, + entity_id, + EntityCollisionCandidates::All, + ); + if entry.hard_collision_relevant { + Self::remove_from_spatial_cells( + state, + &entry.spatial_cells, + entity_id, + EntityCollisionCandidates::HardCollisionRelevant, + ); + } Self::remove_from_chunk(state, entry.chunk, entity_id); Some(entry) } @@ -1806,9 +2134,14 @@ impl WorldEntityManager { cells: &[EntitySpatialCell], entity_id: i32, query_order: EntityQueryOrder, + candidates: EntityCollisionCandidates, ) { for cell in cells { - let insertion_index = state.by_spatial_cell.get(cell).map_or(0, |entity_ids| { + let spatial_index = match candidates { + EntityCollisionCandidates::All => &state.by_spatial_cell, + EntityCollisionCandidates::HardCollisionRelevant => &state.by_hard_collision_cell, + }; + let insertion_index = spatial_index.get(cell).map_or(0, |entity_ids| { let append_in_order = entity_ids.ids.last().is_none_or(|existing_id| { state .live_by_id @@ -1830,8 +2163,13 @@ impl WorldEntityManager { None => entity_ids.ids.len(), } }); - state - .by_spatial_cell + let spatial_index = match candidates { + EntityCollisionCandidates::All => &mut state.by_spatial_cell, + EntityCollisionCandidates::HardCollisionRelevant => { + &mut state.by_hard_collision_cell + } + }; + spatial_index .entry(*cell) .or_default() .insert_at(insertion_index, entity_id); @@ -1842,16 +2180,23 @@ impl WorldEntityManager { state: &mut ManagerState, cells: &[EntitySpatialCell], entity_id: i32, + candidates: EntityCollisionCandidates, ) { for cell in cells { - let remove_cell = if let Some(entity_ids) = state.by_spatial_cell.get_mut(cell) { + let spatial_index = match candidates { + EntityCollisionCandidates::All => &mut state.by_spatial_cell, + EntityCollisionCandidates::HardCollisionRelevant => { + &mut state.by_hard_collision_cell + } + }; + let remove_cell = if let Some(entity_ids) = spatial_index.get_mut(cell) { entity_ids.remove(entity_id); entity_ids.is_empty() } else { false }; if remove_cell { - state.by_spatial_cell.remove(cell); + spatial_index.remove(cell); } } } diff --git a/steel-core/src/entity/manager/tests/mod.rs b/steel-core/src/entity/manager/tests/mod.rs index 2edeeb08f6d..ba9c752da44 100644 --- a/steel-core/src/entity/manager/tests/mod.rs +++ b/steel-core/src/entity/manager/tests/mod.rs @@ -16,6 +16,7 @@ struct ManagerTestEntity { base: EntityBase, entity_type: EntityTypeRef, always_ticking: bool, + hard_collision_relevant: bool, } struct DelayedFirstBoundsCallback { @@ -61,6 +62,22 @@ impl ManagerTestEntity { base: EntityBase::with_uuid(id, uuid, position, entity_type.dimensions, Weak::new()), entity_type, always_ticking: false, + hard_collision_relevant: true, + }) + } + + fn shared_non_hard(id: i32, uuid: Uuid, position: DVec3) -> SharedEntity { + Arc::new(Self { + base: EntityBase::with_uuid( + id, + uuid, + position, + vanilla_entities::ITEM.dimensions, + Weak::new(), + ), + entity_type: &vanilla_entities::ITEM, + always_ticking: false, + hard_collision_relevant: false, }) } @@ -75,6 +92,7 @@ impl ManagerTestEntity { ), entity_type: &vanilla_entities::ITEM, always_ticking: true, + hard_collision_relevant: true, }) } } @@ -228,6 +246,10 @@ impl Entity for ManagerTestEntity { fn is_always_ticking(&self) -> bool { self.always_ticking } + + fn is_hard_collision_relevant(&self) -> bool { + self.hard_collision_relevant + } } fn entity(id: i32, uuid_seed: u128, position: DVec3) -> SharedEntity { diff --git a/steel-core/src/entity/manager/tests/queries_and_order.rs b/steel-core/src/entity/manager/tests/queries_and_order.rs index 0f9c9a9caa3..2e9f90c6b0e 100644 --- a/steel-core/src/entity/manager/tests/queries_and_order.rs +++ b/steel-core/src/entity/manager/tests/queries_and_order.rs @@ -1,7 +1,94 @@ -use std::thread; +use std::{sync::atomic::AtomicUsize, thread}; use super::*; +// Models Vanilla targets such as Happy Ghasts whose collision eligibility depends on the source. +struct SourceSelectiveHardCollisionTarget { + base: EntityBase, + accepted_source_id: i32, +} + +impl SourceSelectiveHardCollisionTarget { + fn shared(id: i32, uuid: Uuid, position: DVec3, accepted_source_id: i32) -> Arc { + Arc::new(Self { + base: EntityBase::with_uuid( + id, + uuid, + position, + vanilla_entities::ITEM.dimensions, + Weak::new(), + ), + accepted_source_id, + }) + } +} + +crate::entity::impl_test_downcast_type!(SourceSelectiveHardCollisionTarget); + +impl Entity for SourceSelectiveHardCollisionTarget { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + &vanilla_entities::ITEM + } + + fn can_be_collided_with(&self, other: Option<&dyn Entity>) -> bool { + other.is_some_and(|source| source.id() == self.accepted_source_id) + } +} + +struct BroadCollisionSourceEntity { + base: EntityBase, + accepted_target_id: i32, +} + +impl BroadCollisionSourceEntity { + fn shared(id: i32, uuid: Uuid, position: DVec3, accepted_target_id: i32) -> Arc { + Arc::new(Self { + base: EntityBase::with_uuid( + id, + uuid, + position, + vanilla_entities::ITEM.dimensions, + Weak::new(), + ), + accepted_target_id, + }) + } +} + +crate::entity::impl_test_downcast_type!(BroadCollisionSourceEntity); + +impl Entity for BroadCollisionSourceEntity { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + &vanilla_entities::ITEM + } + + fn can_collide_with(&self, other: &dyn Entity) -> bool { + other.id() == self.accepted_target_id + } +} + +fn hard_collision_candidate_ids(manager: &WorldEntityManager, aabb: &WorldAabb) -> Vec { + let mut ids = Vec::new(); + let boxes = manager.get_movement_collision_boxes_in_aabb_matching( + aabb, + EntityCollisionCandidates::HardCollisionRelevant, + |entity| { + ids.push(entity.id()); + false + }, + ); + assert_eq!(boxes, Vec::new()); + ids +} + #[test] fn aabb_matching_query_filters_accessible_entities() { let manager = WorldEntityManager::new(); @@ -32,6 +119,208 @@ fn aabb_matching_query_filters_accessible_entities() { assert!(Arc::ptr_eq(&result[0], &second)); } +#[test] +fn hard_collision_broad_phase_matches_full_scan_with_source_dependent_targets() { + const NON_HARD_DISTRACTOR_COUNT: i32 = 128; + const BACKGROUND_GRID_SIDE: i32 = 4; + const SOURCE_ENTITY_ID: i32 = NON_HARD_DISTRACTOR_COUNT + 1; + const LATER_SECTION_ENTITY_ID: i32 = SOURCE_ENTITY_ID + 1; + const FIRST_SAME_SECTION_ENTITY_ID: i32 = SOURCE_ENTITY_ID + 2; + const SECOND_SAME_SECTION_ENTITY_ID: i32 = SOURCE_ENTITY_ID + 3; + const NEXT_CHUNK_INTERIOR_Z: f64 = 17.0; + + let manager = WorldEntityManager::new(); + load_chunk(&manager, ChunkPos::new(0, 0)); + load_chunk(&manager, ChunkPos::new(0, 1)); + + let source = ManagerTestEntity::shared_non_hard( + SOURCE_ENTITY_ID, + Uuid::from_u128(SOURCE_ENTITY_ID as u128), + DVec3::new(1.0, 64.0, 1.0), + ); + for id in 1..=NON_HARD_DISTRACTOR_COUNT { + let position = DVec3::new( + f64::from(id % BACKGROUND_GRID_SIDE), + 64.0, + f64::from((id / BACKGROUND_GRID_SIDE) % BACKGROUND_GRID_SIDE), + ); + let entity = ManagerTestEntity::shared_non_hard(id, Uuid::from_u128(id as u128), position); + assert!( + manager + .add_live_entity(entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + } + + let later_section = SourceSelectiveHardCollisionTarget::shared( + LATER_SECTION_ENTITY_ID, + Uuid::from_u128(LATER_SECTION_ENTITY_ID as u128), + DVec3::new(1.0, 64.0, NEXT_CHUNK_INTERIOR_Z), + source.id(), + ); + let first_same_section = SourceSelectiveHardCollisionTarget::shared( + FIRST_SAME_SECTION_ENTITY_ID, + Uuid::from_u128(FIRST_SAME_SECTION_ENTITY_ID as u128), + DVec3::new(1.0, 64.0, 1.0), + source.id(), + ); + let second_same_section = SourceSelectiveHardCollisionTarget::shared( + SECOND_SAME_SECTION_ENTITY_ID, + Uuid::from_u128(SECOND_SAME_SECTION_ENTITY_ID as u128), + DVec3::new(2.0, 64.0, 1.0), + source.id(), + ); + for entity in [ + Arc::clone(&later_section), + Arc::clone(&first_same_section), + Arc::clone(&second_same_section), + ] { + let entity: SharedEntity = entity; + assert!( + manager + .add_live_entity(entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + } + + assert!(!later_section.can_be_collided_with(None)); + assert!(later_section.can_be_collided_with(Some(source.as_ref()))); + assert_eq!( + EntityCollisionCandidates::for_source(Some(source.as_ref())), + EntityCollisionCandidates::HardCollisionRelevant + ); + + let aabb = WorldAabb::new(-1.0, 63.0, -1.0, 4.0, 66.0, 19.0); + let full = manager.get_movement_collision_boxes_in_aabb_matching( + &aabb, + EntityCollisionCandidates::All, + |entity| source.can_collide_with(entity), + ); + let optimized = manager.get_movement_collision_boxes_in_aabb_matching( + &aabb, + EntityCollisionCandidates::for_source(Some(source.as_ref())), + |entity| source.can_collide_with(entity), + ); + + assert_eq!(optimized, full); + assert_eq!( + optimized, + vec![ + first_same_section.bounding_box(), + second_same_section.bounding_box(), + later_section.bounding_box(), + ] + ); +} + +#[test] +fn conservative_custom_source_scans_non_hard_targets() { + let manager = WorldEntityManager::new(); + load_chunk(&manager, ChunkPos::new(0, 0)); + + let target = + ManagerTestEntity::shared_non_hard(1, Uuid::from_u128(1), DVec3::new(1.0, 64.0, 1.0)); + assert!( + manager + .add_live_entity(Arc::clone(&target), EntityOwnership::ManagerOwned) + .is_ok() + ); + let source = BroadCollisionSourceEntity::shared( + 2, + Uuid::from_u128(2), + DVec3::new(1.0, 64.0, 1.0), + target.id(), + ); + assert_eq!( + EntityCollisionCandidates::for_source(Some(source.as_ref())), + EntityCollisionCandidates::All + ); + + let optimized = manager.get_movement_collision_boxes_in_aabb_matching( + &target.bounding_box(), + EntityCollisionCandidates::for_source(Some(source.as_ref())), + |entity| source.can_collide_with(entity), + ); + let incorrectly_narrowed = manager.get_movement_collision_boxes_in_aabb_matching( + &target.bounding_box(), + EntityCollisionCandidates::HardCollisionRelevant, + |entity| source.can_collide_with(entity), + ); + + assert_eq!(optimized, vec![target.bounding_box()]); + assert_eq!(incorrectly_narrowed, Vec::new()); +} + +#[test] +fn hard_collision_index_tracks_move_bounds_unload_restore_and_remove() { + let manager = WorldEntityManager::new(); + let first_chunk = ChunkPos::new(0, 0); + let second_chunk = ChunkPos::new(1, 0); + load_chunk(&manager, first_chunk); + load_chunk(&manager, second_chunk); + + let hard = entity(1, 1, DVec3::new(1.0, 64.0, 1.0)); + let non_hard = + ManagerTestEntity::shared_non_hard(2, Uuid::from_u128(2), DVec3::new(1.5, 64.0, 1.0)); + for entity in [Arc::clone(&hard), non_hard] { + assert!( + manager + .add_live_entity(entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + } + + let original_bounds = WorldAabb::new(0.0, 63.0, 0.0, 3.0, 66.0, 3.0); + assert_eq!( + hard_collision_candidate_ids(&manager, &original_bounds), + vec![1] + ); + + let moved_position = DVec3::new(17.0, 80.0, 1.0); + hard.base().set_position_local(moved_position); + assert!(manager.commit_move(hard.id(), moved_position).is_ok()); + assert_eq!( + hard_collision_candidate_ids(&manager, &original_bounds), + Vec::::new() + ); + assert_eq!( + hard_collision_candidate_ids(&manager, &hard.bounding_box()), + vec![1] + ); + + let moved_bounds = WorldAabb::new(24.0, 80.0, 0.0, 25.0, 81.0, 1.0); + hard.base().set_bounding_box(moved_bounds); + manager.commit_bounding_box_change(hard.id()); + assert_eq!( + hard_collision_candidate_ids(&manager, &moved_bounds), + vec![1] + ); + + let unload = manager.begin_chunk_unload(second_chunk); + assert_eq!(unload.retained.len(), 1); + assert_eq!( + hard_collision_candidate_ids(&manager, &moved_bounds), + Vec::::new() + ); + let restored = manager.on_chunk_loaded(second_chunk); + assert_eq!(restored.restored.len(), 1); + let _ = manager.update_chunk_visibility(second_chunk, EntityVisibility::Ticking); + assert_eq!( + hard_collision_candidate_ids(&manager, &moved_bounds), + vec![1] + ); + + assert!( + manager + .remove_live_entity(hard.id(), RemovalReason::Discarded) + .is_some() + ); + assert_eq!( + hard_collision_candidate_ids(&manager, &moved_bounds), + Vec::::new() + ); +} + #[test] fn visibility_transitions_separate_tracking_and_ticking() { let manager = WorldEntityManager::new(); @@ -207,6 +496,39 @@ fn accessible_entities_keep_tracking_start_order() { assert_eq!(entity_ids, vec![10, 20, 30]); } +#[test] +fn indexed_ordered_entity_ids_compaction_preserves_order_across_reinsertion() { + const ENTITY_COUNT: i32 = (IndexedOrderedEntityIds::MIN_COMPACTION_SIZE * 2) as i32; + + let mut ids = IndexedOrderedEntityIds::default(); + for entity_id in 0..ENTITY_COUNT { + assert!(ids.insert(entity_id)); + } + + for entity_id in (0..ENTITY_COUNT).step_by(2) { + assert!(ids.remove(entity_id)); + } + + assert_eq!( + ids.iter().copied().collect::>(), + (0..ENTITY_COUNT) + .filter(|entity_id| entity_id % 2 != 0) + .collect::>() + ); + + for entity_id in (0..ENTITY_COUNT).step_by(2).rev() { + assert!(ids.insert(entity_id)); + } + + assert_eq!( + ids.iter().copied().collect::>(), + (0..ENTITY_COUNT) + .filter(|entity_id| entity_id % 2 != 0) + .chain((0..ENTITY_COUNT).step_by(2).rev()) + .collect::>() + ); +} + #[test] fn aabb_queries_use_vanilla_section_order_then_section_insertion_order() { let manager = WorldEntityManager::new(); diff --git a/steel-core/src/entity/manager/tests/ticking.rs b/steel-core/src/entity/manager/tests/ticking.rs index 0fa0c1312ca..38368b1bb1f 100644 --- a/steel-core/src/entity/manager/tests/ticking.rs +++ b/steel-core/src/entity/manager/tests/ticking.rs @@ -1,5 +1,69 @@ +use std::mem; + use super::*; +#[derive(Default)] +enum RelationshipTickAction { + #[default] + None, + StopRiding, +} + +struct RelationshipTickTestEntity { + base: EntityBase, + events: Arc>>, + action: SyncMutex, +} + +impl RelationshipTickTestEntity { + fn shared(id: i32, events: &Arc>>) -> Arc { + Arc::new(Self { + base: EntityBase::with_uuid( + id, + Uuid::from_u128(id as u128), + DVec3::new(1.0, 64.0, 1.0), + vanilla_entities::ITEM.dimensions, + Weak::new(), + ), + events: Arc::clone(events), + action: SyncMutex::new(RelationshipTickAction::None), + }) + } + + fn set_action(&self, action: RelationshipTickAction) { + *self.action.lock() = action; + } + + fn run_action(&self) { + match mem::take(&mut *self.action.lock()) { + RelationshipTickAction::None => {} + RelationshipTickAction::StopRiding => self.stop_riding(), + } + } +} + +crate::entity::impl_test_downcast_type!(RelationshipTickTestEntity); + +impl Entity for RelationshipTickTestEntity { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + &vanilla_entities::ITEM + } + + fn tick(self: Arc) { + self.events.lock().push((self.id(), "tick")); + self.run_action(); + } + + fn ride_tick(self: Arc) { + self.events.lock().push((self.id(), "ride_tick")); + self.run_action(); + } +} + #[test] fn tick_entities_skips_external_entities() { let manager = WorldEntityManager::new(); @@ -275,3 +339,71 @@ fn tick_entities_ticks_player_passenger_vehicle_while_frozen() { assert_eq!(vehicle.tick_count(), 1); assert_eq!(passenger.tick_count(), 1); } + +#[test] +fn passenger_detached_during_ride_tick_ticks_again_at_later_root_slot() { + let manager = WorldEntityManager::new(); + let chunk = ChunkPos::new(0, 0); + load_chunk(&manager, chunk); + + let events = Arc::new(SyncMutex::new(Vec::new())); + let vehicle = RelationshipTickTestEntity::shared(1, &events); + let passenger = RelationshipTickTestEntity::shared(2, &events); + let vehicle_entity: SharedEntity = Arc::::clone(&vehicle); + let passenger_entity: SharedEntity = Arc::::clone(&passenger); + EntityBase::restore_passenger_relationship(&vehicle_entity, &passenger_entity); + passenger.set_action(RelationshipTickAction::StopRiding); + + assert!( + manager + .add_live_entity(vehicle_entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + assert!( + manager + .add_live_entity(passenger_entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + + manager.tick_entities(0, true); + + assert_eq!( + *events.lock(), + vec![(1, "tick"), (2, "ride_tick"), (2, "tick")] + ); + assert_eq!(vehicle.tick_count(), 1); + assert_eq!(passenger.tick_count(), 2); + assert!(!passenger.is_passenger()); +} + +#[test] +fn stable_passenger_tree_ticks_once_in_vehicle_depth_first_order() { + let manager = WorldEntityManager::new(); + let chunk = ChunkPos::new(0, 0); + load_chunk(&manager, chunk); + + let events = Arc::new(SyncMutex::new(Vec::new())); + let vehicle = RelationshipTickTestEntity::shared(1, &events); + let passenger = RelationshipTickTestEntity::shared(2, &events); + let vehicle_entity: SharedEntity = Arc::::clone(&vehicle); + let passenger_entity: SharedEntity = Arc::::clone(&passenger); + EntityBase::restore_passenger_relationship(&vehicle_entity, &passenger_entity); + + assert!( + manager + .add_live_entity(vehicle_entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + assert!( + manager + .add_live_entity(passenger_entity, EntityOwnership::ManagerOwned) + .is_ok() + ); + + manager.tick_entities(0, true); + + assert_eq!(*events.lock(), vec![(1, "tick"), (2, "ride_tick")]); + assert_eq!(vehicle.tick_count(), 1); + assert_eq!(passenger.tick_count(), 1); + assert!(passenger.is_passenger()); +} diff --git a/steel-core/src/entity/mod.rs b/steel-core/src/entity/mod.rs index b2c9060833e..11f5b15b2b2 100644 --- a/steel-core/src/entity/mod.rs +++ b/steel-core/src/entity/mod.rs @@ -12,6 +12,7 @@ use rand::{SeedableRng as _, rngs::StdRng}; use rustc_hash::FxHashSet; use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView; use simdnbt::owned::{NbtCompound, NbtList, NbtTag}; +use smallvec::SmallVec; use steel_math::wrap_degrees; use steel_protocol::packets::game::{ AnimateAction, AttributeSnapshot, CAnimate, CDamageEvent, CEntityEvent, CHurtAnimation, @@ -74,7 +75,7 @@ use crate::physics::{ WorldCollisionProvider, move_entity as resolve_entity_movement, }; use crate::world::game_event::GameEventContext; -use crate::world::{ClipBlockShape, ClipFluid, LevelReader, World}; +use crate::world::{ClipBlockShape, ClipFluid, Explosion, LevelReader, World}; use crate::{enchantment_helper, entity::damage::DamageSource, player::Player}; use entities::ExperienceOrbEntity; @@ -300,6 +301,44 @@ enum BlockEffectSegmentResult { Removed, } +/// Deduplicates block and fluid effects across every movement segment processed for one entity +/// tick. Each segment's geometric sweep separately deduplicates its candidate positions. +enum VisitedBlockPositions { + Inline(SmallVec<[BlockPos; 8]>), + Hashed(FxHashSet), +} + +impl Default for VisitedBlockPositions { + fn default() -> Self { + Self::Inline(SmallVec::new()) + } +} + +impl VisitedBlockPositions { + fn insert(&mut self, pos: BlockPos) -> bool { + match self { + Self::Hashed(visited) => visited.insert(pos), + Self::Inline(inline) => { + if inline.contains(&pos) { + return false; + } + if inline.len() < inline.inline_size() { + inline.push(pos); + return true; + } + + let mut visited = FxHashSet::default(); + visited.reserve(inline.len() + 1); + visited.extend(inline.drain(..)); + let inserted = visited.insert(pos); + debug_assert!(inserted, "inline duplicate check must precede the fallback"); + *self = Self::Hashed(visited); + true + } + } + } +} + #[derive(Debug, Clone, Copy)] struct BlockEffectFireSnapshot { was_on_fire: bool, @@ -510,7 +549,7 @@ fn apply_block_effect_segment( to: DVec3, max_iterations: i32, effect_collector: &mut InsideBlockEffectCollector, - visited_blocks: &mut FxHashSet, + visited_blocks: &mut VisitedBlockPositions, ) -> BlockEffectSegmentResult { let aabb = entity.make_bounding_box_at(to).deflate(1.0E-5); if aabb.is_empty() { @@ -647,7 +686,7 @@ fn apply_effects_from_block_movements(entity: &dyn Entity, movements: &[EntityMo apply_step_on_block(entity, &world); - let mut visited_blocks = FxHashSet::default(); + let mut visited_blocks = VisitedBlockPositions::default(); let mut effect_collector = InsideBlockEffectCollector::new(); let before_effects = BlockEffectFireSnapshot::from_entity(entity); for movement in movements.iter().copied() { @@ -826,6 +865,7 @@ pub use living_base::{ MobEffectSyncChange, MobEffectSyncPacket, }; pub use living_entity::LivingEntity; +pub(crate) use manager::EntityCollisionCandidates; pub use manager::{ AddEntityError, ChunkEntityLoadResult, EntityLifecycleChanges, EntityMoveError, EntityMoveUpdate, EntityOwnership, EntityVisibility, WorldEntityManager, @@ -853,9 +893,7 @@ pub(crate) use spawn::{ }; pub(crate) use storage::{EntityStorage, EntityStorageAddResult}; pub use synced_data::{EntitySyncedData, LivingEntitySyncedData}; -pub(crate) use ticking::{ - snapshot_old_pos_and_rot_for_tick, tick_vehicle_passengers_with_ticked_if, -}; +pub(crate) use ticking::{snapshot_old_pos_and_rot_for_tick, tick_vehicle_passengers_if}; pub use tracker::{EntityChangeSenders, EntityTracker}; #[cfg(test)] @@ -963,7 +1001,7 @@ pub(crate) fn change_entity_world( return None; } - if entity.as_player().is_some() { + let changed = if entity.as_player().is_some() { let Some(player) = source_world.players.get_by_entity_id(entity.id()) else { tracing::error!( entity_id = entity.id(), @@ -976,10 +1014,12 @@ pub(crate) fn change_entity_world( if !player.change_world_within_domain(teleport_transition) { return None; } - return Some(entity); - } - - change_non_player_entity_world(entity, teleport_transition) + entity + } else { + change_non_player_entity_world(entity, teleport_transition)? + }; + changed.on_teleported(); + Some(changed) } fn change_non_player_entity_world( diff --git a/steel-core/src/entity/tests/movement.rs b/steel-core/src/entity/tests/movement.rs index 1aa0d202515..96090a59168 100644 --- a/steel-core/src/entity/tests/movement.rs +++ b/steel-core/src/entity/tests/movement.rs @@ -1,4 +1,6 @@ use super::*; +use crate::physics::MoverType; +use steel_utils::types::UpdateFlags; #[test] fn resolved_movement_application_matches_vanilla_threshold() { @@ -28,6 +30,63 @@ fn move_without_physics_returns_none_when_position_commit_rejects() { assert_vec3_close(entity.position(), DVec3::ZERO); } +#[test] +fn move_preserves_fluid_contact_until_the_entity_fluid_update() { + init_vanilla_registry(); + let fixture = fresh_test_world("entity_move_fluid_update_boundary"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let entity = Arc::new(LivingFluidTestEntity::new_in_world(1.0, 0.0, true, world)); + entity.set_no_physics(true); + + assert!( + Arc::clone(&entity) + .move_entity(MoverType::SelfMovement, DVec3::X) + .is_some() + ); + + assert_eq!( + entity.fluid_contact().water_height().to_bits(), + 1.0_f64.to_bits() + ); + assert_eq!( + entity.refresh_fluid_contact().water_height().to_bits(), + 0.0_f64.to_bits() + ); +} + +#[test] +fn living_move_refreshes_fluid_before_fall_state_is_applied() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("living_move_fluid_fall_boundary"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + assert!(world.set_block( + BlockPos::new(8, 64, 8), + vanilla_blocks::WATER.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let entity = Arc::new(LivingFluidTestEntity::new_in_world(0.0, 0.0, true, world)); + entity.base.set_position_local(DVec3::new(8.5, 65.1, 8.5)); + entity.base.set_fall_distance(8.0); + + assert!( + Arc::clone(&entity) + .move_entity(MoverType::SelfMovement, DVec3::new(0.0, -0.25, 0.0)) + .is_some() + ); + + assert!( + entity.fluid_contact().water_height() > 0.0, + "position={:?}, box={:?}, contact={:?}", + entity.position(), + entity.bounding_box(), + entity.fluid_contact() + ); + assert_eq!(entity.fall_distance().to_bits(), 0.0_f64.to_bits()); +} + #[test] fn fall_damage_reset_clip_target_matches_vanilla_thresholds() { let position = DVec3::new(1.0, 2.0, 3.0); diff --git a/steel-core/src/entity/ticking.rs b/steel-core/src/entity/ticking.rs index e4544fd45aa..59d76a60150 100644 --- a/steel-core/src/entity/ticking.rs +++ b/steel-core/src/entity/ticking.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use rustc_hash::FxHashSet; +use smallvec::SmallVec; use super::{Entity, SharedEntity}; @@ -16,40 +16,37 @@ pub(crate) fn snapshot_old_pos_and_rot_for_tick(entity: &dyn Entity) { /// /// Mirrors vanilla `ServerLevel.tickPassenger`: invalid vehicle links are detached, and /// passengers only recurse when the server-level entity tick list says they may tick. -pub(crate) fn tick_vehicle_passengers_with_ticked_if( +pub(crate) fn tick_vehicle_passengers_if( vehicle: &dyn Entity, - ticked_entities: &mut FxHashSet, post_tick: &mut impl FnMut(&SharedEntity), can_tick: &mut impl FnMut(&SharedEntity) -> bool, ) { - let mut visited = FxHashSet::default(); - visited.insert(vehicle.id()); + let passengers = vehicle.passengers(); + if passengers.is_empty() { + return; + } + + let mut visited = SmallVec::<[i32; 8]>::new(); + visited.push(vehicle.id()); - for passenger in vehicle.passengers() { - tick_passenger( - vehicle, - &passenger, - ticked_entities, - post_tick, - can_tick, - &mut visited, - ); + for passenger in passengers { + tick_passenger(vehicle, &passenger, post_tick, can_tick, &mut visited); } } fn tick_passenger( vehicle: &dyn Entity, entity: &SharedEntity, - ticked_entities: &mut FxHashSet, post_tick: &mut impl FnMut(&SharedEntity), can_tick: &mut impl FnMut(&SharedEntity) -> bool, - visited: &mut FxHashSet, + visited: &mut SmallVec<[i32; 8]>, ) { + let entity_id = entity.id(); assert!( - visited.insert(entity.id()), - "cyclic passenger relationship involving entity {}", - entity.id() + !visited.contains(&entity_id), + "cyclic passenger relationship involving entity {entity_id}" ); + visited.push(entity_id); if entity.is_removed() || entity @@ -57,27 +54,22 @@ fn tick_passenger( .is_none_or(|current_vehicle| current_vehicle.id() != vehicle.id()) { entity.stop_riding(); - visited.remove(&entity.id()); + let popped = visited.pop(); + debug_assert_eq!(popped, Some(entity_id)); return; } - if can_tick(entity) && ticked_entities.insert(entity.id()) { + if can_tick(entity) { snapshot_old_pos_and_rot_for_tick(entity.as_ref()); entity.advance_tick_count(); Arc::clone(entity).ride_tick(); post_tick(entity); for passenger in entity.passengers() { - tick_passenger( - entity.as_ref(), - &passenger, - ticked_entities, - post_tick, - can_tick, - visited, - ); + tick_passenger(entity.as_ref(), &passenger, post_tick, can_tick, visited); } } - visited.remove(&entity.id()); + let popped = visited.pop(); + debug_assert_eq!(popped, Some(entity_id)); } diff --git a/steel-core/src/physics/collision.rs b/steel-core/src/physics/collision.rs index 5a186016bdd..4bf90b05df6 100644 --- a/steel-core/src/physics/collision.rs +++ b/steel-core/src/physics/collision.rs @@ -4,15 +4,20 @@ use std::{ops::ControlFlow, sync::Arc}; use glam::DVec3; use smallvec::SmallVec; -use steel_registry::{blocks::block_state_ext::BlockStateExt, vanilla_blocks, vanilla_entities}; +use steel_registry::{ + blocks::{BlockRef, block_state_ext::BlockStateExt}, + vanilla_blocks, vanilla_entities, +}; use steel_utils::{BlockLocalAabb, BlockPos, BlockStateId, WorldAabb}; use crate::behavior::{ BLOCK_BEHAVIORS, BlockCollisionBoxes, BlockCollisionContext, blocks::PowderSnowBlock, }; -use crate::entity::Entity; -use crate::physics::COLLISION_EPSILON; +use crate::entity::{Entity, EntityCollisionCandidates}; +#[cfg(test)] +use crate::physics::block_has_extensible_collision_behavior; use crate::physics::shapes::join_is_not_empty; +use crate::physics::{COLLISION_EPSILON, block_may_expand_collision_cursor}; use crate::world::{BlockRegionBounds, World}; const BLOCK_COLLISION_EPSILON: f64 = 1.0e-7; @@ -190,8 +195,11 @@ impl BlockCollisionSearchBounds { self, mut visit: impl FnMut(BlockPos, CollisionCursorType) -> ControlFlow, ) -> ControlFlow { - for y in self.min_y..=self.max_y { - for z in self.min_z..=self.max_z { + // Vanilla's Cursor3D advances X first, then Y, then Z. Collision behavior can be + // extensible, so retain that callback order even though the final shape set is usually + // insensitive to traversal order. + for z in self.min_z..=self.max_z { + for y in self.min_y..=self.max_y { for x in self.min_x..=self.max_x { let cursor_type = self.cursor_type(x, y, z); if cursor_type == CollisionCursorType::Corner { @@ -203,6 +211,54 @@ impl BlockCollisionSearchBounds { } ControlFlow::Continue(()) } + + fn try_for_each_inside_candidate( + self, + mut visit: impl FnMut(BlockPos, CollisionCursorType) -> ControlFlow, + ) -> ControlFlow { + let Some(min_x) = self.min_x.checked_add(1) else { + return ControlFlow::Continue(()); + }; + let Some(min_y) = self.min_y.checked_add(1) else { + return ControlFlow::Continue(()); + }; + let Some(min_z) = self.min_z.checked_add(1) else { + return ControlFlow::Continue(()); + }; + let Some(max_x) = self.max_x.checked_sub(1) else { + return ControlFlow::Continue(()); + }; + let Some(max_y) = self.max_y.checked_sub(1) else { + return ControlFlow::Continue(()); + }; + let Some(max_z) = self.max_z.checked_sub(1) else { + return ControlFlow::Continue(()); + }; + if min_x > max_x || min_y > max_y || min_z > max_z { + return ControlFlow::Continue(()); + } + + for z in min_z..=max_z { + for y in min_y..=max_y { + for x in min_x..=max_x { + visit(BlockPos::new(x, y, z), CollisionCursorType::Inside)?; + } + } + } + ControlFlow::Continue(()) + } + + fn try_for_each_region_candidate( + self, + has_special_colliding_blocks: bool, + visit: impl FnMut(BlockPos, CollisionCursorType) -> ControlFlow, + ) -> ControlFlow { + if has_special_colliding_blocks { + self.try_for_each_candidate(visit) + } else { + self.try_for_each_inside_candidate(visit) + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -219,31 +275,30 @@ struct CollisionShape { boxes: BlockCollisionBoxes, } -impl CollisionShape { - fn has_large_collision_shape(&self) -> bool { - self.boxes.iter().any(|aabb| { - aabb.min_x() < 0.0 - || aabb.min_y() < 0.0 - || aabb.min_z() < 0.0 - || aabb.max_x() > 1.0 - || aabb.max_y() > 1.0 - || aabb.max_z() > 1.0 - }) - } +fn should_resolve_collision_shape( + block_state: BlockStateId, + cursor_type: CollisionCursorType, +) -> bool { + should_resolve_collision_shape_for_block( + block_state.get_block(), + block_state + .get_static_collision_shape() + .has_large_collision_shape(), + cursor_type, + ) } -fn should_query_collision_shape( - block_state: BlockStateId, - collision_shape: &CollisionShape, +fn should_resolve_collision_shape_for_block( + block: BlockRef, + has_large_static_shape: bool, cursor_type: CollisionCursorType, ) -> bool { match cursor_type { CollisionCursorType::Inside => true, CollisionCursorType::Face => { - block_state.get_block().config.dynamic_shape - || collision_shape.has_large_collision_shape() + block_may_expand_collision_cursor(block, has_large_static_shape) } - CollisionCursorType::Edge => block_state.get_block() == &vanilla_blocks::MOVING_PISTON, + CollisionCursorType::Edge => block == &vanilla_blocks::MOVING_PISTON, CollisionCursorType::Corner => false, } } @@ -293,18 +348,29 @@ impl<'a> WorldCollisionProvider<'a> { self.world .try_with_block_region(bounds.region_bounds(), |region| { + // A plugin collision callback may mutate a later candidate. Vanilla reads each + // state immediately before its callback, so retain that live path whenever this + // region contains plugin-owned behavior. + if region.maybe_has_extensible_collision_behavior() { + return None; + } let mut candidates = SmallVec::new(); - let _ = bounds.try_for_each_candidate(|block_pos, cursor_type| { - let Some(block_state) = region.get_block_state(block_pos) else { - return ControlFlow::<()>::Continue(()); - }; - if !block_state.is_air() { - candidates.push((block_pos, block_state, cursor_type)); - } - ControlFlow::<()>::Continue(()) - }); - candidates + let has_special_colliding_blocks = region.maybe_has_special_colliding_blocks(); + let _ = bounds.try_for_each_region_candidate( + has_special_colliding_blocks, + |block_pos, cursor_type| { + let Some(block_state) = region.get_block_state(block_pos) else { + return ControlFlow::<()>::Continue(()); + }; + if !block_state.is_air() { + candidates.push((block_pos, block_state, cursor_type)); + } + ControlFlow::<()>::Continue(()) + }, + ); + Some(candidates) }) + .flatten() } fn visit_block_collision_candidates( @@ -341,6 +407,26 @@ impl<'a> WorldCollisionProvider<'a> { CollisionShape { boxes } } + fn visit_block_collision_shapes( + &self, + bounds: BlockCollisionSearchBounds, + context: BlockCollisionContext, + mut visit: impl FnMut( + BlockPos, + BlockStateId, + CollisionCursorType, + &CollisionShape, + ) -> ControlFlow, + ) -> ControlFlow { + self.visit_block_collision_candidates(bounds, |block_pos, block_state, cursor_type| { + if !should_resolve_collision_shape(block_state, cursor_type) { + return ControlFlow::Continue(()); + } + let collision_shape = self.get_collision_shape(block_state, block_pos, context); + visit(block_pos, block_state, cursor_type, &collision_shape) + }) + } + fn entity_collision_context( &self, entity_bottom: f64, @@ -403,11 +489,11 @@ impl<'a> WorldCollisionProvider<'a> { let mut main_support = None; let mut main_support_distance = f64::MAX; - let _ = - self.visit_block_collision_candidates(bounds, |block_pos, block_state, cursor_type| { - let collision_shape = self.get_collision_shape(block_state, block_pos, context); + let _ = self.visit_block_collision_shapes( + bounds, + context, + |block_pos, _block_state, _cursor_type, collision_shape| { if collision_shape.boxes.is_empty() - || !should_query_collision_shape(block_state, &collision_shape, cursor_type) || !collision_shape .boxes .iter() @@ -427,7 +513,8 @@ impl<'a> WorldCollisionProvider<'a> { main_support_distance = distance; } ControlFlow::<()>::Continue(()) - }); + }, + ); main_support } @@ -662,12 +749,11 @@ impl CollisionWorld for WorldCollisionProvider<'_> { ) -> Vec { let bounds = BlockCollisionSearchBounds::from_aabb(aabb); let mut collisions = Vec::new(); - let _ = - self.visit_block_collision_candidates(bounds, |block_pos, block_state, cursor_type| { - let collision_shape = self.get_collision_shape(block_state, block_pos, context); - if collision_shape.boxes.is_empty() - || !should_query_collision_shape(block_state, &collision_shape, cursor_type) - { + let _ = self.visit_block_collision_shapes( + bounds, + context, + |block_pos, _block_state, _cursor_type, collision_shape| { + if collision_shape.boxes.is_empty() { return ControlFlow::<()>::Continue(()); } @@ -679,7 +765,8 @@ impl CollisionWorld for WorldCollisionProvider<'_> { .filter(|shape| aabb.intersects(*shape)), ); ControlFlow::<()>::Continue(()) - }); + }, + ); collisions } @@ -689,25 +776,26 @@ impl CollisionWorld for WorldCollisionProvider<'_> { context: BlockCollisionContext, ) -> bool { let bounds = BlockCollisionSearchBounds::from_aabb(aabb); - self.visit_block_collision_candidates(bounds, |block_pos, block_state, cursor_type| { - let collision_shape = self.get_collision_shape(block_state, block_pos, context); - if collision_shape.boxes.is_empty() - || !should_query_collision_shape(block_state, &collision_shape, cursor_type) - { - return ControlFlow::Continue(()); - } + self.visit_block_collision_shapes( + bounds, + context, + |block_pos, _block_state, _cursor_type, collision_shape| { + if collision_shape.boxes.is_empty() { + return ControlFlow::Continue(()); + } - if collision_shape - .boxes - .iter() - .map(|shape| translate_collision_shape(shape, block_pos)) - .any(|shape| aabb.intersects(shape)) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) + if collision_shape + .boxes + .iter() + .map(|shape| translate_collision_shape(shape, block_pos)) + .any(|shape| aabb.intersects(shape)) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }, + ) .is_break() } @@ -734,8 +822,11 @@ impl CollisionWorld for WorldCollisionProvider<'_> { } let query = aabb.inflate(ENTITY_COLLISION_EPSILON); + let candidates = EntityCollisionCandidates::for_source(self.source); self.world - .get_entity_bounding_boxes_in_aabb_matching(&query, |entity| match self.source { + .get_movement_collision_boxes_in_aabb_matching(&query, candidates, |entity| match self + .source + { Some(source) => { entity.id() != source.id() && !entity.is_removed() @@ -759,18 +850,21 @@ impl CollisionWorld for WorldCollisionProvider<'_> { } let query = aabb.inflate(ENTITY_COLLISION_EPSILON); + let candidates = EntityCollisionCandidates::for_source(self.source); self.world - .has_entity_in_aabb_matching(&query, |entity| match self.source { - Some(source) => { - entity.id() != source.id() - && !entity.is_removed() - && !entity.is_spectator() - && source.can_collide_with(entity) - } - None => { - !entity.is_removed() - && !entity.is_spectator() - && entity.can_be_collided_with(None) + .has_movement_collision_in_aabb_matching(&query, candidates, |entity| { + match self.source { + Some(source) => { + entity.id() != source.id() + && !entity.is_removed() + && !entity.is_spectator() + && source.can_collide_with(entity) + } + None => { + !entity.is_removed() + && !entity.is_spectator() + && entity.can_be_collided_with(None) + } } }) } @@ -806,18 +900,17 @@ mod tests { use std::iter; use super::*; - use steel_registry::blocks::shapes::VoxelShape; - use steel_registry::init_vanilla_registry; - use steel_utils::{BlockLocalAabb, ChunkPos, types::UpdateFlags}; + use steel_registry::{ + blocks::{Block, behavior::BlockConfig}, + init_vanilla_registry, + }; + use steel_utils::{ChunkPos, Identifier, types::UpdateFlags}; use crate::{ behavior::init_behaviors, test_support::{fresh_test_world, insert_ready_full_chunk}, }; - const LARGE_COLLISION_SHAPE: &[BlockLocalAabb] = - &[BlockLocalAabb::new(-0.25, 0.0, 0.0, 1.0, 1.0, 1.0)]; - struct TestCollisionWorld { block_collisions: Vec, entity_collisions: Vec, @@ -969,6 +1062,205 @@ mod tests { assert!(collisions.contains(&WorldAabb::new(0.0, 64.0, 0.0, 1.0, 65.0, 1.0))); } + #[test] + #[expect( + clippy::too_many_lines, + reason = "one scenario pins ordinary, large, dynamic, and moving-piston cursor transitions" + )] + fn prefetched_candidates_use_full_cursor_only_for_special_sections() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("special_collision_section_scan"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let bounds = BlockCollisionSearchBounds { + min_x: 1, + min_y: 64, + min_z: 1, + max_x: 3, + max_y: 66, + max_z: 3, + }; + let face = BlockPos::new(1, 65, 2); + let inside = BlockPos::new(2, 65, 2); + assert!(world.set_block( + face, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert!(world.set_block( + inside, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let provider = WorldCollisionProvider::new(world); + + let ordinary = provider + .prefetched_block_collision_candidates(bounds) + .expect("small loaded collision region should be prefetched"); + assert_eq!( + ordinary + .iter() + .map(|(pos, _, cursor_type)| (*pos, *cursor_type)) + .collect::>(), + vec![(inside, CollisionCursorType::Inside)] + ); + let mut ordinary_shape_callbacks = Vec::new(); + let _ = provider.visit_block_collision_shapes( + bounds, + BlockCollisionContext::empty(), + |pos, _, cursor_type, _| { + ordinary_shape_callbacks.push((pos, cursor_type)); + ControlFlow::<()>::Continue(()) + }, + ); + assert_eq!( + ordinary_shape_callbacks, + vec![(inside, CollisionCursorType::Inside)] + ); + + assert!(world.set_block( + BlockPos::new(15, 64, 15), + vanilla_blocks::OAK_FENCE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let special = provider + .prefetched_block_collision_candidates(bounds) + .expect("small loaded collision region should be prefetched"); + assert_eq!( + special + .iter() + .map(|(pos, _, cursor_type)| (*pos, *cursor_type)) + .collect::>(), + vec![ + (face, CollisionCursorType::Face), + (inside, CollisionCursorType::Inside), + ] + ); + let mut static_shape_callbacks = Vec::new(); + let _ = provider.visit_block_collision_shapes( + bounds, + BlockCollisionContext::empty(), + |pos, _, cursor_type, _| { + static_shape_callbacks.push((pos, cursor_type)); + ControlFlow::<()>::Continue(()) + }, + ); + assert_eq!( + static_shape_callbacks, + vec![(inside, CollisionCursorType::Inside)] + ); + + assert!(world.set_block( + BlockPos::new(15, 64, 15), + vanilla_blocks::AIR.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert!(world.set_block( + face, + vanilla_blocks::SCAFFOLDING.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let edge = BlockPos::new(1, 64, 2); + assert!(world.set_block( + edge, + vanilla_blocks::MOVING_PISTON.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let dynamic = provider + .prefetched_block_collision_candidates(bounds) + .expect("small loaded collision region should be prefetched"); + assert_eq!(dynamic.len(), 3); + let mut callback_sequence = Vec::new(); + let _ = provider.visit_block_collision_shapes( + bounds, + BlockCollisionContext::empty(), + |pos, _, cursor_type, _| { + callback_sequence.push((pos, cursor_type)); + ControlFlow::<()>::Continue(()) + }, + ); + assert_eq!( + callback_sequence, + vec![ + (edge, CollisionCursorType::Edge), + (face, CollisionCursorType::Face), + (inside, CollisionCursorType::Inside), + ] + ); + } + + #[test] + fn prefetched_callbacks_match_full_cursor_across_chunk_section_and_missing_data() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("cross_boundary_collision_reads"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + insert_ready_full_chunk(world, ChunkPos::new(1, 0)); + + let bounds = BlockCollisionSearchBounds { + min_x: 14, + min_y: 78, + min_z: 0, + max_x: 33, + max_y: 81, + max_z: 2, + }; + for pos in [ + BlockPos::new(14, 79, 1), + BlockPos::new(15, 79, 1), + BlockPos::new(16, 80, 1), + BlockPos::new(31, 79, 1), + ] { + assert!(world.set_block( + pos, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + } + let provider = WorldCollisionProvider::new(world); + + let compatibility_callbacks = || { + let mut callbacks = Vec::new(); + let _ = bounds.try_for_each_candidate(|pos, cursor_type| { + let state = world.get_block_state(pos); + if !state.is_air() && should_resolve_collision_shape(state, cursor_type) { + callbacks.push((pos, cursor_type)); + } + ControlFlow::<()>::Continue(()) + }); + callbacks + }; + let provider_callbacks = || { + let mut callbacks = Vec::new(); + let _ = provider.visit_block_collision_shapes( + bounds, + BlockCollisionContext::empty(), + |pos, _, cursor_type, _| { + callbacks.push((pos, cursor_type)); + ControlFlow::<()>::Continue(()) + }, + ); + callbacks + }; + + assert!( + provider + .prefetched_block_collision_candidates(bounds) + .is_some(), + "missing Full data should remain a safe air-like prefetched region" + ); + assert_eq!(provider_callbacks(), compatibility_callbacks()); + + assert!(world.set_block( + BlockPos::new(31, 79, 1), + vanilla_blocks::OAK_FENCE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert_eq!(provider_callbacks(), compatibility_callbacks()); + } + #[test] fn collision_helper_reports_intersecting_entity_shape() { let world = TestCollisionWorld { @@ -1099,64 +1391,149 @@ mod tests { } #[test] - fn collision_shape_filter_matches_vanilla_cursor_rules() { + fn collision_cursor_callbacks_match_vanilla_x_y_z_order() { + let bounds = BlockCollisionSearchBounds { + min_x: 0, + min_y: 0, + min_z: 0, + max_x: 2, + max_y: 2, + max_z: 2, + }; + let mut visited = Vec::new(); + let _ = bounds.try_for_each_candidate(|pos, _| { + visited.push(pos); + ControlFlow::<()>::Continue(()) + }); + + assert_eq!( + visited, + vec![ + BlockPos::new(1, 0, 0), + BlockPos::new(0, 1, 0), + BlockPos::new(1, 1, 0), + BlockPos::new(2, 1, 0), + BlockPos::new(1, 2, 0), + BlockPos::new(0, 0, 1), + BlockPos::new(1, 0, 1), + BlockPos::new(2, 0, 1), + BlockPos::new(0, 1, 1), + BlockPos::new(1, 1, 1), + BlockPos::new(2, 1, 1), + BlockPos::new(0, 2, 1), + BlockPos::new(1, 2, 1), + BlockPos::new(2, 2, 1), + BlockPos::new(1, 0, 2), + BlockPos::new(0, 1, 2), + BlockPos::new(1, 1, 2), + BlockPos::new(2, 1, 2), + BlockPos::new(1, 2, 2), + ] + ); + } + + #[test] + fn static_looking_plugin_block_keeps_full_cursor_callback_order() { + static PLUGIN_BLOCK: Block = Block::new( + Identifier::new_static("collision_test", "static_cube"), + BlockConfig::new(), + &[], + ); + + assert!(!PLUGIN_BLOCK.config.dynamic_shape); + assert!(block_has_extensible_collision_behavior(&PLUGIN_BLOCK)); + let has_large_static_shape = false; + let has_special_colliding_blocks = + block_may_expand_collision_cursor(&PLUGIN_BLOCK, has_large_static_shape); + assert!(has_special_colliding_blocks); + + let bounds = BlockCollisionSearchBounds { + min_x: 0, + min_y: 0, + min_z: 0, + max_x: 2, + max_y: 2, + max_z: 2, + }; + let fixture_positions = [ + BlockPos::new(1, 1, 0), + BlockPos::new(0, 1, 1), + BlockPos::new(1, 1, 1), + BlockPos::new(2, 1, 1), + BlockPos::new(1, 1, 2), + ]; + + let mut compatibility_callbacks = Vec::new(); + let _ = bounds.try_for_each_candidate(|pos, cursor_type| { + if fixture_positions.contains(&pos) + && should_resolve_collision_shape_for_block( + &PLUGIN_BLOCK, + has_large_static_shape, + cursor_type, + ) + { + compatibility_callbacks.push(pos); + } + ControlFlow::<()>::Continue(()) + }); + + let mut optimized_callbacks = Vec::new(); + let _ = bounds.try_for_each_region_candidate( + has_special_colliding_blocks, + |pos, cursor_type| { + if fixture_positions.contains(&pos) + && should_resolve_collision_shape_for_block( + &PLUGIN_BLOCK, + has_large_static_shape, + cursor_type, + ) + { + optimized_callbacks.push(pos); + } + ControlFlow::<()>::Continue(()) + }, + ); + + assert_eq!(compatibility_callbacks, fixture_positions); + assert_eq!(optimized_callbacks, compatibility_callbacks); + } + + #[test] + fn collision_shape_resolution_filter_matches_vanilla_cursor_rules() { init_vanilla_registry(); let stone = vanilla_blocks::STONE.default_state(); + let fence = vanilla_blocks::OAK_FENCE.default_state(); + let scaffolding = vanilla_blocks::SCAFFOLDING.default_state(); let moving_piston = vanilla_blocks::MOVING_PISTON.default_state(); - let large_shape = VoxelShape::from_boxes(LARGE_COLLISION_SHAPE); - let shape = |shape: VoxelShape| CollisionShape { - boxes: shape.into_iter().copied().collect(), - }; - assert!(should_query_collision_shape( + assert!(should_resolve_collision_shape( stone, - &shape(VoxelShape::FULL_BLOCK), CollisionCursorType::Inside )); - assert!(!should_query_collision_shape( + assert!(!should_resolve_collision_shape( stone, - &shape(VoxelShape::FULL_BLOCK), CollisionCursorType::Face )); - assert!(should_query_collision_shape( - stone, - &shape(large_shape), + assert!(should_resolve_collision_shape( + fence, CollisionCursorType::Face )); - assert!(!should_query_collision_shape( - stone, - &shape(large_shape), + assert!(should_resolve_collision_shape( + scaffolding, + CollisionCursorType::Face + )); + assert!(!should_resolve_collision_shape( + fence, CollisionCursorType::Edge )); - assert!(should_query_collision_shape( + assert!(should_resolve_collision_shape( moving_piston, - &shape(VoxelShape::FULL_BLOCK), CollisionCursorType::Edge )); - assert!(!should_query_collision_shape( + assert!(!should_resolve_collision_shape( moving_piston, - &shape(large_shape), CollisionCursorType::Corner )); } - - #[test] - fn collision_shape_filter_uses_position_resolved_offset_bounds() { - init_vanilla_registry(); - - let stone = vanilla_blocks::STONE.default_state(); - let shifted_full_block = CollisionShape { - boxes: VoxelShape::FULL_BLOCK - .into_iter() - .map(|aabb| aabb.translate(DVec3::new(0.25, 0.0, 0.0))) - .collect(), - }; - - assert!(should_query_collision_shape( - stone, - &shifted_full_block, - CollisionCursorType::Face - )); - } } diff --git a/steel-core/src/physics/mod.rs b/steel-core/src/physics/mod.rs index 411a68494d1..6a4e0a785bb 100644 --- a/steel-core/src/physics/mod.rs +++ b/steel-core/src/physics/mod.rs @@ -9,6 +9,12 @@ //! The implementation closely follows vanilla's `Entity.move()` method to ensure //! 1:1 movement validation for anti-cheat purposes. +use steel_registry::{ + blocks::{BlockRef, block_state_ext::BlockStateExt}, + vanilla_blocks, +}; +use steel_utils::{BlockStateId, Identifier}; + pub mod collision; pub(crate) mod entity_move; pub mod movement_validation; @@ -31,3 +37,30 @@ pub use shapes::{collide, join_is_not_empty, merged_face_occludes, translate_sha /// Collision epsilon used for AABB deflation (vanilla constant). pub const COLLISION_EPSILON: f64 = 1.0e-5; + +/// Returns whether this block's collision behavior is owned by an extensible namespace. +pub(crate) fn block_has_extensible_collision_behavior(block: BlockRef) -> bool { + block.key.namespace != Identifier::VANILLA_NAMESPACE +} + +fn block_may_expand_collision_cursor(block: BlockRef, has_large_static_shape: bool) -> bool { + block_has_extensible_collision_behavior(block) + || block.config.dynamic_shape + || block == &vanilla_blocks::MOVING_PISTON + || has_large_static_shape +} + +/// Returns whether this state's collision behavior requires live reads around callbacks. +pub(crate) fn block_state_has_extensible_collision_behavior(state: BlockStateId) -> bool { + block_has_extensible_collision_behavior(state.get_block()) +} + +/// Returns whether a state may require Vanilla's expanded block-collision cursor. +pub(crate) fn block_state_may_expand_collision_cursor(state: BlockStateId) -> bool { + block_may_expand_collision_cursor( + state.get_block(), + state + .get_static_collision_shape() + .has_large_collision_shape(), + ) +} diff --git a/steel-core/src/player/movement/mod.rs b/steel-core/src/player/movement/mod.rs index 005946c1f83..713720e9741 100644 --- a/steel-core/src/player/movement/mod.rs +++ b/steel-core/src/player/movement/mod.rs @@ -568,7 +568,6 @@ impl Player { vehicle.id() ); } - vehicle.refresh_fluid_contact(); vehicle.set_rotation((target_yaw, target_pitch)); self.send_packet(Self::move_vehicle_packet_from_entity(vehicle.as_ref())); vehicle.remove_latest_movement_recording(); @@ -600,7 +599,6 @@ impl Player { vehicle.id() ); } - vehicle.refresh_fluid_contact(); vehicle.set_rotation((target_yaw, target_pitch)); self.send_packet(Self::move_vehicle_packet_from_entity(vehicle.as_ref())); vehicle.remove_latest_movement_recording(); diff --git a/steel-core/src/poi/poi_storage.rs b/steel-core/src/poi/poi_storage.rs index f22afa82b87..5d3320fc495 100644 --- a/steel-core/src/poi/poi_storage.rs +++ b/steel-core/src/poi/poi_storage.rs @@ -371,7 +371,7 @@ impl PointOfInterestStorage { for y in 0..16u8 { for z in 0..16u8 { for x in 0..16u8 { - let state_id = section.states.get(x as usize, y as usize, z as usize); + let state_id = section.states().get(x as usize, y as usize, z as usize); let Some(poi_type_id) = registry.type_id_for_state(state_id) else { continue; diff --git a/steel-core/src/world/block_region.rs b/steel-core/src/world/block_region.rs index 3d8843bdf05..40b9c3f8c5b 100644 --- a/steel-core/src/world/block_region.rs +++ b/steel-core/src/world/block_region.rs @@ -5,12 +5,21 @@ use smallvec::SmallVec; use steel_registry::{REGISTRY, vanilla_blocks}; use steel_utils::{BlockPos, BlockStateId, ChunkPos, SectionPos}; -use crate::chunk::{chunk_holder::ChunkHolder, section::ChunkSection, status::ChunkStatus}; +use crate::chunk::{ + chunk_holder::ChunkHolder, paletted_container::BlockPalette, section::ChunkSection, + status::ChunkStatus, +}; use super::World; /// Maximum combined chunk-holder and section slots acquired by one bulk region read. pub(crate) const MAX_BLOCK_REGION_WORKSET_SLOTS: usize = 64; +const SECTION_EDGE_BLOCKS: i32 = BlockPalette::SIZE as i32; +const SECTION_LOCAL_COORDINATE_MASK: i32 = SECTION_EDGE_BLOCKS - 1; + +const fn section_local_coordinate(block_coordinate: i32) -> usize { + (block_coordinate & SECTION_LOCAL_COORDINATE_MASK) as usize +} /// Inclusive block bounds for one scoped region read. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -29,6 +38,12 @@ impl BlockRegionBounds { } } + /// Returns the inclusive minimum and maximum block corners. + #[must_use] + pub(crate) const fn corners(self) -> (BlockPos, BlockPos) { + (self.min, self.max) + } + #[must_use] const fn contains(self, pos: BlockPos) -> bool { pos.x() >= self.min.x() @@ -160,12 +175,25 @@ impl BlockSectionRead<'_> { if SectionPos::from_block_pos(pos) != self.section_pos { return None; } - Some(self.section.states.get( - (pos.x() & 15) as usize, - (pos.y() & 15) as usize, - (pos.z() & 15) as usize, + Some(self.section.states().get( + section_local_coordinate(pos.x()), + section_local_coordinate(pos.y()), + section_local_coordinate(pos.z()), )) } + + fn stable_air_box_is_clear(&self, min: BlockPos, max: BlockPos) -> bool { + debug_assert_eq!(SectionPos::from_block_pos(min), self.section_pos); + debug_assert_eq!(SectionPos::from_block_pos(max), self.section_pos); + self.section.stable_air_box_is_clear( + section_local_coordinate(min.x()), + section_local_coordinate(max.x()), + section_local_coordinate(min.y()), + section_local_coordinate(max.y()), + section_local_coordinate(min.z()), + section_local_coordinate(max.z()), + ) + } } /// Scoped block-state reads over a rectangular set of prelocked sections. @@ -210,6 +238,91 @@ impl BlockRegionRead<'_> { section.get_block_state(pos) } + /// Returns whether every in-height section in this bounded snapshot came from a Full chunk. + #[must_use] + pub(crate) fn has_complete_data(&self) -> bool { + self.sections.iter().all(Option::is_some) + } + + /// Returns whether any retained section may require expanded block-collision boundaries. + #[must_use] + pub(crate) fn maybe_has_special_colliding_blocks(&self) -> bool { + self.sections + .iter() + .flatten() + .any(|section| section.maybe_has_special_colliding_blocks()) + } + + /// Returns whether any retained section requires live collision-state reads around callbacks. + #[must_use] + pub(crate) fn maybe_has_extensible_collision_behavior(&self) -> bool { + self.sections + .iter() + .flatten() + .any(|section| section.maybe_has_extensible_collision_behavior()) + } + + /// Returns whether the bounded, in-world snapshot contains only immutable Vanilla air. + /// + /// Missing Full data is never accepted: publication or a live mutation must remain visible to + /// the exact fallback query. + #[must_use] + pub(crate) fn contains_only_stable_air(&self) -> bool { + if !self.has_complete_data() { + return false; + } + + let (bounds_min, bounds_max) = self.bounds.corners(); + let min_y = bounds_min.y().max(self.world_min_y); + let max_y = bounds_max.y().min(self.world_max_y); + let min_section_x = + SectionPos::block_to_section_coord(bounds_min.x()).max(-ChunkPos::MAX_COORDINATE_VALUE); + let max_section_x = + SectionPos::block_to_section_coord(bounds_max.x()).min(ChunkPos::MAX_COORDINATE_VALUE); + let min_section_z = + SectionPos::block_to_section_coord(bounds_min.z()).max(-ChunkPos::MAX_COORDINATE_VALUE); + let max_section_z = + SectionPos::block_to_section_coord(bounds_max.z()).min(ChunkPos::MAX_COORDINATE_VALUE); + if min_y > max_y || min_section_x > max_section_x || min_section_z > max_section_z { + return true; + } + + let min_section_y = SectionPos::block_to_section_coord(min_y); + let max_section_y = SectionPos::block_to_section_coord(max_y); + for section_x in min_section_x..=max_section_x { + for section_z in min_section_z..=max_section_z { + for section_y in min_section_y..=max_section_y { + let section_pos = SectionPos::new(section_x, section_y, section_z); + let Some(section) = self.section(section_pos) else { + return false; + }; + let section_min = BlockPos::new( + section_x * SECTION_EDGE_BLOCKS, + section_y * SECTION_EDGE_BLOCKS, + section_z * SECTION_EDGE_BLOCKS, + ); + let section_max = BlockPos::new( + section_min.x() + SECTION_LOCAL_COORDINATE_MASK, + section_min.y() + SECTION_LOCAL_COORDINATE_MASK, + section_min.z() + SECTION_LOCAL_COORDINATE_MASK, + ); + let clipped_min = BlockPos::max( + BlockPos::new(bounds_min.x(), min_y, bounds_min.z()), + section_min, + ); + let clipped_max = BlockPos::min( + BlockPos::new(bounds_max.x(), max_y, bounds_max.z()), + section_max, + ); + if !section.stable_air_box_is_clear(clipped_min, clipped_max) { + return false; + } + } + } + } + true + } + /// Returns a prelocked section view, or `None` for an unloaded or uncached section. #[must_use] pub(crate) fn section(&self, section_pos: SectionPos) -> Option> { @@ -275,7 +388,60 @@ mod tests { test_support::{fresh_test_world, insert_ready_full_chunk}, }; - use super::{BlockRegionBounds, BlockRegionWorkset}; + use super::{BlockRegionBounds, BlockRegionWorkset, SECTION_EDGE_BLOCKS}; + + #[test] + #[expect( + clippy::redundant_closure_for_method_calls, + reason = "the method pointer cannot satisfy the region callback's higher-ranked lifetime" + )] + fn stable_air_region_requires_full_data_and_tracks_live_block_writes() { + let fixture = fresh_test_world("stable_air_region"); + let world = &fixture.world; + init_behaviors(); + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + insert_ready_full_chunk(world, ChunkPos::new(1, 0)); + let first_chunk_max_x = SECTION_EDGE_BLOCKS - 1; + let second_chunk_min_x = SECTION_EDGE_BLOCKS; + let missing_chunk_min_x = SECTION_EDGE_BLOCKS * 2; + let test_y = 64; + let bounds = BlockRegionBounds::from_corners( + BlockPos::new(first_chunk_max_x, test_y, 0), + BlockPos::new(second_chunk_min_x, test_y, 0), + ); + + assert_eq!( + world.try_with_block_region(bounds, |region| region.contains_only_stable_air()), + Some(true) + ); + assert!(world.set_block( + BlockPos::new(second_chunk_min_x, test_y, 0), + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert_eq!( + world.try_with_block_region(bounds, |region| region.contains_only_stable_air()), + Some(false) + ); + assert!(world.set_block( + BlockPos::new(second_chunk_min_x, test_y, 0), + vanilla_blocks::CAVE_AIR.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert_eq!( + world.try_with_block_region(bounds, |region| region.contains_only_stable_air()), + Some(true) + ); + + let missing = BlockRegionBounds::from_corners( + BlockPos::new(first_chunk_max_x, test_y, 0), + BlockPos::new(missing_chunk_min_x, test_y, 0), + ); + assert_eq!( + world.try_with_block_region(missing, |region| region.contains_only_stable_air()), + Some(false) + ); + } #[test] fn region_reuses_section_reads_across_chunk_and_section_boundaries() { @@ -315,6 +481,8 @@ mod tests { region.get_block_state(missing_chunk), Some(vanilla_blocks::AIR.default_state()) ); + assert!(!region.has_complete_data()); + assert!(!region.maybe_has_special_colliding_blocks()); assert_eq!( region.get_block_state(below_world), Some(vanilla_blocks::VOID_AIR.default_state()) diff --git a/steel-core/src/world/block_updates/mod.rs b/steel-core/src/world/block_updates/mod.rs index 61e88fe1fa5..f5149b900a2 100644 --- a/steel-core/src/world/block_updates/mod.rs +++ b/steel-core/src/world/block_updates/mod.rs @@ -1,5 +1,5 @@ use super::*; -use crate::chunk::Chunk; +use crate::chunk::{Chunk, gameplay_chunk_lookup_cache::LocalFullChunkHolderCache}; mod neighbor_updater; @@ -26,6 +26,22 @@ impl World { .unwrap_or_else(|| REGISTRY.blocks.get_base_state_id(&vanilla_blocks::AIR)) } + /// Gets a live block state while locally retaining Full holder identity when the enclosing + /// gameplay lookup scope guarantees stable active-map membership. + pub(crate) fn get_block_state_with_local_holder_cache( + &self, + pos: BlockPos, + cache: &mut LocalFullChunkHolderCache, + ) -> BlockStateId { + if !self.is_in_valid_bounds(pos) { + return REGISTRY.blocks.get_base_state_id(&vanilla_blocks::VOID_AIR); + } + + cache + .block_state(&self.chunk_map, pos) + .unwrap_or_else(|| REGISTRY.blocks.get_base_state_id(&vanilla_blocks::AIR)) + } + ///Vanilla equivalent: `level.getBrightness()` pub fn light_value_at(&self, layer: LightLayer, pos: BlockPos) -> u8 { if layer == LightLayer::Sky && !self.dimension_type.has_skylight { diff --git a/steel-core/src/world/entity_management.rs b/steel-core/src/world/entity_management.rs index 5842e049763..5cff0c64854 100644 --- a/steel-core/src/world/entity_management.rs +++ b/steel-core/src/world/entity_management.rs @@ -7,6 +7,8 @@ use super::{ WorldChangeRequest, block_entity_ticker, mem, vanilla_entities, }; +use crate::entity::EntityCollisionCandidates; + pub(super) struct NavigatingMobTracker { ids: SyncMutex>, } @@ -549,6 +551,30 @@ impl World { .get_entity_bounding_boxes_in_aabb_matching(aabb, predicate) } + /// Gets entity shapes for movement collision through the selected conservative broad phase. + #[must_use] + pub(crate) fn get_movement_collision_boxes_in_aabb_matching( + &self, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + predicate: impl FnMut(&dyn Entity) -> bool, + ) -> Vec { + self.entity_manager + .get_movement_collision_boxes_in_aabb_matching(aabb, candidates, predicate) + } + + /// Returns whether the selected movement-collision broad phase has a matching entity. + #[must_use] + pub(crate) fn has_movement_collision_in_aabb_matching( + &self, + aabb: &WorldAabb, + candidates: EntityCollisionCandidates, + predicate: impl FnMut(&dyn Entity) -> bool, + ) -> bool { + self.entity_manager + .has_movement_collision_in_aabb_matching(aabb, candidates, predicate) + } + /// Gets the nearest entity intersecting the given bounding box and matching `predicate`. /// /// Only returns entities in loaded chunks. diff --git a/steel-core/src/world/explosion/mod.rs b/steel-core/src/world/explosion/mod.rs new file mode 100644 index 00000000000..fd4eff9297a --- /dev/null +++ b/steel-core/src/world/explosion/mod.rs @@ -0,0 +1,523 @@ +//! Server-side explosion calculation and application. + +use std::sync::Arc; + +use glam::DVec3; +use steel_protocol::packets::game::{CExplode, ExplosionParticleInfo, ExplosionParticlePalette}; +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_registry::fluid::FluidState; +use steel_registry::game_rules::GameRule; +use steel_registry::particle_type::ParticleData; +use steel_registry::sound_event::SoundEventHolder; +use steel_registry::vanilla_game_rules::{ + BLOCK_EXPLOSION_DROP_DECAY, MOB_EXPLOSION_DROP_DECAY, MOB_GRIEFING, TNT_EXPLOSION_DROP_DECAY, +}; +use steel_registry::{sound_events, vanilla_particle_types}; +use steel_utils::{BlockPos, BlockStateId}; + +use crate::behavior::FLUID_BEHAVIORS; +use crate::entity::damage::DamageSource; +use crate::entity::{Entity, SharedEntity}; +use crate::world::World; + +use server::ServerExplosion; + +mod server; + +const MAX_PACKET_DISTANCE_SQUARED: f64 = 64.0 * 64.0; + +/// High-level source category used to resolve Vanilla explosion gamerules. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ExplosionInteraction { + /// Damage entities without changing blocks. + None, + /// A block-created explosion governed by `block_explosion_drop_decay`. + Block, + /// A mob-created explosion governed by mob griefing and mob drop decay. + Mob, + /// A TNT explosion governed by `tnt_explosion_drop_decay`. + Tnt, +} + +/// How an explosion interacts with blocks after its affected positions are calculated. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BlockInteraction { + /// Preserve blocks. + Keep, + /// Destroy affected blocks without explosion decay. + Destroy, + /// Destroy affected blocks with the explosion radius in their loot context. + DestroyWithDecay, +} + +impl BlockInteraction { + /// Returns Vanilla's default block-like-entity interaction flag. + #[must_use] + pub const fn should_affect_blocklike_entities(self) -> bool { + matches!(self, Self::Destroy | Self::DestroyWithDecay) + } +} + +/// Read-only explosion context exposed to blocks, entities, and calculators. +pub trait Explosion { + /// Returns the world executing this explosion. + fn world(&self) -> &Arc; + /// Returns the damage source carried by this explosion. + fn damage_source(&self) -> &DamageSource; + /// Returns the resolved block interaction policy. + fn block_interaction(&self) -> BlockInteraction; + /// Returns the living entity credited for this explosion, if any. + fn indirect_source_entity(&self) -> Option<&dyn Entity>; + /// Returns the entity directly creating this explosion, if any. + fn direct_source_entity(&self) -> Option<&dyn Entity>; + /// Returns the explosion radius. + fn radius(&self) -> f32; + /// Returns the exact explosion center. + fn center(&self) -> DVec3; + /// Returns whether block-like entities may be affected. + fn should_affect_blocklike_entities(&self) -> bool; +} + +/// Customizable Vanilla explosion resistance, damage, and knockback policy. +pub trait ExplosionDamageCalculator: Send + Sync { + /// Returns the effective resistance at an affected block position. + fn block_explosion_resistance( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + default_block_explosion_resistance(state, fluid) + } + + /// Returns whether the current ray may affect this block position. + fn should_block_explode( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + true + } + + /// Returns whether the explosion damages this entity. + fn should_damage_entity(&self, _explosion: &dyn Explosion, _entity: &dyn Entity) -> bool { + true + } + + /// Returns the multiplier applied to this entity's explosion knockback. + fn knockback_multiplier(&self, _entity: &dyn Entity) -> f32 { + 1.0 + } + + /// Calculates Vanilla explosion damage for one entity and exposure value. + #[expect( + clippy::manual_midpoint, + reason = "preserve Vanilla's exact floating-point expression" + )] + fn entity_damage_amount( + &self, + explosion: &dyn Explosion, + entity: &dyn Entity, + exposure: f32, + ) -> f32 { + let double_radius = explosion.radius() * 2.0; + let distance = entity.position().distance(explosion.center()) / f64::from(double_radius); + let impact = (1.0 - distance) * f64::from(exposure); + (((impact * impact + impact) / 2.0) * 7.0 * f64::from(double_radius) + 1.0) as f32 + } +} + +/// State-only view available to explicitly immutable explosion block calculators. +/// +/// Keeping this surface narrow prevents worker tasks from reaching mutable world, +/// entity, block-entity, or general [`crate::world::LevelReader`] behavior. +pub(crate) trait ExplosionBlockReader: Sync { + /// Returns `None` when `pos` is outside a bounded reader's stable view. + fn block_state(&self, pos: BlockPos) -> Option; +} + +impl ExplosionBlockReader for World { + #[inline] + fn block_state(&self, pos: BlockPos) -> Option { + Some(self.get_block_state(pos)) + } +} + +/// Opt-in capability for calculators whose block-ray decisions are pure. +/// +/// This trait is crate-private so public custom calculators continue through the +/// sequential compatibility lane unless Steel can prove their implementation is +/// immutable. Implementations may only inspect block states through +/// [`ExplosionBlockReader`]. +pub(crate) trait ImmutableExplosionBlockCalculator: Send + Sync { + /// Returns the greatest per-axis block offset (Chebyshev radius) this calculator reads from + /// each ray position. Implementations opting in must never increase ray power through a + /// returned resistance. + /// + /// Opting in permits one bounded, stable world view for the ray phase. Every reader access + /// made by [`Self::explosion_resistance`] and [`Self::should_explode`] must remain within this + /// distance. Returning `None` retains the live-world compatibility path. + fn bounded_block_read_radius(&self) -> Option { + None + } + + /// Whether repeated resistance queries for an unchanged position may reuse one result. + /// + /// A bounded reader guarantees stable block states during ray traversal. An implementation + /// must still opt in because the compatibility contract permits calculators to observe how + /// often this method runs. Live-world fallback traversal does not cache reads or resistance. + fn can_cache_explosion_resistance(&self) -> bool { + false + } + + /// Whether [`Self::should_explode`] unconditionally returns `true` without side effects. + /// + /// This permits the immutable ray lane to omit the virtual callback and repeated affected-set + /// probes for positions that have already been accepted. Calculators must retain the default + /// unless every state and remaining-power value is allowed. + fn always_allows_block_explosion(&self) -> bool { + false + } + + /// Returns the effective resistance at a ray position. + fn explosion_resistance( + &self, + _reader: &dyn ExplosionBlockReader, + _pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + default_block_explosion_resistance(state, fluid) + } + + fn should_explode( + &self, + _reader: &dyn ExplosionBlockReader, + _pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + true + } +} + +impl ImmutableExplosionBlockCalculator for DefaultExplosionDamageCalculator { + fn bounded_block_read_radius(&self) -> Option { + Some(0) + } + + fn can_cache_explosion_resistance(&self) -> bool { + true + } + + fn always_allows_block_explosion(&self) -> bool { + true + } +} + +fn default_block_explosion_resistance(state: BlockStateId, fluid: FluidState) -> Option { + if state.is_air() && fluid.is_empty() { + return None; + } + + let block_resistance = state.get_block().config.explosion_resistance; + let fluid_resistance = FLUID_BEHAVIORS + .get_behavior(fluid.fluid_id) + .explosion_resistance(); + Some(block_resistance.max(fluid_resistance)) +} + +/// Vanilla's source-less default explosion calculator. +#[derive(Debug, Default)] +pub struct DefaultExplosionDamageCalculator; + +impl ExplosionDamageCalculator for DefaultExplosionDamageCalculator {} + +/// Vanilla's calculator that delegates resistance and block decisions to the source entity. +pub struct EntityBasedExplosionDamageCalculator<'a> { + source: &'a dyn Entity, +} + +impl<'a> EntityBasedExplosionDamageCalculator<'a> { + /// Creates a calculator that delegates block decisions to `source`. + #[must_use] + pub const fn new(source: &'a dyn Entity) -> Self { + Self { source } + } +} + +impl ExplosionDamageCalculator for EntityBasedExplosionDamageCalculator<'_> { + fn block_explosion_resistance( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + DefaultExplosionDamageCalculator + .block_explosion_resistance(explosion, world, pos, state, fluid) + .map(|resistance| { + self.source + .block_explosion_resistance(explosion, world, pos, state, fluid, resistance) + }) + } + + fn should_block_explode( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + power: f32, + ) -> bool { + self.source + .should_block_explode(explosion, world, pos, state, power) + } +} + +enum SelectedDamageCalculator<'a> { + Default, + Entity(SharedEntity), + Custom(&'a dyn ExplosionDamageCalculator), +} + +impl SelectedDamageCalculator<'_> { + /// Returns whether entity-effect hooks use Steel's side-effect-free Vanilla defaults. + const fn has_builtin_entity_effects(&self) -> bool { + matches!(self, Self::Default | Self::Entity(_)) + } +} + +impl ExplosionDamageCalculator for SelectedDamageCalculator<'_> { + fn block_explosion_resistance( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + match self { + Self::Default => DefaultExplosionDamageCalculator + .block_explosion_resistance(explosion, world, pos, state, fluid), + Self::Entity(source) => EntityBasedExplosionDamageCalculator::new(source.as_ref()) + .block_explosion_resistance(explosion, world, pos, state, fluid), + Self::Custom(calculator) => { + calculator.block_explosion_resistance(explosion, world, pos, state, fluid) + } + } + } + + fn should_block_explode( + &self, + explosion: &dyn Explosion, + world: &World, + pos: BlockPos, + state: BlockStateId, + power: f32, + ) -> bool { + match self { + Self::Default => DefaultExplosionDamageCalculator + .should_block_explode(explosion, world, pos, state, power), + Self::Entity(source) => EntityBasedExplosionDamageCalculator::new(source.as_ref()) + .should_block_explode(explosion, world, pos, state, power), + Self::Custom(calculator) => { + calculator.should_block_explode(explosion, world, pos, state, power) + } + } + } + + fn should_damage_entity(&self, explosion: &dyn Explosion, entity: &dyn Entity) -> bool { + match self { + Self::Custom(calculator) => calculator.should_damage_entity(explosion, entity), + Self::Default | Self::Entity(_) => { + DefaultExplosionDamageCalculator.should_damage_entity(explosion, entity) + } + } + } + + fn knockback_multiplier(&self, entity: &dyn Entity) -> f32 { + match self { + Self::Custom(calculator) => calculator.knockback_multiplier(entity), + Self::Default | Self::Entity(_) => { + DefaultExplosionDamageCalculator.knockback_multiplier(entity) + } + } + } + + fn entity_damage_amount( + &self, + explosion: &dyn Explosion, + entity: &dyn Entity, + exposure: f32, + ) -> f32 { + match self { + Self::Custom(calculator) => { + calculator.entity_damage_amount(explosion, entity, exposure) + } + Self::Default | Self::Entity(_) => { + DefaultExplosionDamageCalculator.entity_damage_amount(explosion, entity, exposure) + } + } + } +} + +/// Full visual and gameplay parameters for one server explosion. +pub struct ExplosionOptions<'a> { + /// Entity directly creating the explosion. + pub source: Option, + /// Optional non-default damage source. + pub damage_source: Option, + /// Optional non-default damage calculator. + pub damage_calculator: Option<&'a dyn ExplosionDamageCalculator>, + /// Proven-immutable block-ray calculator for Steel-owned explosion sources. + pub(crate) immutable_block_calculator: Option<&'a dyn ImmutableExplosionBlockCalculator>, + /// Exact explosion center. + pub center: DVec3, + /// Explosion radius. + pub radius: f32, + /// Whether the explosion can create fire. + pub fire: bool, + /// High-level block interaction category. + pub interaction: ExplosionInteraction, + /// Particle used for small or non-block-interacting explosions. + pub small_explosion_particle: ParticleData, + /// Particle used for large block-interacting explosions. + pub large_explosion_particle: ParticleData, + /// Weighted client-side block debris particle palette. + pub block_particles: ExplosionParticlePalette, + /// Client-side explosion sound. + pub explosion_sound: SoundEventHolder, +} + +impl ExplosionOptions<'_> { + /// Creates an explosion with Vanilla's standard visual effects. + #[must_use] + pub fn new(center: DVec3, radius: f32, interaction: ExplosionInteraction) -> Self { + Self { + source: None, + damage_source: None, + damage_calculator: None, + immutable_block_calculator: None, + center, + radius, + fire: false, + interaction, + small_explosion_particle: ParticleData::simple(&vanilla_particle_types::EXPLOSION), + large_explosion_particle: ParticleData::simple( + &vanilla_particle_types::EXPLOSION_EMITTER, + ), + block_particles: default_block_particles(), + explosion_sound: SoundEventHolder::registry(&sound_events::ENTITY_GENERIC_EXPLODE), + } + } +} + +/// Observable server result of running one explosion. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ExplosionOutcome { + /// Number of unique positions reached by the explosion rays. + pub affected_block_count: usize, +} + +impl World { + /// Runs one explosion atomically on the game tick and sends its client effects. + pub fn explode(self: &Arc, options: ExplosionOptions<'_>) -> ExplosionOutcome { + let block_interaction = self.resolve_block_interaction(options.interaction); + let mut explosion = ServerExplosion::new( + self, + options.source, + options.damage_source, + options.damage_calculator, + options.immutable_block_calculator, + options.center, + options.radius, + options.fire, + block_interaction, + ); + let affected_block_count = explosion.explode(); + let explosion_particle = if explosion.is_small() { + options.small_explosion_particle + } else { + options.large_explosion_particle + }; + #[expect( + clippy::manual_unwrap_or, + reason = "make the protocol saturation behavior explicit" + )] + let packet_block_count = match i32::try_from(affected_block_count) { + Ok(count) => count, + Err(_) => i32::MAX, + }; + + self.players.iter_players(|_, player| { + if player.position().distance_squared(options.center) < MAX_PACKET_DISTANCE_SQUARED { + player.send_packet(CExplode { + center: options.center, + radius: options.radius, + block_count: packet_block_count, + player_knockback: explosion.hit_players.get(&player.id()).copied(), + explosion_particle: explosion_particle.clone(), + explosion_sound: options.explosion_sound.clone(), + block_particles: options.block_particles.clone(), + }); + } + true + }); + + ExplosionOutcome { + affected_block_count, + } + } + + fn resolve_block_interaction(&self, interaction: ExplosionInteraction) -> BlockInteraction { + match interaction { + ExplosionInteraction::None => BlockInteraction::Keep, + ExplosionInteraction::Block => self.destroy_interaction(&BLOCK_EXPLOSION_DROP_DECAY), + ExplosionInteraction::Mob => { + if self.get_game_rule(&MOB_GRIEFING) { + self.destroy_interaction(&MOB_EXPLOSION_DROP_DECAY) + } else { + BlockInteraction::Keep + } + } + ExplosionInteraction::Tnt => self.destroy_interaction(&TNT_EXPLOSION_DROP_DECAY), + } + } + + fn destroy_interaction(&self, decay_rule: &GameRule) -> BlockInteraction { + if self.get_game_rule(decay_rule) { + BlockInteraction::DestroyWithDecay + } else { + BlockInteraction::Destroy + } + } +} + +fn default_block_particles() -> ExplosionParticlePalette { + let entries = [ + ExplosionParticleInfo { + particle: ParticleData::simple(&vanilla_particle_types::POOF), + scaling: 0.5, + speed: 1.0, + }, + ExplosionParticleInfo { + particle: ParticleData::simple(&vanilla_particle_types::SMOKE), + scaling: 1.0, + speed: 1.0, + }, + ] + .into_iter() + .map(|particle| (particle, 1)) + .collect(); + let Ok(palette) = ExplosionParticlePalette::try_new(entries) else { + panic!("Vanilla explosion particle palette must be valid"); + }; + palette +} diff --git a/steel-core/src/world/explosion/server.rs b/steel-core/src/world/explosion/server.rs new file mode 100644 index 00000000000..a3da6c6e39c --- /dev/null +++ b/steel-core/src/world/explosion/server.rs @@ -0,0 +1,178 @@ +//! Vanilla server explosion orchestration. + +use std::sync::Arc; + +use glam::DVec3; +use rustc_hash::FxHashMap; +use steel_registry::vanilla_game_rules::MOB_GRIEFING; +use steel_registry::{vanilla_damage_types, vanilla_entities, vanilla_game_events}; + +use crate::entity::damage::DamageSource; +use crate::entity::{Entity, SharedEntity}; +use crate::world::World; +use crate::world::game_event::GameEventContext; + +use super::{ + BlockInteraction, Explosion, ExplosionDamageCalculator, ImmutableExplosionBlockCalculator, + SelectedDamageCalculator, +}; + +mod block_effects; +mod block_rays; +mod entity_effects; +mod exposure; + +const SMALL_EXPLOSION_RADIUS: f32 = 2.0; + +#[cfg(test)] +use block_rays::RAY_COUNT; +#[cfg(test)] +use exposure::{EntityExplosionExposure, seen_percent}; + +pub(super) struct ServerExplosion<'a> { + world: &'a Arc, + fire: bool, + block_interaction: BlockInteraction, + center: DVec3, + source: Option, + indirect_source: Option, + radius: f32, + damage_source: DamageSource, + damage_calculator: SelectedDamageCalculator<'a>, + immutable_block_calculator: Option<&'a dyn ImmutableExplosionBlockCalculator>, + pub(super) hit_players: FxHashMap, +} + +impl<'a> ServerExplosion<'a> { + #[expect( + clippy::too_many_arguments, + reason = "mirrors the Vanilla ServerExplosion construction boundary" + )] + pub(super) fn new( + world: &'a Arc, + source: Option, + damage_source: Option, + damage_calculator: Option<&'a dyn ExplosionDamageCalculator>, + immutable_block_calculator: Option<&'a dyn ImmutableExplosionBlockCalculator>, + center: DVec3, + radius: f32, + fire: bool, + block_interaction: BlockInteraction, + ) -> Self { + let indirect_source = source.as_ref().and_then(|source| { + if source.as_living_entity().is_some() { + Some(Arc::clone(source)) + } else { + source.explosion_indirect_source() + } + }); + let damage_source = damage_source + .unwrap_or_else(|| default_damage_source(source.clone(), indirect_source.clone())); + let damage_calculator = match (damage_calculator, &source) { + (Some(calculator), _) => SelectedDamageCalculator::Custom(calculator), + (None, Some(source)) => SelectedDamageCalculator::Entity(Arc::clone(source)), + (None, None) => SelectedDamageCalculator::Default, + }; + Self { + world, + fire, + block_interaction, + center, + source, + indirect_source, + radius, + damage_source, + damage_calculator, + immutable_block_calculator, + hit_players: FxHashMap::default(), + } + } + + pub(super) fn explode(&mut self) -> usize { + self.world.game_event_at( + &vanilla_game_events::EXPLODE, + self.center, + &GameEventContext::new(self.source.as_deref(), None), + ); + let mut affected = self.calculate_exploded_positions_from_level_random(); + self.hurt_entities(); + if self.interacts_with_blocks() { + self.interact_with_blocks(&mut affected); + } + if self.fire { + self.create_fire(&affected); + } + affected.len() + } + + fn interacts_with_blocks(&self) -> bool { + self.block_interaction != BlockInteraction::Keep + } + + pub(super) fn is_small(&self) -> bool { + self.radius < SMALL_EXPLOSION_RADIUS || !self.interacts_with_blocks() + } +} + +impl Explosion for ServerExplosion<'_> { + fn world(&self) -> &Arc { + self.world + } + + fn damage_source(&self) -> &DamageSource { + &self.damage_source + } + + fn block_interaction(&self) -> BlockInteraction { + self.block_interaction + } + + fn indirect_source_entity(&self) -> Option<&dyn Entity> { + self.indirect_source.as_deref() + } + + fn direct_source_entity(&self) -> Option<&dyn Entity> { + self.source.as_deref() + } + + fn radius(&self) -> f32 { + self.radius + } + + fn center(&self) -> DVec3 { + self.center + } + + fn should_affect_blocklike_entities(&self) -> bool { + let is_wind_charge = self.source.as_ref().is_some_and(|source| { + source.entity_type() == &vanilla_entities::BREEZE_WIND_CHARGE + || source.entity_type() == &vanilla_entities::WIND_CHARGE + }); + !is_wind_charge + && (self.world.get_game_rule(&MOB_GRIEFING) + || self.block_interaction.should_affect_blocklike_entities()) + } +} + +/// Vanilla `DamageSources.explosion(direct, indirect)`. +fn default_damage_source( + direct: Option, + indirect: Option, +) -> DamageSource { + let damage_type = if direct.is_some() && indirect.is_some() { + &vanilla_damage_types::PLAYER_EXPLOSION + } else { + &vanilla_damage_types::EXPLOSION + }; + let mut damage_source = DamageSource::environment(damage_type); + if let Some(direct) = direct { + damage_source = damage_source.with_direct_entity(direct); + } + if let Some(indirect) = indirect { + damage_source = damage_source.with_causing_entity(indirect); + } + damage_source +} + +#[cfg(test)] +mod tests; diff --git a/steel-core/src/world/explosion/server/block_effects.rs b/steel-core/src/world/explosion/server/block_effects.rs new file mode 100644 index 00000000000..735835a74d9 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_effects.rs @@ -0,0 +1,181 @@ +//! Block callbacks, drops, and fire creation for server explosions. + +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_registry::item_stack::ItemStack; +use steel_utils::BlockPos; +use steel_utils::random::Random; +use steel_utils::types::UpdateFlags; + +use crate::behavior::BLOCK_BEHAVIORS; +use crate::behavior::blocks::FireBlock; +use crate::chunk::gameplay_chunk_lookup_cache::LocalFullChunkHolderCache; +use crate::entity::entities::ItemEntity; + +use super::ServerExplosion; + +const FIRE_CHANCE_DENOMINATOR: i32 = 3; +const MAX_DROPS_PER_COMBINED_STACK: i32 = 16; + +impl ServerExplosion<'_> { + pub(super) fn interact_with_blocks(&self, affected: &mut [BlockPos]) { + self.world.with_random(|random| { + vanilla_shuffle(affected, |bound| random.next_i32_bounded(bound)); + }); + let mut stacks = Vec::new(); + let mut full_chunks = LocalFullChunkHolderCache::new(); + + for &pos in affected.iter() { + let state = self + .world + .get_block_state_with_local_holder_cache(pos, &mut full_chunks); + BLOCK_BEHAVIORS + .get_behavior(state.get_block()) + .on_explosion_hit(state, self.world, pos, self, &mut |stack, stack_pos| { + add_or_append_stack(&mut stacks, stack, stack_pos); + }); + } + + for stack in stacks { + self.world.pop_resource(stack.pos, stack.stack); + } + } + + pub(super) fn create_fire(&self, affected: &[BlockPos]) { + self.create_fire_with(affected, || { + self.world + .with_random(|random| random.next_i32_bounded(FIRE_CHANCE_DENOMINATOR)) + }); + } + + fn create_fire_with(&self, affected: &[BlockPos], mut next_int: impl FnMut() -> i32) { + for &pos in affected { + if next_int() == 0 + && self.world.get_block_state(pos).is_air() + && self.world.get_block_state(pos.below()).is_solid_render() + { + self.world.set_block( + pos, + FireBlock::get_state(self.world.as_ref(), pos), + UpdateFlags::UPDATE_ALL, + ); + } + } + } +} + +fn vanilla_shuffle(values: &mut [T], mut next_index: impl FnMut(i32) -> i32) { + let Ok(length) = i32::try_from(values.len()) else { + return; + }; + for remaining in (2..=length).rev() { + let swap_index = next_index(remaining) as usize; + values.swap(remaining as usize - 1, swap_index); + } +} + +struct StackCollector { + pos: BlockPos, + stack: ItemStack, +} + +fn add_or_append_stack(stacks: &mut Vec, mut stack: ItemStack, pos: BlockPos) { + for collector in stacks.iter_mut() { + if ItemEntity::are_mergeable(&collector.stack, &stack) { + let available = collector + .stack + .max_stack_size() + .min(MAX_DROPS_PER_COMBINED_STACK) + - collector.stack.count(); + let transferred = available.min(stack.count()); + collector.stack = collector + .stack + .copy_with_count(collector.stack.count() + transferred); + stack.shrink(transferred); + if stack.is_empty() { + return; + } + } + } + stacks.push(StackCollector { pos, stack }); +} + +#[cfg(test)] +mod tests { + use glam::DVec3; + use steel_registry::item_stack::ItemStack; + use steel_registry::{init_vanilla_registry, vanilla_blocks, vanilla_items}; + use steel_utils::types::UpdateFlags; + use steel_utils::{BlockPos, ChunkPos}; + + use super::*; + use crate::behavior::init_behaviors; + use crate::test_support::{fresh_test_world, insert_ready_full_chunk}; + use crate::world::explosion::BlockInteraction; + + #[test] + fn fire_creation_draws_before_testing_air_and_support() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("explosion_fire_order"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let supported = BlockPos::new(1, 64, 1); + let unsupported = supported.east(); + let occupied = unsupported.east(); + assert!(world.set_block( + supported.below(), + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert!(world.set_block( + occupied, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + DVec3::ZERO, + 1.0, + true, + BlockInteraction::Destroy, + ); + let mut draws = 0; + + explosion.create_fire_with(&[unsupported, occupied, supported], || { + draws += 1; + 0 + }); + + assert_eq!(draws, 3); + assert!(world.get_block_state(unsupported).is_air()); + assert_eq!( + world.get_block_state(occupied).get_block(), + &vanilla_blocks::STONE + ); + assert!(!world.get_block_state(supported).is_air()); + } + + #[test] + fn combined_explosion_drops_never_exceed_vanilla_stack_limit() { + const INPUT_STACK_SIZE: i32 = 10; + const EXPECTED_STACK_COUNT: usize = 2; + + init_vanilla_registry(); + let stack = ItemStack::with_count(&vanilla_items::STONE, INPUT_STACK_SIZE); + let mut stacks = Vec::new(); + + add_or_append_stack(&mut stacks, stack.clone(), BlockPos::ZERO); + add_or_append_stack(&mut stacks, stack, BlockPos::ZERO); + + assert_eq!(stacks.len(), EXPECTED_STACK_COUNT); + assert_eq!(stacks[0].stack.count(), MAX_DROPS_PER_COMBINED_STACK); + assert_eq!( + stacks[1].stack.count(), + INPUT_STACK_SIZE * 2 - MAX_DROPS_PER_COMBINED_STACK + ); + } +} diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs new file mode 100644 index 00000000000..34696546de2 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -0,0 +1,350 @@ +//! Vanilla affected-block ray traversal and immutable-calculator fast paths. + +use std::sync::LazyLock; + +use glam::DVec3; +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_utils::random::Random; +use steel_utils::{BlockPos, BlockStateId}; + +use crate::world::explosion::{ + ExplosionBlockReader, ExplosionDamageCalculator as _, ImmutableExplosionBlockCalculator, +}; +use crate::world::{BlockRegionBounds, BlockRegionRead, World}; + +use super::ServerExplosion; + +mod cache; +mod java_block_pos_set; + +use java_block_pos_set::JavaBlockPosSet; + +#[cfg(test)] +use cache::{ + ExplosionBlockCache, ImmutableRayCachePolicy, bounded_floor_to_i32, + visit_immutable_ray_positions_cached, +}; + +const RAY_GRID_SIZE: i32 = 16; +const RAY_GRID_LAST_INDEX: i32 = RAY_GRID_SIZE - 1; +const RAY_GRID_INTERIOR_SIZE: i32 = RAY_GRID_SIZE - 2; +pub(super) const RAY_COUNT: usize = (RAY_GRID_SIZE * RAY_GRID_SIZE * RAY_GRID_SIZE + - RAY_GRID_INTERIOR_SIZE * RAY_GRID_INTERIOR_SIZE * RAY_GRID_INTERIOR_SIZE) + as usize; +const RAY_STEP: f64 = 0.3_f32 as f64; +const RAY_POWER_DECAY: f32 = 0.225_000_01; +const INITIAL_RAY_POWER_BASE: f32 = 0.7; +const INITIAL_RAY_POWER_RANDOM_SCALE: f32 = 0.6; +const MAX_INITIAL_RAY_POWER_SCALE: f32 = 1.3; +const RESISTANCE_POWER_OFFSET: f32 = 0.3; +const RESISTANCE_POWER_SCALE: f32 = 0.3; +const RAY_REGION_BLOCK_PADDING: f64 = 1.0; + +#[derive(Clone, Copy)] +struct ExplosionRay { + step: DVec3, + initial_power: f32, +} + +struct RegionExplosionBlockReader<'reader, 'world> { + region: &'reader BlockRegionRead<'world>, +} + +impl<'reader, 'world> RegionExplosionBlockReader<'reader, 'world> { + const fn new(region: &'reader BlockRegionRead<'world>) -> Self { + Self { region } + } +} + +impl ExplosionBlockReader for RegionExplosionBlockReader<'_, '_> { + #[inline] + fn block_state(&self, pos: BlockPos) -> Option { + self.region.get_block_state(pos) + } +} + +static RAY_STEPS: LazyLock<[DVec3; RAY_COUNT]> = LazyLock::new(|| { + let mut steps = [DVec3::ZERO; RAY_COUNT]; + let mut index = 0; + // Keep Vanilla's X/Y/Z traversal order: random ray powers are consumed in this order. + for xx in 0..RAY_GRID_SIZE { + for yy in 0..RAY_GRID_SIZE { + for zz in 0..RAY_GRID_SIZE { + if is_boundary_ray(xx, yy, zz) { + steps[index] = ray_direction(xx, yy, zz) * RAY_STEP; + index += 1; + } + } + } + } + debug_assert_eq!(index, RAY_COUNT); + steps +}); + +#[derive(Clone, Copy)] +struct ExplosionRayContext { + center: DVec3, + bounds: ExplosionWorldBounds, +} + +impl ExplosionRayContext { + /// Proves the cached traversal's current and first out-of-region samples fit in `i32`. + /// Every component is bounded by [`RAY_STEP`]. One-cell headroom on each face therefore covers + /// the sample that makes the bounded reader request the generic fallback. + fn can_use_bounded_floor(self, region_bounds: BlockRegionBounds) -> bool { + let (min, max) = region_bounds.corners(); + self.center.is_finite() + && ray_axis_has_bounded_floor(self.center.x, min.x(), max.x()) + && ray_axis_has_bounded_floor(self.center.y, min.y(), max.y()) + && ray_axis_has_bounded_floor(self.center.z, min.z(), max.z()) + } +} + +fn ray_axis_has_bounded_floor(center: f64, min: i32, max: i32) -> bool { + min > i32::MIN && max < i32::MAX && center >= f64::from(min) && center < f64::from(max) + 1.0 +} + +#[derive(Clone, Copy)] +struct ExplosionWorldBounds { + min_y: i32, + max_y: i32, +} + +impl ExplosionWorldBounds { + const fn from_world(world: &World) -> Self { + Self { + min_y: world.get_min_y(), + max_y: world.get_max_y(), + } + } + + const fn contains(self, pos: BlockPos) -> bool { + pos.y() >= self.min_y && pos.y() <= self.max_y && World::is_in_world_bounds_horizontal(pos) + } +} + +impl ServerExplosion<'_> { + pub(super) fn calculate_exploded_positions_from_level_random(&self) -> Vec { + let Some(calculator) = self.immutable_calculator_for_rays() else { + return self.calculate_exploded_positions_sequential(|| { + self.world.with_random(Random::next_f32) + }); + }; + + let powers = self + .world + .with_random(|random| self.draw_immutable_ray_powers(|| random.next_f32())); + self.calculate_immutable_ray_powers(&powers, calculator) + } + + #[cfg(test)] + pub(super) fn calculate_exploded_positions( + &self, + mut next_float: impl FnMut() -> f32, + ) -> Vec { + let Some(calculator) = self.immutable_calculator_for_rays() else { + return self.calculate_exploded_positions_sequential(next_float); + }; + + let powers = self.draw_immutable_ray_powers(&mut next_float); + self.calculate_immutable_ray_powers(&powers, calculator) + } + + fn immutable_calculator_for_rays(&self) -> Option<&dyn ImmutableExplosionBlockCalculator> { + if !self.radius.is_finite() || self.radius < 0.0 { + return None; + } + self.immutable_block_calculator + } + + fn calculate_immutable_ray_powers( + &self, + powers: &[f32; RAY_COUNT], + calculator: &dyn ImmutableExplosionBlockCalculator, + ) -> Vec { + if let Some(read_radius) = calculator.bounded_block_read_radius() + && let Some(bounds) = self.immutable_ray_region_bounds(read_radius) + && let Some(Some(affected)) = self.world.try_with_block_region(bounds, |region| { + if !region.has_complete_data() { + return None; + } + let reader = RegionExplosionBlockReader::new(region); + self.calculate_immutable_ray_powers_with_reader(powers, calculator, &reader, bounds) + }) + { + return affected; + } + + self.calculate_immutable_ray_powers_uncached_with_reader( + powers, + calculator, + self.world.as_ref(), + ) + } + + fn calculate_immutable_ray_powers_uncached_with_reader( + &self, + powers: &[f32; RAY_COUNT], + calculator: &dyn ImmutableExplosionBlockCalculator, + reader: &R, + ) -> Vec { + let context = ExplosionRayContext { + center: self.center, + bounds: ExplosionWorldBounds::from_world(self.world), + }; + let mut affected = JavaBlockPosSet::default(); + for (&step, &initial_power) in RAY_STEPS.iter().zip(powers) { + visit_immutable_ray_positions( + ExplosionRay { + step, + initial_power, + }, + context, + reader, + calculator, + |pos| { + affected.insert(pos); + }, + ); + } + affected.into_iter().collect() + } + + fn immutable_ray_region_bounds(&self, read_radius: u32) -> Option { + if !self.center.is_finite() { + return None; + } + let maximum_ray_distance = f64::from(self.radius) * f64::from(MAX_INITIAL_RAY_POWER_SCALE) + / f64::from(RAY_POWER_DECAY) + * RAY_STEP; + let extent = maximum_ray_distance + f64::from(read_radius) + RAY_REGION_BLOCK_PADDING; + if !extent.is_finite() { + return None; + } + let extent = DVec3::splat(extent); + Some(BlockRegionBounds::from_corners( + BlockPos::from(self.center - extent), + BlockPos::from(self.center + extent), + )) + } + + fn calculate_exploded_positions_sequential( + &self, + mut next_float: impl FnMut() -> f32, + ) -> Vec { + let mut affected = JavaBlockPosSet::default(); + let bounds = ExplosionWorldBounds::from_world(self.world); + + for &step in RAY_STEPS.iter() { + let mut remaining_power = initial_ray_power(self.radius, next_float()); + let mut ray_pos = self.center; + while remaining_power > 0.0 { + let pos = BlockPos::from(ray_pos); + let state = self.world.get_block_state(pos); + let fluid = state.get_fluid_state(); + if !bounds.contains(pos) { + break; + } + + if let Some(resistance) = self + .damage_calculator + .block_explosion_resistance(self, self.world, pos, state, fluid) + { + remaining_power -= ray_power_loss_from_resistance(resistance); + } + + if remaining_power > 0.0 + && self.damage_calculator.should_block_explode( + self, + self.world, + pos, + state, + remaining_power, + ) + { + affected.insert(pos); + } + + ray_pos += step; + remaining_power -= RAY_POWER_DECAY; + } + } + + affected.into_iter().collect() + } + + fn draw_immutable_ray_powers(&self, mut next_float: impl FnMut() -> f32) -> [f32; RAY_COUNT] { + let mut powers = [0.0; RAY_COUNT]; + for power in &mut powers { + *power = initial_ray_power(self.radius, next_float()); + } + powers + } +} + +fn ray_direction(xx: i32, yy: i32, zz: i32) -> DVec3 { + let mut xd = ray_direction_component(xx); + let mut yd = ray_direction_component(yy); + let mut zd = ray_direction_component(zz); + let direction_length = (xd * xd + yd * yd + zd * zd).sqrt(); + xd /= direction_length; + yd /= direction_length; + zd /= direction_length; + DVec3::new(xd, yd, zd) +} + +fn ray_direction_component(index: i32) -> f64 { + f64::from(index as f32 / RAY_GRID_LAST_INDEX as f32 * 2.0 - 1.0) +} + +fn initial_ray_power(radius: f32, random: f32) -> f32 { + radius * (INITIAL_RAY_POWER_BASE + random * INITIAL_RAY_POWER_RANDOM_SCALE) +} + +fn ray_power_loss_from_resistance(resistance: f32) -> f32 { + (resistance + RESISTANCE_POWER_OFFSET) * RESISTANCE_POWER_SCALE +} + +const fn is_boundary_ray(xx: i32, yy: i32, zz: i32) -> bool { + xx == 0 + || xx == RAY_GRID_LAST_INDEX + || yy == 0 + || yy == RAY_GRID_LAST_INDEX + || zz == 0 + || zz == RAY_GRID_LAST_INDEX +} + +fn visit_immutable_ray_positions( + ray: ExplosionRay, + context: ExplosionRayContext, + reader: &R, + calculator: &dyn ImmutableExplosionBlockCalculator, + mut visit: impl FnMut(BlockPos), +) { + let mut remaining_power = ray.initial_power; + let mut ray_pos = context.center; + while remaining_power > 0.0 { + let pos = BlockPos::from(ray_pos); + let Some(state) = reader.block_state(pos) else { + return; + }; + let fluid = state.get_fluid_state(); + if !context.bounds.contains(pos) { + break; + } + + if let Some(resistance) = calculator.explosion_resistance(reader, pos, state, fluid) { + remaining_power -= ray_power_loss_from_resistance(resistance); + } + + if remaining_power > 0.0 && calculator.should_explode(reader, pos, state, remaining_power) { + visit(pos); + } + + ray_pos += ray.step; + remaining_power -= RAY_POWER_DECAY; + } +} + +#[cfg(test)] +mod tests; diff --git a/steel-core/src/world/explosion/server/block_rays/cache.rs b/steel-core/src/world/explosion/server/block_rays/cache.rs new file mode 100644 index 00000000000..7b4b3eadb6a --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/cache.rs @@ -0,0 +1,496 @@ +//! Bounded block-ray caches for immutable explosion calculators. + +use std::mem; + +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_utils::{BlockPos, BlockStateId, PackedBlockPos}; + +use crate::world::BlockRegionBounds; +use crate::world::explosion::{ExplosionBlockReader, ImmutableExplosionBlockCalculator}; + +use super::super::ServerExplosion; +use super::{ + ExplosionRay, ExplosionRayContext, ExplosionWorldBounds, JavaBlockPosSet, RAY_POWER_DECAY, + RAY_STEPS, ray_power_loss_from_resistance, +}; + +const BLOCK_CACHE_BITS: u32 = 9; +const BLOCK_CACHE_SIZE: usize = 1 << BLOCK_CACHE_BITS; +const BLOCK_CACHE_MASK: usize = BLOCK_CACHE_SIZE - 1; +/// Bounds the temporary dense-cache allocations while covering standard radius-four explosions. +const MAX_DENSE_BLOCK_CACHE_CELLS: usize = 8_192; +const EMPTY_DENSE_BLOCK_CACHE_SLOT: u16 = u16::MAX; +const DENSE_BLOCK_CACHE_HAS_RESISTANCE: u8 = 1; +const DENSE_BLOCK_CACHE_AFFECTED: u8 = 1 << 1; +const F64_INTEGER_MANTISSA_BIAS: f64 = 6_755_399_441_055_744.0; +const DENSE_BLOCK_CACHE_ENTRY_SIZE_BYTES: usize = 8; +const LONG_HASH_PHI: u64 = 0x9e37_79b9_7f4a_7c15; + +#[derive(Clone, Copy)] +struct ExplosionBlockCacheEntry { + tag: i64, + state: BlockStateId, + resistance: Option, + occupied: bool, + affected: bool, +} + +impl ExplosionBlockCacheEntry { + const EMPTY: Self = Self { + tag: 0, + state: BlockStateId(0), + resistance: None, + occupied: false, + affected: false, + }; +} + +pub(super) struct ExplosionBlockCache { + entries: [ExplosionBlockCacheEntry; BLOCK_CACHE_SIZE], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct DenseExplosionBlockCacheEntry { + resistance: f32, + state: BlockStateId, + flags: u8, + _padding: u8, +} + +const _: [(); DENSE_BLOCK_CACHE_ENTRY_SIZE_BYTES] = + [(); mem::size_of::()]; + +pub(super) struct DenseExplosionBlockCache { + min: BlockPos, + size_x: usize, + size_y: usize, + size_z: usize, + slots: Vec, + entries: Vec, +} + +#[derive(Clone, Copy)] +pub(super) enum ExplosionBlockCacheLookup { + Hit(usize), + Miss(Miss), +} + +pub(super) trait ExplosionRayBlockCache { + type Miss: Copy; + + fn lookup(&self, pos: BlockPos) -> Option>; + fn state(&self, entry_index: usize) -> BlockStateId; + fn resistance(&self, entry_index: usize) -> Option; + fn affected(&self, entry_index: usize) -> bool; + fn insert(&mut self, miss: Self::Miss, state: BlockStateId, resistance: Option) -> usize; + fn mark_affected(&mut self, entry_index: usize); +} + +#[derive(Clone, Copy)] +pub(super) struct ImmutableRayCachePolicy { + pub(super) resistance: bool, + pub(super) always_allows_block_explosion: bool, +} + +impl Default for ExplosionBlockCache { + fn default() -> Self { + Self { + entries: [ExplosionBlockCacheEntry::EMPTY; BLOCK_CACHE_SIZE], + } + } +} + +impl ExplosionRayBlockCache for ExplosionBlockCache { + type Miss = (usize, i64); + + #[inline] + fn lookup(&self, pos: BlockPos) -> Option> { + let tag = PackedBlockPos::from(pos).as_raw(); + let cache_index = explosion_block_cache_index(tag); + let entry = self.entries[cache_index]; + Some(if entry.occupied && entry.tag == tag { + ExplosionBlockCacheLookup::Hit(cache_index) + } else { + ExplosionBlockCacheLookup::Miss((cache_index, tag)) + }) + } + + #[inline] + fn state(&self, entry_index: usize) -> BlockStateId { + self.entries[entry_index].state + } + + #[inline] + fn resistance(&self, entry_index: usize) -> Option { + self.entries[entry_index].resistance + } + + #[inline] + fn affected(&self, entry_index: usize) -> bool { + self.entries[entry_index].affected + } + + #[inline] + fn insert( + &mut self, + (cache_index, tag): Self::Miss, + state: BlockStateId, + resistance: Option, + ) -> usize { + self.entries[cache_index] = ExplosionBlockCacheEntry { + tag, + state, + resistance, + occupied: true, + affected: false, + }; + cache_index + } + + #[inline] + fn mark_affected(&mut self, entry_index: usize) { + self.entries[entry_index].affected = true; + } +} + +impl DenseExplosionBlockCache { + pub(super) fn try_new(bounds: BlockRegionBounds) -> Option { + let (min, max) = bounds.corners(); + let size_x = inclusive_block_count(min.x(), max.x())?; + let size_y = inclusive_block_count(min.y(), max.y())?; + let size_z = inclusive_block_count(min.z(), max.z())?; + let volume = size_x.checked_mul(size_y)?.checked_mul(size_z)?; + if volume > MAX_DENSE_BLOCK_CACHE_CELLS + || volume >= usize::from(EMPTY_DENSE_BLOCK_CACHE_SLOT) + { + return None; + } + + Some(Self { + min, + size_x, + size_y, + size_z, + slots: vec![EMPTY_DENSE_BLOCK_CACHE_SLOT; volume], + entries: Vec::with_capacity(volume), + }) + } + + #[inline] + pub(super) fn cell_index(&self, pos: BlockPos) -> Option { + let x = usize::try_from(i64::from(pos.x()) - i64::from(self.min.x())).ok()?; + let y = usize::try_from(i64::from(pos.y()) - i64::from(self.min.y())).ok()?; + let z = usize::try_from(i64::from(pos.z()) - i64::from(self.min.z())).ok()?; + if x >= self.size_x || y >= self.size_y || z >= self.size_z { + return None; + } + Some((y * self.size_z + z) * self.size_x + x) + } +} + +impl ExplosionRayBlockCache for DenseExplosionBlockCache { + type Miss = usize; + + #[inline] + fn lookup(&self, pos: BlockPos) -> Option> { + let slot_index = self.cell_index(pos)?; + let entry_index = self.slots[slot_index]; + Some(if entry_index == EMPTY_DENSE_BLOCK_CACHE_SLOT { + ExplosionBlockCacheLookup::Miss(slot_index) + } else { + ExplosionBlockCacheLookup::Hit(usize::from(entry_index)) + }) + } + + #[inline] + fn state(&self, entry_index: usize) -> BlockStateId { + self.entries[entry_index].state + } + + #[inline] + fn resistance(&self, entry_index: usize) -> Option { + let entry = self.entries[entry_index]; + (entry.flags & DENSE_BLOCK_CACHE_HAS_RESISTANCE != 0).then_some(entry.resistance) + } + + #[inline] + fn affected(&self, entry_index: usize) -> bool { + self.entries[entry_index].flags & DENSE_BLOCK_CACHE_AFFECTED != 0 + } + + #[inline] + fn insert( + &mut self, + slot_index: Self::Miss, + state: BlockStateId, + resistance: Option, + ) -> usize { + let entry_index = self.entries.len(); + debug_assert!(entry_index < usize::from(EMPTY_DENSE_BLOCK_CACHE_SLOT)); + let (resistance, flags) = resistance.map_or((0.0, 0), |resistance| { + (resistance, DENSE_BLOCK_CACHE_HAS_RESISTANCE) + }); + self.entries.push(DenseExplosionBlockCacheEntry { + resistance, + state, + flags, + _padding: 0, + }); + self.slots[slot_index] = entry_index as u16; + entry_index + } + + #[inline] + fn mark_affected(&mut self, entry_index: usize) { + self.entries[entry_index].flags |= DENSE_BLOCK_CACHE_AFFECTED; + } +} + +fn inclusive_block_count(min: i32, max: i32) -> Option { + usize::try_from(i64::from(max) - i64::from(min) + 1).ok() +} + +impl ServerExplosion<'_> { + pub(super) fn calculate_immutable_ray_powers_with_reader( + &self, + powers: &[f32; super::RAY_COUNT], + calculator: &dyn ImmutableExplosionBlockCalculator, + reader: &R, + bounds: BlockRegionBounds, + ) -> Option> { + let context = ExplosionRayContext { + center: self.center, + bounds: ExplosionWorldBounds::from_world(self.world), + }; + let cache_policy = ImmutableRayCachePolicy { + resistance: calculator.can_cache_explosion_resistance(), + always_allows_block_explosion: calculator.always_allows_block_explosion(), + }; + let use_bounded_floor = context.can_use_bounded_floor(bounds); + + if cache_policy.resistance + && cache_policy.always_allows_block_explosion + && let Some(cache) = DenseExplosionBlockCache::try_new(bounds) + { + return Self::calculate_immutable_ray_powers_with_cache( + powers, + calculator, + reader, + context, + cache_policy, + cache, + use_bounded_floor, + ); + } + + Self::calculate_immutable_ray_powers_with_cache( + powers, + calculator, + reader, + context, + cache_policy, + ExplosionBlockCache::default(), + use_bounded_floor, + ) + } + + fn calculate_immutable_ray_powers_with_cache< + R: ExplosionBlockReader, + C: ExplosionRayBlockCache, + >( + powers: &[f32; super::RAY_COUNT], + calculator: &dyn ImmutableExplosionBlockCalculator, + reader: &R, + context: ExplosionRayContext, + cache_policy: ImmutableRayCachePolicy, + cache: C, + use_bounded_floor: bool, + ) -> Option> { + if use_bounded_floor { + return Self::calculate_immutable_ray_powers_with_cache_mode::( + powers, + calculator, + reader, + context, + cache_policy, + cache, + ); + } + Self::calculate_immutable_ray_powers_with_cache_mode::( + powers, + calculator, + reader, + context, + cache_policy, + cache, + ) + } + + fn calculate_immutable_ray_powers_with_cache_mode< + R: ExplosionBlockReader, + C: ExplosionRayBlockCache, + const USE_BOUNDED_FLOOR: bool, + >( + powers: &[f32; super::RAY_COUNT], + calculator: &dyn ImmutableExplosionBlockCalculator, + reader: &R, + context: ExplosionRayContext, + cache_policy: ImmutableRayCachePolicy, + mut cache: C, + ) -> Option> { + let mut affected = JavaBlockPosSet::default(); + for (&step, &initial_power) in RAY_STEPS.iter().zip(powers) { + if !visit_immutable_ray_positions_cached::( + ExplosionRay { + step, + initial_power, + }, + context, + reader, + calculator, + cache_policy, + &mut cache, + &mut affected, + ) { + return None; + } + } + Some(affected.into_iter().collect()) + } +} + +pub(super) fn visit_immutable_ray_positions_cached< + R: ExplosionBlockReader, + C: ExplosionRayBlockCache, + const USE_BOUNDED_FLOOR: bool, +>( + ray: ExplosionRay, + context: ExplosionRayContext, + reader: &R, + calculator: &dyn ImmutableExplosionBlockCalculator, + cache_policy: ImmutableRayCachePolicy, + cache: &mut C, + affected: &mut JavaBlockPosSet, +) -> bool { + let mut remaining_power = ray.initial_power; + let mut ray_pos = context.center; + let mut previous_cell: Option<(BlockPos, usize)> = None; + while remaining_power > 0.0 { + let pos = ray_block_pos::(ray_pos); + if let Some((previous, cache_index)) = previous_cell + && previous == pos + && cache_policy.resistance + && cache_policy.always_allows_block_explosion + && cache.affected(cache_index) + { + if let Some(resistance) = cache.resistance(cache_index) { + remaining_power -= ray_power_loss_from_resistance(resistance); + } + ray_pos += ray.step; + remaining_power -= RAY_POWER_DECAY; + continue; + } + + let lookup = match previous_cell { + Some((previous, cache_index)) if previous == pos => { + ExplosionBlockCacheLookup::Hit(cache_index) + } + _ => { + let Some(lookup) = cache.lookup(pos) else { + return false; + }; + lookup + } + }; + let state = match lookup { + ExplosionBlockCacheLookup::Hit(cache_index) => cache.state(cache_index), + ExplosionBlockCacheLookup::Miss(_) => { + let Some(state) = reader.block_state(pos) else { + return false; + }; + state + } + }; + if !context.bounds.contains(pos) { + break; + } + + let resistance = match lookup { + ExplosionBlockCacheLookup::Hit(cache_index) if cache_policy.resistance => { + cache.resistance(cache_index) + } + _ => { + let fluid = state.get_fluid_state(); + calculator.explosion_resistance(reader, pos, state, fluid) + } + }; + let cache_index = match lookup { + ExplosionBlockCacheLookup::Hit(cache_index) => cache_index, + ExplosionBlockCacheLookup::Miss(miss) => cache.insert( + miss, + state, + if cache_policy.resistance { + resistance + } else { + None + }, + ), + }; + previous_cell = Some((pos, cache_index)); + + if let Some(resistance) = resistance { + remaining_power -= ray_power_loss_from_resistance(resistance); + } + + if remaining_power > 0.0 { + let already_affected = cache.affected(cache_index); + let should_explode = if cache_policy.always_allows_block_explosion { + !already_affected + } else { + calculator.should_explode(reader, pos, state, remaining_power) + }; + if should_explode && !already_affected { + affected.insert(pos); + cache.mark_affected(cache_index); + } + } + + ray_pos += ray.step; + remaining_power -= RAY_POWER_DECAY; + } + true +} + +#[inline] +fn ray_block_pos(position: glam::DVec3) -> BlockPos { + if USE_BOUNDED_FLOOR { + BlockPos::new( + bounded_floor_to_i32(position.x), + bounded_floor_to_i32(position.y), + bounded_floor_to_i32(position.z), + ) + } else { + BlockPos::from(position) + } +} + +/// Floors a finite in-range coordinate without Rust's saturating float-to-int conversion. +/// +/// Adding `1.5 * 2^52` maps every integral binary64 value in the i32 range exactly into the +/// mantissa; its low 32 bits are the integer's two's-complement representation. +#[inline] +pub(super) fn bounded_floor_to_i32(value: f64) -> i32 { + debug_assert!( + value.is_finite() && value >= f64::from(i32::MIN) && value < f64::from(i32::MAX) + 1.0 + ); + ((value.floor() + F64_INTEGER_MANTISSA_BIAS).to_bits() as u32) as i32 +} + +#[inline] +const fn explosion_block_cache_index(tag: i64) -> usize { + let mut mixed = (tag as u64).wrapping_mul(LONG_HASH_PHI); + mixed ^= mixed >> 32; + mixed ^= mixed >> 16; + (mixed as usize) & BLOCK_CACHE_MASK +} diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs new file mode 100644 index 00000000000..6aca8a9a345 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs @@ -0,0 +1,159 @@ +//! Explosion-local emulation of Java `HashSet` iteration order. + +use std::mem; +use std::vec::IntoIter; + +use steel_utils::BlockPos; + +const JAVA_HASH_MAP_TREEIFY_THRESHOLD: usize = 8; +const JAVA_HASH_MAP_MIN_TREEIFY_CAPACITY: usize = 64; +const JAVA_HASH_MAP_LOAD_FACTOR_NUMERATOR: usize = 3; +const JAVA_HASH_MAP_LOAD_FACTOR_DENOMINATOR: usize = 4; +const JAVA_BLOCK_POS_HASH_MULTIPLIER: i32 = 31; +const JAVA_HASH_MAP_SPREAD_SHIFT: u32 = 16; +const JAVA_BLOCK_POS_SET_EMPTY_INDEX: u32 = u32::MAX; + +#[derive(Default)] +pub(super) struct JavaBlockPosSet { + buckets: Vec, + entries: Vec, +} + +#[derive(Clone, Copy)] +struct JavaBlockPosBucket { + head: u32, + tail: u32, +} + +impl JavaBlockPosBucket { + const EMPTY: Self = Self { + head: JAVA_BLOCK_POS_SET_EMPTY_INDEX, + tail: JAVA_BLOCK_POS_SET_EMPTY_INDEX, + }; +} + +struct JavaBlockPosEntry { + pos: BlockPos, + next: u32, +} + +impl JavaBlockPosSet { + pub(super) fn insert(&mut self, pos: BlockPos) -> bool { + if self.buckets.is_empty() { + self.buckets.resize(16, JavaBlockPosBucket::EMPTY); + self.entries.reserve(16); + } + let index = java_block_pos_bucket(pos, self.buckets.len()); + let bucket = self.buckets[index]; + let mut current = bucket.head; + let mut bin_len = 0; + while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { + let entry = &self.entries[current as usize]; + if entry.pos == pos { + return false; + } + current = entry.next; + bin_len += 1; + } + + let Ok(entry_index) = u32::try_from(self.entries.len()) else { + panic!("JavaBlockPosSet entry arena exceeded its u32 index space"); + }; + assert_ne!( + entry_index, JAVA_BLOCK_POS_SET_EMPTY_INDEX, + "JavaBlockPosSet entry arena exhausted its u32 index space" + ); + self.entries.push(JavaBlockPosEntry { + pos, + next: JAVA_BLOCK_POS_SET_EMPTY_INDEX, + }); + if bucket.tail == JAVA_BLOCK_POS_SET_EMPTY_INDEX { + self.buckets[index] = JavaBlockPosBucket { + head: entry_index, + tail: entry_index, + }; + } else { + self.entries[bucket.tail as usize].next = entry_index; + self.buckets[index].tail = entry_index; + } + + // HashMap attempts to treeify after adding a ninth entry to one bin, but grows the + // table instead while its capacity is below 64. That split changes iteration order. + if self.buckets.len() < JAVA_HASH_MAP_MIN_TREEIFY_CAPACITY + && bin_len >= JAVA_HASH_MAP_TREEIFY_THRESHOLD + { + self.resize(); + } + // Steel intentionally keeps list bins at larger capacities. HashMap tree-bin order can + // depend on JVM identity hashes and is not a reproducible Vanilla ordering contract. + if self.entries.len() + > self.buckets.len() * JAVA_HASH_MAP_LOAD_FACTOR_NUMERATOR + / JAVA_HASH_MAP_LOAD_FACTOR_DENOMINATOR + { + self.resize(); + } + true + } + + #[cfg(test)] + pub(super) const fn bucket_count(&self) -> usize { + self.buckets.len() + } + + fn resize(&mut self) { + let new_capacity = self.buckets.len().saturating_mul(2); + if new_capacity == self.buckets.len() { + return; + } + let resized = vec![JavaBlockPosBucket::EMPTY; new_capacity]; + let old_buckets = mem::replace(&mut self.buckets, resized); + for bucket in old_buckets { + let mut current = bucket.head; + while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { + let entry_index = current as usize; + let next = self.entries[entry_index].next; + let index = java_block_pos_bucket(self.entries[entry_index].pos, new_capacity); + let new_bucket = self.buckets[index]; + self.entries[entry_index].next = JAVA_BLOCK_POS_SET_EMPTY_INDEX; + if new_bucket.tail == JAVA_BLOCK_POS_SET_EMPTY_INDEX { + self.buckets[index] = JavaBlockPosBucket { + head: current, + tail: current, + }; + } else { + self.entries[new_bucket.tail as usize].next = current; + self.buckets[index].tail = current; + } + current = next; + } + } + } +} + +impl IntoIterator for JavaBlockPosSet { + type Item = BlockPos; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + let mut ordered = Vec::with_capacity(self.entries.len()); + for bucket in self.buckets { + let mut current = bucket.head; + while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { + let entry = &self.entries[current as usize]; + ordered.push(entry.pos); + current = entry.next; + } + } + ordered.into_iter() + } +} + +const fn java_block_pos_bucket(pos: BlockPos, capacity: usize) -> usize { + let hash = pos + .y() + .wrapping_add(pos.z().wrapping_mul(JAVA_BLOCK_POS_HASH_MULTIPLIER)) + .wrapping_mul(JAVA_BLOCK_POS_HASH_MULTIPLIER) + .wrapping_add(pos.x()) as u32; + let spread = hash ^ (hash >> JAVA_HASH_MAP_SPREAD_SHIFT); + spread as usize & (capacity - 1) +} diff --git a/steel-core/src/world/explosion/server/block_rays/tests.rs b/steel-core/src/world/explosion/server/block_rays/tests.rs new file mode 100644 index 00000000000..5d4a85f5575 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/tests.rs @@ -0,0 +1,570 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use glam::DVec3; +use sha2::{Digest as _, Sha256}; +use steel_registry::fluid::FluidState; +use steel_registry::{init_vanilla_registry, vanilla_blocks}; +use steel_utils::random::{Random, legacy_random::LegacyRandom}; +use steel_utils::types::UpdateFlags; +use steel_utils::{BlockPos, BlockStateId, ChunkPos}; + +use super::*; +use crate::behavior::init_behaviors; +use crate::test_support::{fresh_test_world, insert_ready_full_chunk}; +use crate::world::explosion::default_block_explosion_resistance; +use crate::world::{BlockInteraction, DefaultExplosionDamageCalculator}; + +const FIXED_RANDOM_SAMPLE: f32 = 0.5; +const STANDARD_TNT_RADIUS: f32 = 4.0; +const MAX_TNT_EXPLOSION_POWER: f32 = 128.0; +const VANILLA_INITIAL_POWER_BASE: f32 = 0.7; +const VANILLA_INITIAL_POWER_RANDOM_SCALE: f32 = 0.6; +const VANILLA_OVERWORLD_MIN_Y: i32 = -64; +const VANILLA_OVERWORLD_MAX_Y: i32 = 319; +const VANILLA_HORIZONTAL_MIN: i32 = -30_000_000; +const VANILLA_HORIZONTAL_MAX_EXCLUSIVE: i32 = 30_000_000; +const EXPECTED_MAX_RADIUS_AFFECTED_COUNT: usize = 280_896; +const EXPECTED_MAX_RADIUS_POSITION_SHA256: &str = + "157409059963f34bd804ca3dc36d83ed5161b02fbf349ff5b05e2ecdb02c7691"; +const FNV1A_64_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325; +const FNV1A_64_PRIME: u64 = 0x100_0000_01b3; +const EXPECTED_RAY_STEPS_FNV1A_64: u64 = 0x0f55_998f_8a80_904d; +const BOUNDED_FLOOR_RANDOM_CASE_COUNT: usize = 20_000; +const BOUNDED_FLOOR_TEST_RNG_SEED: u64 = 0x6a09_e667_f3bc_c909; +const BOUNDED_FLOOR_TEST_RNG_MULTIPLIER: u64 = 6_364_136_223_846_793_005; +const BOUNDED_FLOOR_TEST_RNG_INCREMENT: u64 = 1_442_695_040_888_963_407; +const EXACT_INTEGER_POWER_OF_TWO_SAMPLE: f64 = 65_536.0; +const ORIGIN_BLOCK_CENTER: DVec3 = DVec3::new(0.5, 64.5, 0.5); +// Bottom-center of a resting TNT plus Vanilla's 1/16-height explosion offset. +const RESTING_PRIMED_TNT_EXPLOSION_CENTER: DVec3 = DVec3::new(0.5, 64.061_25, 0.5); + +// OpenJDK 25 iteration order after Vanilla inserts this fixture into its HashSet. +const EXPECTED_JAVA_EMPTY_WORLD_POSITIONS: [BlockPos; 27] = [ + BlockPos::new(0, 64, 0), + BlockPos::new(-1, 64, 1), + BlockPos::new(1, 64, -1), + BlockPos::new(0, 64, 1), + BlockPos::new(1, 64, 0), + BlockPos::new(1, 64, 1), + BlockPos::new(-1, 65, -1), + BlockPos::new(-1, 65, 0), + BlockPos::new(0, 65, -1), + BlockPos::new(-1, 63, -1), + BlockPos::new(-1, 65, 1), + BlockPos::new(0, 65, 0), + BlockPos::new(1, 65, -1), + BlockPos::new(-1, 63, 0), + BlockPos::new(0, 63, -1), + BlockPos::new(0, 65, 1), + BlockPos::new(1, 65, 0), + BlockPos::new(-1, 63, 1), + BlockPos::new(0, 63, 0), + BlockPos::new(1, 63, -1), + BlockPos::new(1, 65, 1), + BlockPos::new(0, 63, 1), + BlockPos::new(1, 63, 0), + BlockPos::new(1, 63, 1), + BlockPos::new(-1, 64, -1), + BlockPos::new(-1, 64, 0), + BlockPos::new(0, 64, -1), +]; + +#[derive(Default)] +struct CountingImmutableCalculator { + resistance_calls: AtomicUsize, + decision_calls: AtomicUsize, + cache_resistance: bool, + always_allows_block_explosion: bool, + bounded_read_radius: Option, +} + +impl ImmutableExplosionBlockCalculator for CountingImmutableCalculator { + fn bounded_block_read_radius(&self) -> Option { + self.bounded_read_radius + } + + fn can_cache_explosion_resistance(&self) -> bool { + self.cache_resistance + } + + fn always_allows_block_explosion(&self) -> bool { + self.always_allows_block_explosion + } + + fn explosion_resistance( + &self, + _reader: &dyn ExplosionBlockReader, + _pos: BlockPos, + state: BlockStateId, + fluid: FluidState, + ) -> Option { + self.resistance_calls.fetch_add(1, Ordering::Relaxed); + default_block_explosion_resistance(state, fluid) + } + + fn should_explode( + &self, + _reader: &dyn ExplosionBlockReader, + _pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + self.decision_calls.fetch_add(1, Ordering::Relaxed); + true + } +} + +fn calculate_cached_immutable_rays( + rays: &[ExplosionRay], + context: ExplosionRayContext, + reader: &R, + calculator: &dyn ImmutableExplosionBlockCalculator, +) -> Vec { + let mut affected = JavaBlockPosSet::default(); + let mut cache = ExplosionBlockCache::default(); + let cache_policy = ImmutableRayCachePolicy { + resistance: calculator.can_cache_explosion_resistance(), + always_allows_block_explosion: calculator.always_allows_block_explosion(), + }; + for ray in rays { + assert!(visit_immutable_ray_positions_cached::< + R, + ExplosionBlockCache, + USE_BOUNDED_FLOOR, + >( + *ray, + context, + reader, + calculator, + cache_policy, + &mut cache, + &mut affected, + )); + } + affected.into_iter().collect() +} + +#[test] +fn deterministic_empty_world_rays_match_the_java_hash_set_fixture() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("explosion_java_hash_set_fixture"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + ORIGIN_BLOCK_CENTER, + 1.0, + false, + BlockInteraction::Destroy, + ); + let mut draws = 0; + + let affected = explosion.calculate_exploded_positions(|| { + draws += 1; + FIXED_RANDOM_SAMPLE + }); + + assert_eq!(draws, RAY_COUNT); + assert_eq!(affected, EXPECTED_JAVA_EMPTY_WORLD_POSITIONS); +} + +#[test] +fn precomputed_ray_steps_match_java_bit_digest() { + let mut digest = FNV1A_64_OFFSET_BASIS; + for step in RAY_STEPS.iter() { + assert!(step.x.abs() < 1.0 && step.y.abs() < 1.0 && step.z.abs() < 1.0); + for bits in [step.x.to_bits(), step.y.to_bits(), step.z.to_bits()] { + for byte in bits.to_le_bytes() { + digest ^= u64::from(byte); + digest = digest.wrapping_mul(FNV1A_64_PRIME); + } + } + } + + // Produced by the Minecraft 26.2 ServerExplosion expression under OpenJDK 25. + assert_eq!(digest, EXPECTED_RAY_STEPS_FNV1A_64); +} + +#[test] +fn maximum_radius_air_rays_match_the_java_membership_fixture() { + struct AirReader(BlockStateId); + + impl ExplosionBlockReader for AirReader { + fn block_state(&self, _pos: BlockPos) -> Option { + Some(self.0) + } + } + + init_vanilla_registry(); + let center = RESTING_PRIMED_TNT_EXPLOSION_CENTER; + let initial_power = MAX_TNT_EXPLOSION_POWER + * (VANILLA_INITIAL_POWER_BASE + FIXED_RANDOM_SAMPLE * VANILLA_INITIAL_POWER_RANDOM_SCALE); + let rays = RAY_STEPS + .iter() + .copied() + .map(|step| ExplosionRay { + step, + initial_power, + }) + .collect::>(); + let mut affected = calculate_cached_immutable_rays::<_, false>( + &rays, + ExplosionRayContext { + center, + bounds: ExplosionWorldBounds { + min_y: VANILLA_OVERWORLD_MIN_Y, + max_y: VANILLA_OVERWORLD_MAX_Y, + }, + }, + &AirReader(vanilla_blocks::AIR.default_state()), + &DefaultExplosionDamageCalculator, + ); + + assert_eq!(affected.len(), EXPECTED_MAX_RADIUS_AFFECTED_COUNT); + affected.sort_unstable_by_key(|pos| (pos.x(), pos.y(), pos.z())); + let mut hasher = Sha256::new(); + for pos in affected { + hasher.update(pos.x().to_be_bytes()); + hasher.update(pos.y().to_be_bytes()); + hasher.update(pos.z().to_be_bytes()); + } + let digest = hasher + .finalize() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::>() + .concat(); + assert_eq!(digest, EXPECTED_MAX_RADIUS_POSITION_SHA256); +} + +#[test] +fn unusual_radii_retain_vanilla_ray_sampling_and_bounds_behavior() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("explosion_unusual_radii"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + + for radius in [f32::NEG_INFINITY, -1.0, -0.0, 0.0, f32::NAN] { + let explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + ORIGIN_BLOCK_CENTER, + radius, + false, + BlockInteraction::Destroy, + ); + let mut draws = 0; + let affected = explosion.calculate_exploded_positions(|| { + draws += 1; + FIXED_RANDOM_SAMPLE + }); + assert_eq!(draws, RAY_COUNT, "radius={radius:?}"); + assert!(affected.is_empty(), "radius={radius:?}"); + } + + let tiny = ServerExplosion::new( + world, + None, + None, + None, + None, + ORIGIN_BLOCK_CENTER, + f32::MIN_POSITIVE, + false, + BlockInteraction::Destroy, + ); + let tiny_affected = tiny.calculate_exploded_positions(|| FIXED_RANDOM_SAMPLE); + assert_eq!(tiny_affected, [BlockPos::new(0, 64, 0)]); + + let positive_infinity = ServerExplosion::new( + world, + None, + None, + None, + None, + DVec3::new( + f64::from(VANILLA_HORIZONTAL_MAX_EXCLUSIVE) + 0.5, + ORIGIN_BLOCK_CENTER.y, + ORIGIN_BLOCK_CENTER.z, + ), + f32::INFINITY, + false, + BlockInteraction::Destroy, + ); + let mut draws = 0; + let affected = positive_infinity.calculate_exploded_positions(|| { + draws += 1; + FIXED_RANDOM_SAMPLE + }); + assert_eq!(draws, RAY_COUNT); + assert_eq!(affected, Vec::new()); +} + +#[test] +fn immutable_rays_match_sequential_lane_in_a_complete_region() { + const RANDOM_SEED: u64 = 0x1A11_0DED; + + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("immutable_explosion_rays"); + let world = &fixture.world; + for x in -1..=1 { + for z in -1..=1 { + insert_ready_full_chunk(world, ChunkPos::new(x, z)); + } + } + // Rays from this center cross chunk and section boundaries through resistant blocks. + let center = DVec3::new(0.5, 63.5, 0.5); + for (pos, state) in [ + ( + BlockPos::new(0, 63, 0), + vanilla_blocks::STONE.default_state(), + ), + ( + BlockPos::new(-1, 64, 0), + vanilla_blocks::WATER.default_state(), + ), + ( + BlockPos::new(2, 63, -1), + vanilla_blocks::OBSIDIAN.default_state(), + ), + ] { + assert!(world.set_block(pos, state, UpdateFlags::UPDATE_NONE)); + } + let calculator = DefaultExplosionDamageCalculator; + + for radius in [-0.0, f32::MIN_POSITIVE, STANDARD_TNT_RADIUS] { + let explosion = |immutable| { + ServerExplosion::new( + world, + None, + None, + None, + immutable, + center, + radius, + false, + BlockInteraction::Destroy, + ) + }; + let immutable = explosion(Some(&calculator as &dyn ImmutableExplosionBlockCalculator)); + let bounds = immutable + .immutable_ray_region_bounds(0) + .expect("finite explosion has bounded ray coverage"); + #[expect( + clippy::redundant_closure_for_method_calls, + reason = "the method item cannot satisfy the region callback's higher-ranked lifetime" + )] + let complete = world.try_with_block_region(bounds, |region| region.has_complete_data()); + assert_eq!( + complete, + Some(true), + "radius={radius:?} must use the bounded cache" + ); + let calculate = |explosion: &ServerExplosion<'_>| { + let mut random = LegacyRandom::from_seed(RANDOM_SEED); + explosion.calculate_exploded_positions(|| random.next_f32()) + }; + + assert_eq!( + calculate(&immutable), + calculate(&explosion(None)), + "radius={radius:?}" + ); + } +} + +#[test] +fn bounded_floor_matches_rust_floor_cast_across_i32_domain() { + let mut values = vec![ + 0.0, + -0.0, + f64::from_bits(1), + -f64::from_bits(1), + 0.5, + -0.5, + f64::from(i32::MIN), + f64::from(i32::MAX), + f64::from(VANILLA_HORIZONTAL_MIN), + f64::from(VANILLA_HORIZONTAL_MAX_EXCLUSIVE), + ]; + for base in [ + f64::from(i32::MIN), + f64::from(VANILLA_HORIZONTAL_MIN), + -EXACT_INTEGER_POWER_OF_TWO_SAMPLE, + -1.0, + 0.0, + 1.0, + EXACT_INTEGER_POWER_OF_TWO_SAMPLE, + f64::from(VANILLA_HORIZONTAL_MAX_EXCLUSIVE), + f64::from(i32::MAX), + ] { + values.extend([base.next_down(), base, base.next_up()]); + } + + let lower = f64::from(i32::MIN); + let upper = f64::from(i32::MAX) + 1.0; + for value in values { + if value >= lower && value < upper { + assert_eq!(bounded_floor_to_i32(value), value.floor() as i32); + } + } + + let mut state = BOUNDED_FLOOR_TEST_RNG_SEED; + for _ in 0..BOUNDED_FLOOR_RANDOM_CASE_COUNT { + state = state + .wrapping_mul(BOUNDED_FLOOR_TEST_RNG_MULTIPLIER) + .wrapping_add(BOUNDED_FLOOR_TEST_RNG_INCREMENT); + let integer = (state >> u32::BITS) as u32 as i32; + state = state + .wrapping_mul(BOUNDED_FLOOR_TEST_RNG_MULTIPLIER) + .wrapping_add(BOUNDED_FLOOR_TEST_RNG_INCREMENT); + let fraction = f64::from(state as u32) / (f64::from(u32::MAX) + 1.0); + let value = f64::from(integer) + fraction; + if value < upper { + assert_eq!(bounded_floor_to_i32(value), value.floor() as i32); + } + } +} + +#[test] +fn incomplete_bounded_region_falls_back_before_calculator_hooks() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("immutable_explosion_incomplete_region"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let center = DVec3::new(15.5, 64.5, 8.5); + let actual_calculator = CountingImmutableCalculator { + bounded_read_radius: Some(0), + ..CountingImmutableCalculator::default() + }; + let actual_explosion = ServerExplosion::new( + world, + None, + None, + None, + Some(&actual_calculator), + center, + STANDARD_TNT_RADIUS, + false, + BlockInteraction::Destroy, + ); + let powers = actual_explosion.draw_immutable_ray_powers(|| FIXED_RANDOM_SAMPLE); + + let actual = actual_explosion.calculate_immutable_ray_powers(&powers, &actual_calculator); + + let expected_calculator = CountingImmutableCalculator::default(); + let expected_explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + center, + STANDARD_TNT_RADIUS, + false, + BlockInteraction::Destroy, + ); + let expected = expected_explosion.calculate_immutable_ray_powers_uncached_with_reader( + &powers, + &expected_calculator, + world.as_ref(), + ); + + assert_eq!(actual, expected); + assert_eq!( + actual_calculator.resistance_calls.load(Ordering::Relaxed), + expected_calculator.resistance_calls.load(Ordering::Relaxed) + ); + assert_eq!( + actual_calculator.decision_calls.load(Ordering::Relaxed), + expected_calculator.decision_calls.load(Ordering::Relaxed) + ); +} + +#[test] +fn bounded_immutable_reader_covers_maximum_power_rays() { + const BOUNDARY_EPSILON: f64 = 0.001; + + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("bounded_explosion_reader_coverage"); + let world = &fixture.world; + let calculator = DefaultExplosionDamageCalculator; + let maximum_initial_power = + STANDARD_TNT_RADIUS * (VANILLA_INITIAL_POWER_BASE + VANILLA_INITIAL_POWER_RANDOM_SCALE); + let powers = [maximum_initial_power; RAY_COUNT]; + + for center in [ + DVec3::new(0.0, 64.0, 0.0), + DVec3::new( + 16.0 - BOUNDARY_EPSILON, + 80.0 - BOUNDARY_EPSILON, + 16.0 - BOUNDARY_EPSILON, + ), + DVec3::new( + -16.0 + BOUNDARY_EPSILON, + 48.0 + BOUNDARY_EPSILON, + -16.0 + BOUNDARY_EPSILON, + ), + ] { + let explosion = ServerExplosion::new( + world, + None, + None, + None, + Some(&calculator), + center, + STANDARD_TNT_RADIUS, + false, + BlockInteraction::Destroy, + ); + let bounds = explosion + .immutable_ray_region_bounds(0) + .expect("finite radius-four explosion has bounded ray coverage"); + let affected = world + .try_with_block_region(bounds, |region| { + let reader = RegionExplosionBlockReader::new(region); + explosion.calculate_immutable_ray_powers_with_reader( + &powers, + &calculator, + &reader, + bounds, + ) + }) + .expect("radius-four ray workset stays within the bounded-reader slot limit") + .expect("bounded reader covers every maximum-power ray access"); + + assert!( + !affected.is_empty(), + "maximum-power rays affect blocks at {center:?}" + ); + } +} + +#[test] +fn java_block_pos_set_resizes_on_a_ninth_collision_before_capacity_sixty_four() { + let mut positions = JavaBlockPosSet::default(); + for x in 1..=13 { + assert!(positions.insert(BlockPos::new(x, 0, 0))); + } + assert_eq!(positions.bucket_count(), 32); + + for x in (0..=224).step_by(32) { + assert!(positions.insert(BlockPos::new(x, 0, 0))); + } + assert_eq!(positions.bucket_count(), 32); + + assert!(positions.insert(BlockPos::new(256, 0, 0))); + assert_eq!(positions.bucket_count(), 64); +} diff --git a/steel-core/src/world/explosion/server/entity_effects.rs b/steel-core/src/world/explosion/server/entity_effects.rs new file mode 100644 index 00000000000..f96d8922007 --- /dev/null +++ b/steel-core/src/world/explosion/server/entity_effects.rs @@ -0,0 +1,132 @@ +//! Entity damage, knockback, and post-explosion callbacks. + +use glam::DVec3; +use steel_registry::vanilla_entity_type_tags::EntityTypeTag; +use steel_registry::{REGISTRY, TaggedRegistryExt as _, vanilla_attributes}; +use steel_utils::types::GameType; +use steel_utils::{BlockPos, WorldAabb}; + +use crate::behavior::BlockCollisionContext; +use crate::entity::Entity; +use crate::entity::entities::PrimedTntEntity; +use crate::world::explosion::ExplosionDamageCalculator as _; +use crate::world::raycast::ExplosionExposureRaycast; + +use super::ServerExplosion; +use super::exposure::EntityExplosionExposure; + +const MIN_DAMAGE_RADIUS: f32 = 1.0e-5; +const DAMAGE_RADIUS_SCALE: f32 = 2.0; +const ENTITY_QUERY_PADDING: f64 = 1.0; +const NORMALIZE_EPSILON: f64 = 1.0e-5_f32 as f64; + +impl ServerExplosion<'_> { + pub(super) fn hurt_entities(&mut self) { + if self.radius < MIN_DAMAGE_RADIUS { + return; + } + + let double_radius = self.radius * DAMAGE_RADIUS_SCALE; + let radius = f64::from(double_radius); + let bounds = WorldAabb::from_min_max( + DVec3::new( + (self.center.x - radius - ENTITY_QUERY_PADDING).floor(), + (self.center.y - radius - ENTITY_QUERY_PADDING).floor(), + (self.center.z - radius - ENTITY_QUERY_PADDING).floor(), + ), + DVec3::new( + (self.center.x + radius + ENTITY_QUERY_PADDING).floor(), + (self.center.y + radius + ENTITY_QUERY_PADDING).floor(), + (self.center.z + radius + ENTITY_QUERY_PADDING).floor(), + ), + ); + let source_id = self.source.as_ref().map(|source| source.id()); + let entities = self.world.get_entities_in_aabb_matching(&bounds, |entity| { + source_id != Some(entity.id()) && !entity.is_spectator() + }); + let builtin_entity_effects = self.damage_calculator.has_builtin_entity_effects(); + let mut exposure_raycast = + ExplosionExposureRaycast::new(self.world.as_ref(), BlockCollisionContext::empty()); + exposure_raycast.configure_clear_grid( + BlockPos::from(bounds.min_corner()), + BlockPos::from(bounds.max_corner()), + ); + // The freshly constructed cache is already safe for the first target. + let mut reusable_from_previous_tnt = true; + + for entity in entities { + // Exact Steel PrimedTNT rejects damage, keeps the base no-op explosion callback, and + // only accepts the impulse. With built-in entity effects, no block mutation can occur + // between these targets, so their static exposure shapes remain current. + let inert_primed_tnt = builtin_entity_effects + && steel_utils::Downcast::downcast_ref::(entity.as_ref()) + .is_some(); + if !reusable_from_previous_tnt || !inert_primed_tnt { + exposure_raycast.clear(); + } + reusable_from_previous_tnt = inert_primed_tnt; + + if entity.ignore_explosion(self) { + continue; + } + let distance = entity.position().distance(self.center) / radius; + if distance > 1.0 { + continue; + } + + let delta = entity.explosion_damage_origin() - self.center; + let delta_length = delta.length(); + let direction = if delta_length < NORMALIZE_EPSILON { + DVec3::ZERO + } else { + delta / delta_length + }; + let should_damage = self + .damage_calculator + .should_damage_entity(self, entity.as_ref()); + let knockback_multiplier = self.damage_calculator.knockback_multiplier(entity.as_ref()); + let exposure = if !should_damage && knockback_multiplier == 0.0 { + 0.0 + } else { + EntityExplosionExposure::capture(entity.as_ref()) + .calculate_cached_with(&mut exposure_raycast, self.center) + }; + + if should_damage { + let amount = + self.damage_calculator + .entity_damage_amount(self, entity.as_ref(), exposure); + entity.hurt(self.world, &self.damage_source, amount); + } + + let knockback_resistance = entity.as_living_entity().map_or(0.0, |living| { + living + .attributes() + .lock() + .required_value(vanilla_attributes::EXPLOSION_KNOCKBACK_RESISTANCE) + }); + let knockback_power = (1.0 - distance) + * f64::from(exposure) + * f64::from(knockback_multiplier) + * (1.0 - knockback_resistance); + let knockback = direction * knockback_power; + entity.push_impulse(knockback); + + if REGISTRY.entity_types.is_in_tag( + entity.entity_type(), + &EntityTypeTag::REDIRECTABLE_PROJECTILE, + ) { + if let Some(projectile) = entity.as_projectile() { + projectile.set_owner_entity(self.damage_source.causing_entity()); + } + } else if let Some(player) = entity.as_player() + && !player.is_spectator() + && (player.game_mode() != GameType::Creative || !player.abilities.lock().flying) + { + self.hit_players.insert(player.id(), knockback); + } + + entity.on_explosion_hit(self.source.as_deref()); + } + } +} diff --git a/steel-core/src/world/explosion/server/exposure.rs b/steel-core/src/world/explosion/server/exposure.rs new file mode 100644 index 00000000000..bd60b4f856a --- /dev/null +++ b/steel-core/src/world/explosion/server/exposure.rs @@ -0,0 +1,240 @@ +//! Vanilla entity exposure sampling and Steel's collision-ray reuse. + +use glam::DVec3; +use steel_registry::vanilla_entities; +use steel_utils::{BlockPos, WorldAabb}; + +use crate::behavior::BlockCollisionContext; +use crate::behavior::blocks::PowderSnowBlock; +use crate::chunk::paletted_container::BlockPalette; +use crate::entity::Entity; +#[cfg(test)] +use crate::world::World as ServerWorld; +use crate::world::raycast::{ExplosionExposureRaycast, collision_path_axis_block_bounds}; +use crate::world::{BlockRegionBounds, MAX_BLOCK_REGION_WORKSET_SLOTS}; + +const EXPOSURE_SAMPLE_DENSITY: f64 = 2.0; +const EXPOSURE_SAMPLE_OFFSET_DIVISOR: f64 = 2.0; +const MAX_EXPOSURE_CERTIFICATE_AXIS_WORK: usize = + MAX_BLOCK_REGION_WORKSET_SLOTS * BlockPalette::SIZE; + +#[derive(Clone, Copy)] +pub(super) struct EntityExplosionExposure { + bounding_box: WorldAabb, + pub(super) collision_context: BlockCollisionContext, + x_step: f64, + y_step: f64, + z_step: f64, + x_offset: f64, + z_offset: f64, +} + +impl EntityExplosionExposure { + pub(super) fn capture(entity: &dyn Entity) -> Self { + let bounding_box = entity.bounding_box(); + let x_step = exposure_axis_step(bounding_box.width()); + let y_step = exposure_axis_step(bounding_box.height()); + let z_step = exposure_axis_step(bounding_box.depth()); + let collision_context = + BlockCollisionContext::entity(entity.position().y, entity.is_descending()) + .with_fall_distance(entity.fall_distance()) + .with_can_walk_on_powder_snow(PowderSnowBlock::can_entity_walk_on_powder_snow( + entity, + )) + .with_falling_block(entity.entity_type() == &vanilla_entities::FALLING_BLOCK); + + Self { + bounding_box, + collision_context, + x_step, + y_step, + z_step, + x_offset: (1.0 - (1.0 / x_step).floor() * x_step) / EXPOSURE_SAMPLE_OFFSET_DIVISOR, + z_offset: (1.0 - (1.0 / z_step).floor() * z_step) / EXPOSURE_SAMPLE_OFFSET_DIVISOR, + } + } + + const fn has_negative_step(self) -> bool { + self.x_step < 0.0 || self.y_step < 0.0 || self.z_step < 0.0 + } + + fn sample_position(&self, x_fraction: f64, y_fraction: f64, z_fraction: f64) -> DVec3 { + DVec3::new( + self.bounding_box.min_x() + + (self.bounding_box.max_x() - self.bounding_box.min_x()) * x_fraction + + self.x_offset, + self.bounding_box.min_y() + + (self.bounding_box.max_y() - self.bounding_box.min_y()) * y_fraction, + self.bounding_box.min_z() + + (self.bounding_box.max_z() - self.bounding_box.min_z()) * z_fraction + + self.z_offset, + ) + } + + fn axis_sample_block_bounds( + axis_min: f64, + axis_max: f64, + step: f64, + offset: f64, + center: f64, + ) -> Option<(i32, i32)> { + let can_sample_axis = axis_min.is_finite() + && axis_max.is_finite() + && axis_max >= axis_min + && step.is_finite() + && step > 0.0 + && offset.is_finite() + && center.is_finite(); + if !can_sample_axis { + return None; + } + + let axis_length = axis_max - axis_min; + let mut min_block = i32::MAX; + let mut max_block = i32::MIN; + let mut fraction = 0.0; + for _ in 0..MAX_EXPOSURE_CERTIFICATE_AXIS_WORK { + if fraction > 1.0 { + return Some((min_block, max_block)); + } + let sample = axis_min + axis_length * fraction + offset; + let (sample_min, sample_max) = collision_path_axis_block_bounds( + sample, + center, + MAX_EXPOSURE_CERTIFICATE_AXIS_WORK, + )?; + min_block = min_block.min(sample_min); + max_block = max_block.max(sample_max); + fraction += step; + if !fraction.is_finite() { + return None; + } + } + None + } + + /// Builds a Cartesian envelope containing every block visited by all exposure rays. + /// + /// Each axis uses Vanilla's repeated-addition sample sequence and exact scalar DDA recurrence. + /// This is linear in the three axis sample counts instead of their Cartesian product. + pub(super) fn stable_air_certificate_bounds(self, center: DVec3) -> Option { + let (min_x, max_x) = Self::axis_sample_block_bounds( + self.bounding_box.min_x(), + self.bounding_box.max_x(), + self.x_step, + self.x_offset, + center.x, + )?; + let (min_y, max_y) = Self::axis_sample_block_bounds( + self.bounding_box.min_y(), + self.bounding_box.max_y(), + self.y_step, + 0.0, + center.y, + )?; + let (min_z, max_z) = Self::axis_sample_block_bounds( + self.bounding_box.min_z(), + self.bounding_box.max_z(), + self.z_step, + self.z_offset, + center.z, + )?; + Some(BlockRegionBounds::from_corners( + BlockPos::new(min_x, min_y, min_z), + BlockPos::new(max_x, max_y, max_z), + )) + } + + fn for_each_sample(self, mut visit: impl FnMut(DVec3)) -> usize { + let mut sample_count = 0; + // Repeated addition and inclusive bounds intentionally mirror Vanilla's floating-point + // sample sequence; deriving each fraction from an integer index can change boundary rays. + let mut x_fraction = 0.0; + while x_fraction <= 1.0 { + let mut y_fraction = 0.0; + while y_fraction <= 1.0 { + let mut z_fraction = 0.0; + while z_fraction <= 1.0 { + visit(self.sample_position(x_fraction, y_fraction, z_fraction)); + sample_count += 1; + z_fraction += self.z_step; + } + y_fraction += self.y_step; + } + x_fraction += self.x_step; + } + sample_count + } + + #[cfg(test)] + #[inline] + fn sample_is_visible(self, world: &ServerWorld, center: DVec3, from: DVec3) -> bool { + world.is_block_collision_path_clear(from, center, self.collision_context) + } + + fn exposure(visible_samples: u32, sample_count: usize) -> f32 { + visible_samples as f32 / sample_count as f32 + } + + #[cfg(test)] + pub(super) fn calculate_uncached(self, world: &ServerWorld, center: DVec3) -> f32 { + if self.has_negative_step() { + return 0.0; + } + + self.calculate_with_visibility(|from| self.sample_is_visible(world, center, from)) + } + + pub(super) fn calculate_with_visibility( + self, + mut is_visible: impl FnMut(DVec3) -> bool, + ) -> f32 { + let mut visible_samples = 0; + let sample_count = self.for_each_sample(|from| { + if is_visible(from) { + visible_samples += 1; + } + }); + Self::exposure(visible_samples, sample_count) + } + + #[cfg(test)] + fn calculate_cached(self, world: &ServerWorld, center: DVec3) -> f32 { + if self.has_negative_step() { + return 0.0; + } + + let mut raycast = ExplosionExposureRaycast::new(world, self.collision_context); + self.calculate_cached_with(&mut raycast, center) + } + + pub(super) fn calculate_cached_with( + self, + raycast: &mut ExplosionExposureRaycast<'_>, + center: DVec3, + ) -> f32 { + if self.has_negative_step() { + return 0.0; + } + if let Some(bounds) = self.stable_air_certificate_bounds(center) + && raycast.stable_air_box_is_clear(bounds) + { + return 1.0; + } + raycast.set_collision_context(self.collision_context); + self.calculate_with_visibility(|from| raycast.is_path_clear(from, center)) + } +} + +fn exposure_axis_step(axis_length: f64) -> f64 { + 1.0 / (axis_length * EXPOSURE_SAMPLE_DENSITY + 1.0) +} + +#[cfg(test)] +pub(super) fn seen_percent(world: &ServerWorld, center: DVec3, entity: &dyn Entity) -> f32 { + let exposure = EntityExplosionExposure::capture(entity); + if exposure.has_negative_step() { + return 0.0; + } + exposure.calculate_cached(world, center) +} diff --git a/steel-core/src/world/explosion/server/tests.rs b/steel-core/src/world/explosion/server/tests.rs new file mode 100644 index 00000000000..e1fc79965e4 --- /dev/null +++ b/steel-core/src/world/explosion/server/tests.rs @@ -0,0 +1,671 @@ +use std::sync::{ + Arc, Weak, + atomic::{AtomicUsize, Ordering}, +}; + +use glam::DVec3; +use steel_registry::blocks::block_state_ext::BlockStateExt as _; +use steel_registry::blocks::properties::{BlockStateProperties, PistonType}; +use steel_registry::item_stack::ItemStack; +use steel_registry::{ + entity_type::EntityTypeRef, fluid::FluidState, init_vanilla_registry, vanilla_blocks, + vanilla_entities, vanilla_game_rules::MOB_GRIEFING, vanilla_items, +}; +use steel_utils::types::{GameType, UpdateFlags}; +use steel_utils::{ + BlockPos, BlockStateId, ChunkPos, Direction, Downcast as _, DowncastType, DowncastTypeKey, + WorldAabb, +}; + +use super::*; +use crate::behavior::{BlockCollisionContext, init_behaviors}; +use crate::block_entity::{ + SharedBlockEntity, entities::PistonMovingBlockEntity, init_block_entities, +}; +use crate::entity::entities::{ + ChestMinecartEntity, ItemEntity, ItemFrameEntity, LeashFenceKnotEntity, PigEntity, + PrimedTntEntity, +}; +use crate::entity::{EntityBase, EntityFluidContact, next_entity_id}; +use crate::player::ResetReason; +use crate::test_support::{ + TestPlayerBuilder, TestWorld, fresh_test_world, insert_ready_full_chunk, +}; +use crate::world::raycast::ExplosionExposureRaycast; +use crate::world::{ExplosionInteraction, ExplosionOptions, World}; + +const TEST_BLOCK_BOTTOM_CENTER: DVec3 = DVec3::new(0.5, 64.0, 0.5); +const TEST_BLOCK_CENTER: DVec3 = DVec3::new(0.5, 64.5, 0.5); +const TEST_LOW_EXPLOSION_CENTER: DVec3 = DVec3::new(0.5, 64.125, 0.5); +const TEST_WALL_POS: BlockPos = BlockPos::new(1, 64, 0); +const NEAR_EXPOSURE_TARGET_DISTANCE: f64 = 2.0; +const FAR_EXPOSURE_TARGET_DISTANCE: f64 = 6.0; +const ENTITY_EFFECT_TEST_RADIUS: f32 = 2.0; + +struct VetoExplosionSource { + base: EntityBase, + resistance_calls: AtomicUsize, + decision_calls: AtomicUsize, +} + +// SAFETY: This test-only key uniquely identifies `VetoExplosionSource` in Steel's test build. +unsafe impl DowncastType for VetoExplosionSource { + const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:test/explosion_veto_source"); +} + +impl Entity for VetoExplosionSource { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + &vanilla_entities::ITEM + } + + fn block_explosion_resistance( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + _state: BlockStateId, + _fluid: FluidState, + resistance: f32, + ) -> f32 { + self.resistance_calls.fetch_add(1, Ordering::Relaxed); + resistance + } + + fn should_block_explode( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + self.decision_calls.fetch_add(1, Ordering::Relaxed); + false + } +} + +#[derive(Default)] +struct VetoCustomCalculator { + resistance_calls: AtomicUsize, + decision_calls: AtomicUsize, +} + +impl ExplosionDamageCalculator for VetoCustomCalculator { + fn block_explosion_resistance( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + _state: BlockStateId, + _fluid: FluidState, + ) -> Option { + self.resistance_calls.fetch_add(1, Ordering::Relaxed); + None + } + + fn should_block_explode( + &self, + _explosion: &dyn Explosion, + _world: &World, + _pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + self.decision_calls.fetch_add(1, Ordering::Relaxed); + false + } +} + +struct BlockMutatingExposureEntity { + base: EntityBase, + place_wall_on_hit: bool, +} + +// SAFETY: This test-only key uniquely identifies `BlockMutatingExposureEntity`. +unsafe impl DowncastType for BlockMutatingExposureEntity { + const TYPE_KEY: DowncastTypeKey = + DowncastTypeKey::new("steel:test/block_mutating_exposure_entity"); +} + +impl Entity for BlockMutatingExposureEntity { + fn base(&self) -> &EntityBase { + &self.base + } + + fn entity_type(&self) -> EntityTypeRef { + &vanilla_entities::ITEM + } + + fn on_explosion_hit(&self, _explosion_source: Option<&dyn Entity>) { + if self.place_wall_on_hit + && let Some(world) = self.level() + { + world.set_block( + TEST_WALL_POS, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + ); + } + } +} + +fn ready_world(key: &'static str) -> TestWorld { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world(key); + insert_ready_full_chunk(&fixture.world, ChunkPos::new(0, 0)); + fixture +} + +fn item_entity(world: &Arc, position: DVec3) -> ItemEntity { + ItemEntity::new( + &vanilla_entities::ITEM, + next_entity_id(), + position, + Arc::downgrade(world), + ) +} + +fn add_entity(world: &Arc, entity: E) -> Arc { + let entity = Arc::new(entity); + let Ok(()) = world.try_add_entity(Arc::::clone(&entity)) else { + panic!("test entity must be added to its loaded chunk"); + }; + entity +} + +fn add_block_attached_targets( + world: &Arc, +) -> (Arc, Arc) { + let item_frame = add_entity( + world, + ItemFrameEntity::new_attached( + &vanilla_entities::ITEM_FRAME, + next_entity_id(), + TEST_WALL_POS, + Direction::West, + Arc::downgrade(world), + ), + ); + let leash_knot = add_entity( + world, + LeashFenceKnotEntity::new_attached( + &vanilla_entities::LEASH_KNOT, + next_entity_id(), + BlockPos::new(0, 64, 1), + Arc::downgrade(world), + ), + ); + (item_frame, leash_knot) +} + +fn assert_exposure_matches_seen_percent(world: &World, center: DVec3, entity: &dyn Entity) { + let exposure = EntityExplosionExposure::capture(entity); + assert_eq!( + seen_percent(world, center, entity).to_bits(), + exposure.calculate_uncached(world, center).to_bits() + ); +} + +#[test] +fn player_knockback_map_excludes_creative_flying_and_spectator_players() { + let fixture = ready_world("explosion_player_knockback_rules"); + let world = &fixture.world; + let player = |name, offset, game_mode| { + let player = TestPlayerBuilder::new(Arc::clone(world), name, next_entity_id()).build(); + player + .base() + .set_position_local(TEST_BLOCK_BOTTOM_CENTER + offset); + player.restore_game_modes(game_mode, None); + player.abilities.lock().update_for_game_mode(game_mode); + player.set_client_loaded(true); + assert!(world.add_player(Arc::clone(&player), ResetReason::InitialJoin)); + player + }; + let survival = player("Survival", DVec3::X, GameType::Survival); + let creative = player("Creative", DVec3::Z, GameType::Creative); + creative.abilities.lock().flying = true; + let spectator = player("Spectator", -DVec3::X, GameType::Spectator); + let mut explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + TEST_BLOCK_BOTTOM_CENTER, + ENTITY_EFFECT_TEST_RADIUS, + false, + BlockInteraction::Keep, + ); + + explosion.hurt_entities(); + + let delta = survival.explosion_damage_origin() - explosion.center; + let normalized_distance = survival.position().distance(explosion.center) + / (f64::from(ENTITY_EFFECT_TEST_RADIUS) * 2.0); + let expected_knockback = delta / delta.length() * (1.0 - normalized_distance); + assert_eq!(survival.velocity(), expected_knockback); + assert_eq!( + explosion.hit_players.get(&survival.id()), + Some(&expected_knockback) + ); + // Creative flyers are still pushed, but Vanilla omits their client knockback. + assert_ne!(creative.velocity(), DVec3::ZERO); + assert!(!explosion.hit_players.contains_key(&creative.id())); + assert_eq!(spectator.velocity(), DVec3::ZERO); + assert!(!explosion.hit_players.contains_key(&spectator.id())); +} + +#[test] +fn source_and_custom_calculator_hooks_run_on_the_sequential_lane() { + let fixture = ready_world("explosion_calculator_hooks"); + let world = &fixture.world; + let center = TEST_BLOCK_CENTER; + assert!(world.set_block( + BlockPos::from(center), + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let source = Arc::new(VetoExplosionSource { + base: EntityBase::new( + next_entity_id(), + center, + vanilla_entities::ITEM.dimensions, + Arc::downgrade(world), + ), + resistance_calls: AtomicUsize::new(0), + decision_calls: AtomicUsize::new(0), + }); + let source_explosion = ServerExplosion::new( + world, + Some(Arc::clone(&source) as SharedEntity), + None, + None, + None, + center, + 2.0, + false, + BlockInteraction::Destroy, + ); + + assert_eq!(source_explosion.calculate_exploded_positions(|| 0.5), []); + assert!(source.resistance_calls.load(Ordering::Relaxed) > 0); + assert!(source.decision_calls.load(Ordering::Relaxed) > 0); + + let custom = VetoCustomCalculator::default(); + let custom_explosion = ServerExplosion::new( + world, + None, + None, + Some(&custom), + None, + center, + 2.0, + false, + BlockInteraction::Destroy, + ); + + assert_eq!(custom_explosion.calculate_exploded_positions(|| 0.5), []); + assert!(custom.resistance_calls.load(Ordering::Relaxed) > RAY_COUNT); + assert!(custom.decision_calls.load(Ordering::Relaxed) > RAY_COUNT); +} + +#[test] +fn cached_exposure_raycast_matches_clear_partial_and_blocked_paths() { + let fixture = ready_world("cached_explosion_exposure"); + let world = &fixture.world; + let center = TEST_BLOCK_CENTER; + let player = TestPlayerBuilder::new(Arc::clone(world), "Cached", next_entity_id()).build(); + player + .base() + .set_position_local(TEST_BLOCK_BOTTOM_CENTER + DVec3::X * NEAR_EXPOSURE_TARGET_DISTANCE); + + let compare = || { + let exposure = EntityExplosionExposure::capture(player.as_ref()); + let uncached = exposure.calculate_uncached(world, center); + let mut raycast = ExplosionExposureRaycast::new(world, exposure.collision_context); + raycast.configure_clear_grid(BlockPos::new(0, 63, 0), BlockPos::new(4, 67, 1)); + let cached = exposure.calculate_with_visibility(|from| raycast.is_path_clear(from, center)); + assert_eq!(cached.to_bits(), uncached.to_bits()); + cached + }; + + assert_eq!(compare().to_bits(), 1.0_f32.to_bits()); + assert!(world.set_block( + TEST_WALL_POS, + vanilla_blocks::STONE_SLAB.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let partial = compare(); + assert!(partial > 0.0 && partial < 1.0, "partial exposure={partial}"); + assert!(world.set_block( + TEST_WALL_POS, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert_eq!(compare().to_bits(), 0.0_f32.to_bits()); +} + +#[test] +fn stable_air_certificate_skips_rays_and_observes_later_block_writes() { + let fixture = ready_world("stable_air_explosion_exposure"); + let world = &fixture.world; + let center = TEST_BLOCK_CENTER; + let entity = item_entity( + world, + TEST_BLOCK_BOTTOM_CENTER + DVec3::X * FAR_EXPOSURE_TARGET_DISTANCE, + ); + let exposure = EntityExplosionExposure::capture(&entity); + let mut raycast = ExplosionExposureRaycast::new(world, exposure.collision_context); + + assert_eq!( + exposure + .calculate_cached_with(&mut raycast, center) + .to_bits(), + 1.0_f32.to_bits() + ); + let after_air = raycast.stats(); + assert_eq!(after_air.stable_air_hits, 1); + assert_eq!(after_air.block_visits, 0); + + assert!(world.set_block( + TEST_WALL_POS, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let expected = exposure.calculate_uncached(world, center); + assert_eq!( + exposure + .calculate_cached_with(&mut raycast, center) + .to_bits(), + expected.to_bits() + ); + assert_eq!(raycast.stats().stable_air_hits, 1); +} + +#[test] +fn resting_tnt_certificate_includes_vanilla_adjusted_source_block() { + init_vanilla_registry(); + let explosion_position = DVec3::new(0.5, 200.0, 0.5); + let target = PrimedTntEntity::new( + &vanilla_entities::TNT, + next_entity_id(), + explosion_position + DVec3::X * FAR_EXPOSURE_TARGET_DISTANCE, + Weak::new(), + ); + let center = explosion_position + + DVec3::Y * (f64::from(vanilla_entities::TNT.dimensions.height) * 0.0625); + let exposure = EntityExplosionExposure::capture(&target); + let Some(bounds) = exposure.stable_air_certificate_bounds(center) else { + panic!("resting TNT exposure must have finite certificate bounds"); + }; + let (min, max) = bounds.corners(); + + // Vanilla extrapolates the bottom sample by -1e-7 before flooring it. Tightening this to the + // raw entity hull at y=200 would miss tall collision shapes sourced from the block below. + assert_eq!(min.y(), 199); + assert_eq!(max.y(), 200); +} + +#[test] +fn cached_exposure_matches_across_chunk_and_section_boundaries() { + let fixture = ready_world("cached_exposure_boundaries"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(1, 0)); + assert!(world.set_block( + BlockPos::new(15, 79, 0), + vanilla_blocks::STONE_SLAB.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let entity = item_entity(world, DVec3::new(16.5, 80.0, 0.5)); + + assert_exposure_matches_seen_percent(world, DVec3::new(14.5, 78.5, 0.5), &entity); +} + +#[test] +fn exposure_cache_does_not_retain_air_from_a_missing_chunk() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("explosion_exposure_missing_chunk"); + let world = &fixture.world; + let from = TEST_BLOCK_CENTER + DVec3::X * NEAR_EXPOSURE_TARGET_DISTANCE; + let to = TEST_BLOCK_CENTER; + let mut raycast = ExplosionExposureRaycast::new(world, BlockCollisionContext::empty()); + raycast.configure_clear_grid(BlockPos::new(0, 64, 0), BlockPos::new(2, 64, 0)); + + assert!(raycast.is_path_clear(from, to)); + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + assert!(world.set_block( + TEST_WALL_POS, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + assert!(!raycast.is_path_clear(from, to)); +} + +#[test] +fn exposure_clipping_uses_the_entity_collision_context() { + let fixture = ready_world("explosion_entity_collision_context"); + let world = &fixture.world; + assert!(world.set_block( + TEST_WALL_POS, + vanilla_blocks::POWDER_SNOW.default_state(), + UpdateFlags::UPDATE_ALL, + )); + let entity = item_entity( + world, + TEST_BLOCK_BOTTOM_CENTER + DVec3::X * NEAR_EXPOSURE_TARGET_DISTANCE, + ); + let center = TEST_LOW_EXPLOSION_CENTER; + let walking_exposure = EntityExplosionExposure::capture(&entity); + let mut shared_raycast = + ExplosionExposureRaycast::new(world, walking_exposure.collision_context); + shared_raycast.configure_clear_grid(BlockPos::new(0, 63, 0), BlockPos::new(3, 66, 1)); + + let walking = walking_exposure.calculate_cached_with(&mut shared_raycast, center); + assert_eq!(walking.to_bits(), 1.0_f32.to_bits()); + + // Powder snow only collides with entities that are not descending. + entity.set_fall_distance(3.0); + entity.set_shared_shift_key_down(true); + let falling_exposure = EntityExplosionExposure::capture(&entity); + assert!(falling_exposure.collision_context.is_descending()); + let falling = falling_exposure.calculate_cached_with(&mut shared_raycast, center); + assert_eq!(falling.to_bits(), 0.0_f32.to_bits()); + assert_eq!( + falling.to_bits(), + falling_exposure.calculate_uncached(world, center).to_bits() + ); +} + +#[test] +fn exposure_cache_is_cleared_before_block_mutating_entity_callbacks() { + let fixture = ready_world("explosion_exposure_callback_mutation"); + let world = &fixture.world; + let position = TEST_BLOCK_BOTTOM_CENTER + DVec3::X * NEAR_EXPOSURE_TARGET_DISTANCE; + let entity = |place_wall_on_hit| BlockMutatingExposureEntity { + base: EntityBase::new( + next_entity_id(), + position, + vanilla_entities::ITEM.dimensions, + Arc::downgrade(world), + ), + place_wall_on_hit, + }; + let mutator = add_entity(world, entity(true)); + let observer = add_entity(world, entity(false)); + + world.explode(ExplosionOptions::new( + TEST_LOW_EXPLOSION_CENTER, + ENTITY_EFFECT_TEST_RADIUS, + ExplosionInteraction::None, + )); + + assert_eq!( + world.get_block_state(TEST_WALL_POS), + vanilla_blocks::STONE.default_state() + ); + assert!(mutator.velocity().x > 0.0); + assert_eq!(observer.velocity(), DVec3::ZERO); +} + +#[test] +fn moving_piston_exposure_uses_live_block_entities() { + let fixture = ready_world("moving_piston_explosion_exposure"); + let world = &fixture.world; + init_block_entities(); + let moving_state = vanilla_blocks::MOVING_PISTON + .default_state() + .set_value(&BlockStateProperties::FACING, Direction::East) + .set_value(&BlockStateProperties::PISTON_TYPE, PistonType::Normal); + let moved_state = vanilla_blocks::PISTON + .default_state() + .set_value(&BlockStateProperties::FACING, Direction::East) + .set_value(&BlockStateProperties::EXTENDED, true); + assert!(world.set_block(TEST_WALL_POS, moving_state, UpdateFlags::UPDATE_NONE)); + let block_entity: SharedBlockEntity = Arc::new(PistonMovingBlockEntity::new_moving( + Arc::downgrade(world), + TEST_WALL_POS, + moving_state, + moved_state, + Direction::East, + false, + true, + )); + assert!(world.set_block_entity(block_entity)); + let center = TEST_LOW_EXPLOSION_CENTER; + let entity = item_entity( + world, + TEST_BLOCK_BOTTOM_CENTER + DVec3::X * NEAR_EXPOSURE_TARGET_DISTANCE, + ); + let exposure = EntityExplosionExposure::capture(&entity); + let live = exposure.calculate_uncached(world, center); + assert!(live < 1.0, "the moving piston should occlude a sample"); + let mut raycast = ExplosionExposureRaycast::new(world, exposure.collision_context); + raycast.configure_clear_grid(BlockPos::new(0, 63, 0), BlockPos::new(3, 66, 1)); + + assert_eq!( + exposure + .calculate_cached_with(&mut raycast, center) + .to_bits(), + live.to_bits() + ); +} + +#[test] +fn non_destructive_explosion_ignores_blocklike_entities_without_mob_griefing() { + let fixture = ready_world("explosion_ignores_blocklike_entities"); + let world = &fixture.world; + assert!(world.set_game_rule(&MOB_GRIEFING, false)); + let item = item_entity(world, TEST_BLOCK_BOTTOM_CENTER + DVec3::X); + item.set_item(ItemStack::new(&vanilla_items::STONE)); + let item = add_entity(world, item); + let (item_frame, leash_knot) = add_block_attached_targets(world); + + world.explode(ExplosionOptions::new( + TEST_BLOCK_BOTTOM_CENTER, + ENTITY_EFFECT_TEST_RADIUS, + ExplosionInteraction::None, + )); + + assert!(!item.is_removed()); + assert_eq!(item.velocity(), DVec3::ZERO); + assert_eq!(item_frame.velocity(), DVec3::ZERO); + assert_eq!(leash_knot.velocity(), DVec3::ZERO); +} + +#[test] +fn mob_explosion_does_not_push_vehicles_when_mob_griefing_is_disabled() { + let fixture = ready_world("explosion_ignores_vehicles_without_mob_griefing"); + let world = &fixture.world; + assert!(world.set_game_rule(&MOB_GRIEFING, false)); + let minecart = add_entity( + world, + ChestMinecartEntity::new( + &vanilla_entities::CHEST_MINECART, + next_entity_id(), + TEST_BLOCK_BOTTOM_CENTER + DVec3::X, + Arc::downgrade(world), + ), + ); + let pig: SharedEntity = Arc::new(PigEntity::new( + &vanilla_entities::PIG, + next_entity_id(), + TEST_BLOCK_BOTTOM_CENTER, + Arc::downgrade(world), + )); + let mut options = ExplosionOptions::new( + TEST_BLOCK_BOTTOM_CENTER, + ENTITY_EFFECT_TEST_RADIUS, + ExplosionInteraction::Mob, + ); + options.source = Some(pig); + + world.explode(options); + + assert_eq!(minecart.velocity(), DVec3::ZERO); +} + +#[test] +fn submerged_source_explosion_does_not_push_block_attached_entities() { + let fixture = ready_world("submerged_explosion_ignores_block_attached_entities"); + let world = &fixture.world; + let source = item_entity(world, TEST_BLOCK_BOTTOM_CENTER); + source + .base() + .set_fluid_contact(EntityFluidContact::from_parts(1.0, 0.0, false, false)); + assert!(source.is_in_water()); + let (item_frame, leash_knot) = add_block_attached_targets(world); + let mut options = ExplosionOptions::new( + TEST_BLOCK_BOTTOM_CENTER, + ENTITY_EFFECT_TEST_RADIUS, + ExplosionInteraction::Block, + ); + options.source = Some(Arc::new(source) as SharedEntity); + + world.explode(options); + + assert_eq!(item_frame.velocity(), DVec3::ZERO); + assert_eq!(leash_knot.velocity(), DVec3::ZERO); +} + +#[test] +fn destructive_explosion_removes_stone_and_spawns_its_loot() { + let fixture = ready_world("explosion_block_destruction"); + let world = &fixture.world; + let center_pos = BlockPos::new(0, 64, 0); + assert!(world.set_block( + center_pos, + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_ALL, + )); + let mut explosion = ServerExplosion::new( + world, + None, + None, + None, + None, + TEST_BLOCK_CENTER, + 4.0, + false, + BlockInteraction::Destroy, + ); + + explosion.explode(); + + assert!(world.get_block_state(center_pos).is_air()); + let drops = world.get_entities_in_aabb_matching( + &WorldAabb::new(-1.0, 63.0, -1.0, 2.0, 67.0, 2.0), + |entity| entity.entity_type() == &vanilla_entities::ITEM, + ); + assert!(drops.iter().any(|entity| { + entity + .as_ref() + .downcast_ref::() + .is_some_and(|item| item.get_item().is(&vanilla_items::COBBLESTONE)) + })); +} diff --git a/steel-core/src/world/level_effects.rs b/steel-core/src/world/level_effects.rs index d9efdb947dd..a14de01dd03 100644 --- a/steel-core/src/world/level_effects.rs +++ b/steel-core/src/world/level_effects.rs @@ -431,6 +431,9 @@ impl World { if let Some(entity) = context.entity() { ctx = ctx.with_this_entity(entity_loot_ref(entity)); } + if let Some(radius) = context.explosion_radius() { + ctx = ctx.with_explosion(radius); + } loot_table.get_random_items(&mut ctx) } diff --git a/steel-core/src/world/mod.rs b/steel-core/src/world/mod.rs index 3029f0bb052..e3a17e79dc0 100644 --- a/steel-core/src/world/mod.rs +++ b/steel-core/src/world/mod.rs @@ -116,6 +116,7 @@ mod domain_entity_directory; mod entity_management; mod environment; mod events; +pub mod explosion; /// Vanilla game-event contexts, listeners, and dispatch storage. pub mod game_event; mod level_effects; @@ -141,7 +142,7 @@ pub use crate::config::WorldStorageConfig; use crate::worldgen::generators::vanilla::fuzzed_biome_at_block; use crate::worldgen::{ChunkGenerator, ChunkGeneratorType}; use block_event::BlockEventQueue; -pub(crate) use block_region::{BlockRegionBounds, MAX_BLOCK_REGION_WORKSET_SLOTS}; +pub(crate) use block_region::{BlockRegionBounds, BlockRegionRead, MAX_BLOCK_REGION_WORKSET_SLOTS}; use block_updates::CollectingNeighborUpdater; pub use border::WorldBorderError; pub(crate) use border::{MAX_CENTER_COORDINATE, MAX_SIZE}; @@ -150,6 +151,11 @@ pub(crate) use domain_entity_directory::DomainEntityDirectory; use entity_management::NavigatingMobTracker; #[cfg(test)] use entity_management::nearest_player_distance_in_range; +pub use explosion::{ + BlockInteraction, DefaultExplosionDamageCalculator, EntityBasedExplosionDamageCalculator, + Explosion, ExplosionDamageCalculator, ExplosionInteraction, ExplosionOptions, ExplosionOutcome, +}; +pub(crate) use explosion::{ExplosionBlockReader, ImmutableExplosionBlockCalculator}; pub use level_reader::{LevelAccessor, LevelReader, ScheduledTickAccess}; pub use player_index::{PlayerAreaMap, PlayerMap}; pub use raycast::{ClipBlockShape, ClipFluid, ClipHitResult, RaytraceAction}; @@ -259,6 +265,8 @@ pub struct World { pub(crate) game_time: Arc, /// Per-world saved data storage. pub(crate) saved_data: SavedDataManager, + /// Vanilla's live per-level random source. + random: SyncMutex, /// Runtime world border state. world_border: SyncMutex, /// Vanilla sleeping player counts for night-skip checks. @@ -314,6 +322,16 @@ pub struct World { } impl World { + /// Runs an operation against Vanilla's live per-level random source. + pub(crate) fn with_random(&self, operation: impl FnOnce(&mut RandomSource) -> T) -> T { + operation(&mut self.random.lock()) + } + + #[cfg(test)] + pub(crate) fn set_random_seed_for_test(&self, seed: i64) { + *self.random.lock() = RandomSource::Legacy(LegacyRandom::from_seed(seed as u64)); + } + /// Creates a new world with custom configuration. /// /// This allows specifying storage backend (disk or RAM-only) and other options. @@ -434,6 +452,9 @@ impl World { game_time: level_data.game_time_handle(), level_data: SyncRwLock::new(level_data), saved_data, + random: SyncMutex::new(RandomSource::Legacy(LegacyRandom::from_seed( + rand::random::(), + ))), world_border: SyncMutex::new(world_border), sleep_status: SyncMutex::new(sleep_status::SleepStatus::default()), view_distance, diff --git a/steel-core/src/world/raycast.rs b/steel-core/src/world/raycast.rs index 131c9fc28aa..49ec6655c95 100644 --- a/steel-core/src/world/raycast.rs +++ b/steel-core/src/world/raycast.rs @@ -1,4 +1,7 @@ use super::*; +use crate::chunk::gameplay_chunk_lookup_cache::LocalFullChunkHolderCache; +use crate::physics::block_has_extensible_collision_behavior; +use steel_registry::blocks::BlockRef; /// Controls how a block position is treated during a raytrace traversal. /// @@ -59,6 +62,604 @@ pub struct ClipHitResult { pub world_border_hit: bool, } +const VANILLA_RAY_ENDPOINT_ADJUSTMENT: f64 = -1.0e-7; +const AABB_CLIP_EPSILON: f64 = 1.0e-7; + +// Radius-four TNT exposure repeatedly visits a few hundred nearby positions. A 256-entry direct +// cache retains almost all of that working set without heap allocation or cross-explosion lifetime. +// Its size must remain a power of two because lookups wrap with a mask. +const EXPLOSION_EXPOSURE_CACHE_SIZE: usize = 256; +const EXPLOSION_EXPOSURE_CACHE_MASK: usize = EXPLOSION_EXPOSURE_CACHE_SIZE - 1; +const EXPLOSION_EXPOSURE_FIBONACCI_HASH_MULTIPLIER: u64 = 0x9e37_79b9_7f4a_7c15; +const EMPTY_EXPOSURE_CACHE_GENERATION: u32 = 0; +const FIRST_EXPOSURE_CACHE_GENERATION: u32 = 1; + +// A normal radius-four explosion queries at most 19 positions on each axis. Two bits per position +// fit inline, avoiding one heap allocation while unusual radii use the compatibility path. +const EXPLOSION_EXPOSURE_CLEAR_GRID_AXIS_SIZE: usize = 19; +const EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_BITS: usize = 2; +const EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD: usize = + u64::BITS as usize / EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_BITS; +const EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_MASK: u64 = + (1 << EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_BITS) - 1; +const EXPLOSION_EXPOSURE_CLEAR_GRID_POSITIONS: usize = EXPLOSION_EXPOSURE_CLEAR_GRID_AXIS_SIZE + * EXPLOSION_EXPOSURE_CLEAR_GRID_AXIS_SIZE + * EXPLOSION_EXPOSURE_CLEAR_GRID_AXIS_SIZE; +const EXPLOSION_EXPOSURE_CLEAR_GRID_WORDS: usize = + EXPLOSION_EXPOSURE_CLEAR_GRID_POSITIONS.div_ceil(EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +enum ExplosionExposureClearGridState { + Unresolved = 0, + StaticEmpty = 1, + RequiresExactCollision = 2, +} + +const UNRESOLVED_EXPOSURE_GRID_WORD: u64 = ExplosionExposureClearGridState::Unresolved as u64; + +impl ExplosionExposureClearGridState { + const fn for_collision(is_empty: bool) -> Self { + if is_empty { + Self::StaticEmpty + } else { + Self::RequiresExactCollision + } + } + + const fn from_encoded(encoded: u64) -> Self { + if encoded == Self::Unresolved as u64 { + Self::Unresolved + } else if encoded == Self::StaticEmpty as u64 { + Self::StaticEmpty + } else { + // The unused fourth encoding conservatively takes the exact path too. + Self::RequiresExactCollision + } + } +} + +#[derive(Clone, Copy)] +struct ExplosionExposureClearGridBounds { + min: BlockPos, + size_x: usize, + size_y: usize, + size_z: usize, +} + +struct ExplosionExposureClearGrid { + bounds: Option, + // Each lookup reads one packed word instead of two bitsets. + states: [u64; EXPLOSION_EXPOSURE_CLEAR_GRID_WORDS], +} + +impl ExplosionExposureClearGrid { + const fn new() -> Self { + Self { + bounds: None, + states: [UNRESOLVED_EXPOSURE_GRID_WORD; EXPLOSION_EXPOSURE_CLEAR_GRID_WORDS], + } + } + + fn configure(&mut self, min: BlockPos, max: BlockPos) { + let Some(size_x) = inclusive_axis_size(min.x(), max.x()) else { + self.bounds = None; + return; + }; + let Some(size_y) = inclusive_axis_size(min.y(), max.y()) else { + self.bounds = None; + return; + }; + let Some(size_z) = inclusive_axis_size(min.z(), max.z()) else { + self.bounds = None; + return; + }; + let Some(volume) = size_x + .checked_mul(size_y) + .and_then(|plane| plane.checked_mul(size_z)) + else { + self.bounds = None; + return; + }; + if volume > EXPLOSION_EXPOSURE_CLEAR_GRID_POSITIONS { + self.bounds = None; + return; + } + + self.bounds = Some(ExplosionExposureClearGridBounds { + min, + size_x, + size_y, + size_z, + }); + self.states.fill(UNRESOLVED_EXPOSURE_GRID_WORD); + } + + fn clear(&mut self) { + self.states.fill(UNRESOLVED_EXPOSURE_GRID_WORD); + } + + fn index(&self, pos: BlockPos) -> Option { + let bounds = self.bounds?; + // Wrapping subtraction maps every below-min coordinate to a large unsigned value. Since + // the configured grid is tiny, the same range checks safely reject both underflow and + // coordinates above the maximum without three checked-subtraction branches. + let x = pos.x().wrapping_sub(bounds.min.x()) as u32 as usize; + let y = pos.y().wrapping_sub(bounds.min.y()) as u32 as usize; + let z = pos.z().wrapping_sub(bounds.min.z()) as u32 as usize; + if x >= bounds.size_x || y >= bounds.size_y || z >= bounds.size_z { + return None; + } + Some((y * bounds.size_z + z) * bounds.size_x + x) + } + + const fn state(&self, index: usize) -> ExplosionExposureClearGridState { + let word = index / EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD; + let shift = (index % EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD) + * EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_BITS; + let encoded = (self.states[word] >> shift) & EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_MASK; + ExplosionExposureClearGridState::from_encoded(encoded) + } + + fn record(&mut self, index: usize, state: ExplosionExposureClearGridState) { + debug_assert_ne!(state, ExplosionExposureClearGridState::Unresolved); + let word = index / EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD; + let shift = (index % EXPLOSION_EXPOSURE_CLEAR_GRID_STATES_PER_WORD) + * EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_BITS; + let mask = EXPLOSION_EXPOSURE_CLEAR_GRID_STATE_MASK << shift; + let value = (state as u64) << shift; + self.states[word] = (self.states[word] & !mask) | value; + } +} + +fn inclusive_axis_size(min: i32, max: i32) -> Option { + let size = usize::try_from(max.checked_sub(min)?.checked_add(1)?).ok()?; + (size != 0).then_some(size) +} + +#[derive(Clone, Copy)] +struct ExplosionExposureCacheEntry { + pos: BlockPos, + collision: OffsetVoxelShape, + generation: u32, +} + +impl ExplosionExposureCacheEntry { + const EMPTY: Self = Self { + pos: BlockPos::new(0, 0, 0), + collision: OffsetVoxelShape::without_offset(VoxelShape::EMPTY), + generation: EMPTY_EXPOSURE_CACHE_GENERATION, + }; +} + +#[derive(Clone, Copy)] +struct ExplosionExposureBlockRead { + state: BlockStateId, + cacheable: bool, +} + +/// Bounded cache for vanilla explosion exposure rays. +/// +/// Missing chunks are never cached, allowing asynchronous Full publication to become visible +/// while the synchronous gameplay callback is running. Dynamic and non-Vanilla collision shapes +/// are resolved on every visit so live block entities and plugin callbacks retain their normal +/// query semantics. Reuse across entities is only valid when the caller can prove that the +/// intervening entity callbacks cannot mutate blocks; callers must clear the cache before invoking +/// any other entity implementation. +pub(crate) struct ExplosionExposureRaycast<'world> { + world: &'world World, + collision_context: BlockCollisionContext, + full_chunks: LocalFullChunkHolderCache, + entries: [ExplosionExposureCacheEntry; EXPLOSION_EXPOSURE_CACHE_SIZE], + clear_grid: ExplosionExposureClearGrid, + generation: u32, + #[cfg(test)] + stats: ExplosionExposureRaycastStats, +} + +#[cfg(test)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub(crate) struct ExplosionExposureRaycastStats { + pub(crate) block_visits: usize, + pub(crate) cache_hits: usize, + pub(crate) state_lookups: usize, + pub(crate) collision_lookups: usize, + pub(crate) clear_grid_hits: usize, + pub(crate) clear_grid_resolutions: usize, + pub(crate) stable_air_queries: usize, + pub(crate) stable_air_hits: usize, +} + +impl<'world> ExplosionExposureRaycast<'world> { + pub(crate) const fn new( + world: &'world World, + collision_context: BlockCollisionContext, + ) -> Self { + Self { + world, + collision_context, + full_chunks: LocalFullChunkHolderCache::new(), + entries: [ExplosionExposureCacheEntry::EMPTY; EXPLOSION_EXPOSURE_CACHE_SIZE], + clear_grid: ExplosionExposureClearGrid::new(), + generation: FIRST_EXPOSURE_CACHE_GENERATION, + #[cfg(test)] + stats: ExplosionExposureRaycastStats { + block_visits: 0, + cache_hits: 0, + state_lookups: 0, + collision_lookups: 0, + clear_grid_hits: 0, + clear_grid_resolutions: 0, + stable_air_queries: 0, + stable_air_hits: 0, + }, + } + } + + /// Configures a bounded, explosion-local cache of statically clear positions. + /// + /// Bounds that exceed the inline capacity disable the optimization and retain the exact + /// compatibility path. + pub(crate) fn configure_clear_grid(&mut self, min: BlockPos, max: BlockPos) { + self.clear_grid.configure(min, max); + } + + /// Drops every retained static collision result. + pub(crate) fn clear(&mut self) { + self.clear_grid.clear(); + if self.generation == u32::MAX { + self.entries.fill(ExplosionExposureCacheEntry::EMPTY); + self.generation = FIRST_EXPOSURE_CACHE_GENERATION; + } else { + self.generation += 1; + } + } + + /// Selects the entity collision context used by subsequent exposure rays. + pub(crate) const fn set_collision_context(&mut self, collision_context: BlockCollisionContext) { + self.collision_context = collision_context; + } + + /// Proves that an inclusive block box contains only immutable Vanilla air. + pub(crate) fn stable_air_box_is_clear(&mut self, bounds: BlockRegionBounds) -> bool { + #[cfg(test)] + { + self.stats.stable_air_queries += 1; + } + #[expect( + clippy::redundant_closure_for_method_calls, + reason = "the method item cannot satisfy the region callback's higher-ranked lifetime" + )] + let Some(clear) = self + .world + .try_with_block_region(bounds, |region| region.contains_only_stable_air()) + else { + return false; + }; + #[cfg(test)] + if clear { + self.stats.stable_air_hits += 1; + } + clear + } + + /// Keeps cached shapes only across Steel-owned Vanilla collision behavior calls. + /// + /// Plugin behavior may mutate another block while answering a shape query. Clearing after its + /// callback makes the next Vanilla DDA visit read the same live state Vanilla would observe. + fn retain_cache_after_collision_query(&mut self, block: BlockRef) -> bool { + let retain = !block_has_extensible_collision_behavior(block); + if !retain { + self.clear(); + } + retain + } + + /// Returns whether a collider-only, fluid-free exposure ray misses every block. + pub(crate) fn is_path_clear(&mut self, from: DVec3, to: DVec3) -> bool { + is_collision_path_clear(from, to, |pos| self.block_intersects_ray(pos, from, to)) + } + + fn block_intersects_ray(&mut self, pos: BlockPos, from: DVec3, to: DVec3) -> bool { + #[cfg(test)] + { + self.stats.block_visits += 1; + } + + let mut unresolved_grid_index = None; + if let Some(index) = self.clear_grid.index(pos) { + match self.clear_grid.state(index) { + ExplosionExposureClearGridState::StaticEmpty => { + #[cfg(test)] + { + self.stats.clear_grid_hits += 1; + } + return false; + } + ExplosionExposureClearGridState::RequiresExactCollision => {} + ExplosionExposureClearGridState::Unresolved => { + unresolved_grid_index = Some(index); + } + } + } + + self.block_intersects_ray_exact(pos, from, to, unresolved_grid_index) + } + + fn block_intersects_ray_exact( + &mut self, + pos: BlockPos, + from: DVec3, + to: DVec3, + unresolved_grid_index: Option, + ) -> bool { + let cache_index = explosion_exposure_cache_index(pos); + let entry = self.entries[cache_index]; + if entry.generation == self.generation && entry.pos == pos { + #[cfg(test)] + { + self.stats.cache_hits += 1; + } + if let Some(index) = unresolved_grid_index { + let grid_state = + ExplosionExposureClearGridState::for_collision(entry.collision.is_empty()); + self.clear_grid.record(index, grid_state); + #[cfg(test)] + { + self.stats.clear_grid_resolutions += 1; + } + } + return Self::static_collision_intersects(entry.collision, pos, from, to); + } + + #[cfg(test)] + { + self.stats.state_lookups += 1; + self.stats.collision_lookups += 1; + } + let block_read = self + .world + .explosion_exposure_block_state(pos, &mut self.full_chunks); + let state = block_read.state; + let block = state.get_block(); + let behavior = BLOCK_BEHAVIORS.get_behavior(block); + if block.config.dynamic_shape { + let boxes = + behavior.get_collision_boxes(state, self.world, pos, self.collision_context); + self.retain_cache_after_collision_query(block); + return boxes + .into_iter() + .any(|aabb| World::clip_local_aabb(pos, from, to, aabb).is_some()); + } + + let shape = behavior.get_collision_shape(state, self.world, pos, self.collision_context); + let offset = if shape.is_empty() { + DVec3::ZERO + } else { + behavior.get_collision_shape_offset(state, self.world, pos, self.collision_context) + }; + let collision = OffsetVoxelShape::new(shape, offset); + let retain_cache = self.retain_cache_after_collision_query(block); + let intersects = Self::static_collision_intersects(collision, pos, from, to); + if block_read.cacheable && retain_cache { + let collision_is_empty = collision.is_empty(); + let mut recorded_in_grid = false; + if let Some(index) = unresolved_grid_index { + let grid_state = ExplosionExposureClearGridState::for_collision(collision_is_empty); + self.clear_grid.record(index, grid_state); + recorded_in_grid = true; + #[cfg(test)] + { + self.stats.clear_grid_resolutions += 1; + } + } + // Dense clear entries no longer need a large direct-cache slot. Retaining only + // potentially colliding shapes reduces destructive conflicts on the exact path. + if !recorded_in_grid || !collision_is_empty { + self.entries[cache_index] = ExplosionExposureCacheEntry { + pos, + collision, + generation: self.generation, + }; + } + } + intersects + } + + fn static_collision_intersects( + collision: OffsetVoxelShape, + pos: BlockPos, + from: DVec3, + to: DVec3, + ) -> bool { + collision + .iter() + .any(|aabb| World::clip_local_aabb(pos, from, to, aabb).is_some()) + } + + #[cfg(test)] + pub(crate) const fn stats(&self) -> ExplosionExposureRaycastStats { + self.stats + } +} + +#[inline] +fn explosion_exposure_cache_index(pos: BlockPos) -> usize { + let tag = PackedBlockPos::from(pos).as_raw() as u64; + // Fibonacci multiplication followed by xor folding disperses nearby packed positions into + // the low bits used by the power-of-two cache. + let mut mixed = tag.wrapping_mul(EXPLOSION_EXPOSURE_FIBONACCI_HASH_MULTIPLIER); + mixed ^= mixed >> (u64::BITS / 2); + mixed ^= mixed >> (u64::BITS / 4); + (mixed as usize) & EXPLOSION_EXPOSURE_CACHE_MASK +} + +#[inline] +fn is_collision_path_clear( + start_pos: DVec3, + end_pos: DVec3, + mut blocks_ray: impl FnMut(BlockPos) -> bool, +) -> bool { + if start_pos == end_pos { + return true; + } + + let to = vanilla_lerp_vec3(VANILLA_RAY_ENDPOINT_ADJUSTMENT, end_pos, start_pos); + let from = vanilla_lerp_vec3(VANILLA_RAY_ENDPOINT_ADJUSTMENT, start_pos, end_pos); + let mut block = BlockPos::from(from); + if blocks_ray(block) { + return false; + } + + let difference = to - from; + let step = glam::IVec3::new( + minecraft_sign(difference.x), + minecraft_sign(difference.y), + minecraft_sign(difference.z), + ); + let delta = DVec3::new( + if step.x == 0 { + f64::MAX + } else { + f64::from(step.x) / difference.x + }, + if step.y == 0 { + f64::MAX + } else { + f64::from(step.y) / difference.y + }, + if step.z == 0 { + f64::MAX + } else { + f64::from(step.z) / difference.z + }, + ); + let mut next = DVec3::new( + delta.x + * if step.x > 0 { + 1.0 - (from.x - from.x.floor()) + } else { + from.x - from.x.floor() + }, + delta.y + * if step.y > 0 { + 1.0 - (from.y - from.y.floor()) + } else { + from.y - from.y.floor() + }, + delta.z + * if step.z > 0 { + 1.0 - (from.z - from.z.floor()) + } else { + from.z - from.z.floor() + }, + ); + + while next.x <= 1.0 || next.y <= 1.0 || next.z <= 1.0 { + if next.x < next.y && next.x < next.z { + block.0.x += step.x; + next.x += delta.x; + } else if next.y < next.z { + block.0.y += step.y; + next.y += delta.y; + } else { + block.0.z += step.z; + next.z += delta.z; + } + if blocks_ray(block) { + return false; + } + } + + true +} + +/// Returns the inclusive block-coordinate envelope visited by one axis of Vanilla's block DDA. +/// +/// Axis crossing counts are independent of inter-axis tie order: the traversal cannot finish +/// until every finite `next` value is greater than one. Replaying the scalar recurrence also +/// retains the rare terminal step caused by floating-point rounding at `next == 1`. +pub(crate) fn collision_path_axis_block_bounds( + start: f64, + end: f64, + max_steps: usize, +) -> Option<(i32, i32)> { + if !start.is_finite() || !end.is_finite() { + return None; + } + + let to = vanilla_lerp(VANILLA_RAY_ENDPOINT_ADJUSTMENT, end, start); + let from = vanilla_lerp(VANILLA_RAY_ENDPOINT_ADJUSTMENT, start, end); + let can_initialize_block_traversal = to.is_finite() + && from.is_finite() + && from >= f64::from(i32::MIN) + && from < f64::from(i32::MAX) + 1.0; + if !can_initialize_block_traversal { + return None; + } + + let mut block = from.floor() as i32; + let mut min = block; + let mut max = block; + let difference = to - from; + let step = minecraft_sign(difference); + let delta = if step == 0 { + f64::MAX + } else { + f64::from(step) / difference + }; + let from_fraction = from - from.floor(); + let mut next = delta + * if step > 0 { + 1.0 - from_fraction + } else { + from_fraction + }; + if !delta.is_finite() || !next.is_finite() { + return None; + } + + let mut steps = 0; + while next <= 1.0 { + if steps == max_steps { + return None; + } + block = block.checked_add(step)?; + min = min.min(block); + max = max.max(block); + next += delta; + steps += 1; + } + Some((min, max)) +} + +#[inline] +const fn minecraft_sign(number: f64) -> i32 { + if number == 0.0 { + 0 + } else if number > 0.0 { + 1 + } else { + -1 + } +} + +#[inline] +fn vanilla_lerp(alpha: f64, start: f64, end: f64) -> f64 { + start + alpha * (end - start) +} + +#[inline] +fn vanilla_lerp_vec3(alpha: f64, start: DVec3, end: DVec3) -> DVec3 { + // `glam::DVec3::lerp` uses `start * (1 - alpha) + end * alpha`. Vanilla's + // `Mth.lerp` uses this operation order, which can round to a different block boundary. + DVec3::new( + vanilla_lerp(alpha, start.x, end.x), + vanilla_lerp(alpha, start.y, end.y), + vanilla_lerp(alpha, start.z, end.z), + ) +} + impl ClipHitResult { /// Returns whether this clip missed all selected block and fluid shapes. #[must_use] @@ -68,6 +669,32 @@ impl ClipHitResult { } impl World { + /// Reads exposure state while distinguishing stable Full data from an air + /// fallback caused by a chunk that may still publish asynchronously. + fn explosion_exposure_block_state( + &self, + pos: BlockPos, + full_chunks: &mut LocalFullChunkHolderCache, + ) -> ExplosionExposureBlockRead { + if !self.is_in_valid_bounds(pos) { + return ExplosionExposureBlockRead { + state: REGISTRY.blocks.get_base_state_id(&vanilla_blocks::VOID_AIR), + cacheable: true, + }; + } + + full_chunks.block_state(&self.chunk_map, pos).map_or_else( + || ExplosionExposureBlockRead { + state: REGISTRY.blocks.get_base_state_id(&vanilla_blocks::AIR), + cacheable: false, + }, + |state| ExplosionExposureBlockRead { + state, + cacheable: true, + }, + ) + } + /// Checks if a ray intersects with a block's selection box. pub fn ray_outline_check( &self, @@ -177,6 +804,25 @@ impl World { Self::clip_miss(start_pos, end_pos) } + /// Returns whether a collider-only, fluid-free clip misses every block. + #[cfg(test)] + pub(crate) fn is_block_collision_path_clear( + &self, + start_pos: DVec3, + end_pos: DVec3, + collision_context: BlockCollisionContext, + ) -> bool { + is_collision_path_clear(start_pos, end_pos, |pos| { + let state = self.get_block_state(pos); + let boxes = BLOCK_BEHAVIORS + .get_behavior(state.get_block()) + .get_collision_boxes(state, self, pos, collision_context); + boxes + .into_iter() + .any(|aabb| Self::clip_local_aabb(pos, start_pos, end_pos, aabb).is_some()) + }) + } + /// Performs a normal ray clip, then clamps the hit to the world border if /// the ray started inside it but the hit landed outside. #[must_use] @@ -478,11 +1124,11 @@ impl World { let local = point - block_vec; !aabb.is_empty() && local.x >= aabb.min_x() - && local.x <= aabb.max_x() + && local.x < aabb.max_x() && local.y >= aabb.min_y() - && local.y <= aabb.max_y() + && local.y < aabb.max_y() && local.z >= aabb.min_z() - && local.z <= aabb.max_z() + && local.z < aabb.max_z() } pub(super) fn clip_miss(from: DVec3, to: DVec3) -> ClipHitResult { @@ -516,79 +1162,112 @@ impl World { result } - /// Ray-AABB intersection returning the entry t-parameter and the hit face. - /// - /// Returns `Some((tmin, direction))` where `tmin` is the ray parameter at entry - /// and `direction` is the face normal pointing away from the hit surface. - /// Returns `None` if the AABB is missed or entirely behind the ray origin. - /// - /// Used internally by [`ray_outline_check`] to pick the *closest* hit across - /// a multi-box voxel shape, matching vanilla's `VoxelShape.clip()` behavior. + /// Mirrors Minecraft 26.2 `AABB.getDirection` for one block-local shape box. pub(super) fn intersects_aabb_with_t( start: DVec3, end: DVec3, min: DVec3, max: DVec3, ) -> Option<(f64, Direction)> { - let dir = end - start; - - let mut tmin = f64::NEG_INFINITY; - let mut tmax = f64::INFINITY; - let mut hit_dir = None; + let delta = end - start; + let mut scale = 1.0; + let mut direction = None; + + if delta.x > AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + delta, + start, + min.x, + [min.y, max.y, min.z, max.z], + Direction::West, + ); + } else if delta.x < -AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + delta, + start, + max.x, + [min.y, max.y, min.z, max.z], + Direction::East, + ); + } - macro_rules! slab { - ($start:expr, $dir:expr, $min:expr, $max:expr, $neg:expr, $pos:expr) => {{ - if $dir.abs() < 1e-8 { - if $start < $min || $start > $max { - return None; - } - } else { - let inv = 1.0 / $dir; - let mut t1 = ($min - $start) * inv; - let mut t2 = ($max - $start) * inv; - - let dir_hit = if t1 > t2 { - std::mem::swap(&mut t1, &mut t2); - $pos - } else { - $neg - }; - - if t1 > tmin { - tmin = t1; - hit_dir = Some(dir_hit); - } + let y_delta = DVec3::new(delta.y, delta.z, delta.x); + let y_start = DVec3::new(start.y, start.z, start.x); + if delta.y > AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + y_delta, + y_start, + min.y, + [min.z, max.z, min.x, max.x], + Direction::Down, + ); + } else if delta.y < -AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + y_delta, + y_start, + max.y, + [min.z, max.z, min.x, max.x], + Direction::Up, + ); + } - tmax = tmax.min(t2); - if tmin > tmax { - return None; - } - } - }}; + let z_delta = DVec3::new(delta.z, delta.x, delta.y); + let z_start = DVec3::new(start.z, start.x, start.y); + if delta.z > AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + z_delta, + z_start, + min.z, + [min.x, max.x, min.y, max.y], + Direction::North, + ); + } else if delta.z < -AABB_CLIP_EPSILON { + Self::clip_aabb_face( + &mut scale, + &mut direction, + z_delta, + z_start, + max.z, + [min.x, max.x, min.y, max.y], + Direction::South, + ); } - slab!( - start.x, - dir.x, - min.x, - max.x, - Direction::West, - Direction::East - ); - slab!(start.y, dir.y, min.y, max.y, Direction::Down, Direction::Up); - slab!( - start.z, - dir.z, - min.z, - max.z, - Direction::North, - Direction::South - ); + direction.map(|direction| (scale, direction)) + } - if tmax < 0.0 { - None - } else { - hit_dir.map(|d| (tmin, d)) + #[inline] + fn clip_aabb_face( + scale: &mut f64, + direction: &mut Option, + delta: DVec3, + start: DVec3, + face: f64, + side_bounds: [f64; 4], + new_direction: Direction, + ) { + let candidate_scale = (face - start.x) / delta.x; + let side_b = start.y + candidate_scale * delta.y; + let side_c = start.z + candidate_scale * delta.z; + if 0.0 < candidate_scale + && candidate_scale < *scale + && side_bounds[0] - AABB_CLIP_EPSILON < side_b + && side_b < side_bounds[1] + AABB_CLIP_EPSILON + && side_bounds[2] - AABB_CLIP_EPSILON < side_c + && side_c < side_bounds[3] + AABB_CLIP_EPSILON + { + *scale = candidate_scale; + *direction = Some(new_direction); } } @@ -720,3 +1399,307 @@ impl World { (None, None) } } + +#[cfg(test)] +mod voxel_shape_clip_tests { + use super::*; + + fn axis_point(axis: usize, primary: f64, side_b: f64, side_c: f64) -> DVec3 { + match axis { + 0 => DVec3::new(primary, side_b, side_c), + 1 => DVec3::new(side_c, primary, side_b), + 2 => DVec3::new(side_b, side_c, primary), + _ => unreachable!("the fixture only defines the three coordinate axes"), + } + } + + fn axis_bounds(axis: usize, primary_min: f64, primary_max: f64) -> (DVec3, DVec3) { + ( + axis_point(axis, primary_min, 0.0, 0.0), + axis_point(axis, primary_max, 1.0, 1.0), + ) + } + + fn primary_component(vector: DVec3, axis: usize) -> f64 { + match axis { + 0 => vector.x, + 1 => vector.y, + 2 => vector.z, + _ => unreachable!("the fixture only defines the three coordinate axes"), + } + } + + #[test] + fn voxel_shape_clip_matches_java_full_cube_at_exact_opposite_boundaries() { + let shape = OffsetVoxelShape::without_offset(VoxelShape::FULL_BLOCK); + + // Minecraft 26.2's VoxelShape.clip probes 0.001 along the segment. For a + // CubeVoxelShape the resulting x=1 index is outside, while x=0 is inside. + assert!( + World::clip_shape( + BlockPos::ZERO, + DVec3::new(0.0, 0.5, 0.5), + DVec3::new(1000.0, 0.5, 0.5), + shape, + ) + .is_none() + ); + + let Some(hit) = World::clip_shape( + BlockPos::ZERO, + DVec3::new(1.0, 0.5, 0.5), + DVec3::new(-999.0, 0.5, 0.5), + shape, + ) else { + panic!("the lower-bound probe should start inside the full cube"); + }; + assert!(hit.inside); + assert_eq!(hit.location, DVec3::new(0.0, 0.5, 0.5)); + assert_eq!(hit.direction, Direction::East); + } + + #[test] + fn aabb_clip_matches_java_strict_side_epsilon_in_every_axis() { + for (axis, positive_direction) in [ + (0, Direction::West), + (1, Direction::Down), + (2, Direction::North), + ] { + let (min, max) = axis_bounds(axis, 0.0, 1.0); + let Some((_, direction)) = World::intersects_aabb_with_t( + axis_point(axis, -1.0, 1.0 + 0.5e-7, -0.5e-7), + axis_point(axis, 2.0, 1.0 + 0.5e-7, -0.5e-7), + min, + max, + ) else { + panic!("Java's side epsilon should accept axis {axis}"); + }; + assert_eq!(direction, positive_direction); + + assert!( + World::intersects_aabb_with_t( + axis_point(axis, -1.0, 1.0 + AABB_CLIP_EPSILON, 0.5), + axis_point(axis, 2.0, 1.0 + AABB_CLIP_EPSILON, 0.5), + min, + max, + ) + .is_none(), + "Java's upper side epsilon is strict on axis {axis}", + ); + assert!( + World::intersects_aabb_with_t( + axis_point(axis, -1.0, 0.5, -AABB_CLIP_EPSILON), + axis_point(axis, 2.0, 0.5, -AABB_CLIP_EPSILON), + min, + max, + ) + .is_none(), + "Java's lower side epsilon is strict on axis {axis}", + ); + } + } + + #[test] + fn aabb_clip_matches_java_direction_threshold_in_every_axis() { + let threshold = AABB_CLIP_EPSILON; + let above_threshold = f64::from_bits(threshold.to_bits() + 1); + for (axis, positive_direction, negative_direction) in [ + (0, Direction::West, Direction::East), + (1, Direction::Down, Direction::Up), + (2, Direction::North, Direction::South), + ] { + let exact_positive_from = axis_point(axis, -threshold / 2.0, 0.5, 0.5); + let exact_positive_to = axis_point(axis, threshold / 2.0, 0.5, 0.5); + assert_eq!( + primary_component(exact_positive_to - exact_positive_from, axis).to_bits(), + threshold.to_bits(), + ); + let (positive_min, positive_max) = axis_bounds(axis, 0.0, 1.0); + assert!( + World::intersects_aabb_with_t( + exact_positive_from, + exact_positive_to, + positive_min, + positive_max, + ) + .is_none(), + "Java does not test a positive face at exactly +1e-7 on axis {axis}", + ); + + let above_positive_from = axis_point(axis, -above_threshold / 2.0, 0.5, 0.5); + let above_positive_to = axis_point(axis, above_threshold / 2.0, 0.5, 0.5); + let Some((_, direction)) = World::intersects_aabb_with_t( + above_positive_from, + above_positive_to, + positive_min, + positive_max, + ) else { + panic!("Java tests a positive face above +1e-7 on axis {axis}"); + }; + assert_eq!(direction, positive_direction); + + let exact_negative_from = axis_point(axis, threshold / 2.0, 0.5, 0.5); + let exact_negative_to = axis_point(axis, -threshold / 2.0, 0.5, 0.5); + assert_eq!( + primary_component(exact_negative_to - exact_negative_from, axis).to_bits(), + (-threshold).to_bits(), + ); + let (negative_min, negative_max) = axis_bounds(axis, -1.0, 0.0); + assert!( + World::intersects_aabb_with_t( + exact_negative_from, + exact_negative_to, + negative_min, + negative_max, + ) + .is_none(), + "Java does not test a negative face at exactly -1e-7 on axis {axis}", + ); + + let above_negative_from = axis_point(axis, above_threshold / 2.0, 0.5, 0.5); + let above_negative_to = axis_point(axis, -above_threshold / 2.0, 0.5, 0.5); + let Some((_, direction)) = World::intersects_aabb_with_t( + above_negative_from, + above_negative_to, + negative_min, + negative_max, + ) else { + panic!("Java tests a negative face below -1e-7 on axis {axis}"); + }; + assert_eq!(direction, negative_direction); + } + } + + #[test] + fn aabb_clip_preserves_java_axis_order_for_equal_corner_hits() { + let Some((_, direction)) = World::intersects_aabb_with_t( + DVec3::new(-1.0, -1.0, 0.5), + DVec3::new(2.0, 2.0, 0.5), + DVec3::ZERO, + DVec3::ONE, + ) else { + panic!("the corner fixture should hit the full cube"); + }; + assert_eq!(direction, Direction::West); + } +} + +#[cfg(test)] +mod explosion_exposure_cache_tests { + use super::*; + use crate::test_support::fresh_test_world; + use steel_registry::{ + blocks::{Block, behavior::BlockConfig}, + vanilla_blocks, + }; + + const TEST_DDA_MAX_STEPS: usize = 64; + + #[test] + fn scalar_axis_envelope_matches_full_vanilla_dda_visits() { + for (start, end) in [ + (DVec3::splat(0.5), DVec3::new(5.5, 2.5, -3.5)), + (DVec3::new(1.0, 2.0, 3.0), DVec3::new(0.0, 2.0, 3.0)), + (DVec3::splat(-0.5), DVec3::new(-4.5, -4.5, -1.5)), + (DVec3::splat(0.5), DVec3::splat(4.5)), + ] { + let mut visits = Vec::new(); + assert!(is_collision_path_clear(start, end, |pos| { + visits.push(pos); + false + })); + for axis in 0..3 { + let (expected_min, expected_max) = visits + .iter() + .map(|pos| pos.0[axis]) + .fold((i32::MAX, i32::MIN), |(min, max), value| { + (min.min(value), max.max(value)) + }); + assert_eq!( + collision_path_axis_block_bounds(start[axis], end[axis], TEST_DDA_MAX_STEPS), + Some((expected_min, expected_max)), + "axis={axis}, start={start:?}, end={end:?}, visits={visits:?}" + ); + } + } + } + + #[test] + fn collision_path_matches_vanilla_axis_tie_breaking() { + let start = DVec3::splat(0.5); + for (end, expected) in [ + (DVec3::new(1.5, 1.5, 0.75), BlockPos::new(0, 1, 0)), + (DVec3::new(1.5, 0.75, 1.5), BlockPos::new(0, 0, 1)), + (DVec3::new(0.75, 1.5, 1.5), BlockPos::new(0, 0, 1)), + (DVec3::splat(1.5), BlockPos::new(0, 0, 1)), + ] { + let mut first_step = None; + is_collision_path_clear(start, end, |pos| { + if pos == BlockPos::ZERO { + return false; + } + first_step = Some(pos); + true + }); + assert_eq!(first_step, Some(expected), "end={end:?}"); + } + } + + #[test] + fn collision_path_preserves_vanilla_lerp_rounding_at_integer_boundaries() { + let start = DVec3::new(1.0, 0.5, 0.5); + let end = DVec3::new(1.000_000_001, 0.5, 0.5); + let mut visits = Vec::new(); + + assert!(is_collision_path_clear(start, end, |pos| { + visits.push(pos); + false + })); + // Vanilla's `Mth.lerp` operation order moves the adjusted start below x = 1. + assert_eq!(visits, [BlockPos::new(0, 0, 0), BlockPos::new(1, 0, 0)]); + } + + #[test] + fn extensible_collision_query_invalidates_every_exposure_cache() { + static PLUGIN_BLOCK: Block = Block::new( + Identifier::new_static("exposure_test", "mutating_shape"), + BlockConfig::new(), + &[], + ); + + let fixture = fresh_test_world("extensible_exposure_cache_boundary"); + + let world = &fixture.world; + let mut raycast = + ExplosionExposureRaycast::new(world.as_ref(), BlockCollisionContext::empty()); + let pos = BlockPos::new(0, 64, 0); + raycast.configure_clear_grid(pos, pos); + let Some(grid_index) = raycast.clear_grid.index(pos) else { + panic!("the configured position should be represented in the clear grid"); + }; + raycast + .clear_grid + .record(grid_index, ExplosionExposureClearGridState::StaticEmpty); + let direct_index = explosion_exposure_cache_index(pos); + raycast.entries[direct_index] = ExplosionExposureCacheEntry { + pos, + collision: OffsetVoxelShape::without_offset(VoxelShape::EMPTY), + generation: raycast.generation, + }; + + let initial_generation = raycast.generation; + assert!(raycast.retain_cache_after_collision_query(&vanilla_blocks::STONE)); + assert_eq!(raycast.generation, initial_generation); + assert_eq!( + raycast.clear_grid.state(grid_index), + ExplosionExposureClearGridState::StaticEmpty, + ); + assert!(!raycast.retain_cache_after_collision_query(&PLUGIN_BLOCK)); + assert_ne!(raycast.generation, initial_generation); + assert_eq!( + raycast.clear_grid.state(grid_index), + ExplosionExposureClearGridState::Unresolved, + ); + assert_ne!(raycast.entries[direct_index].generation, raycast.generation); + } +} diff --git a/steel-core/src/worldgen/chunk_stage_hashes.rs b/steel-core/src/worldgen/chunk_stage_hashes.rs index d8b1cb16603..2fc25254309 100644 --- a/steel-core/src/worldgen/chunk_stage_hashes.rs +++ b/steel-core/src/worldgen/chunk_stage_hashes.rs @@ -316,7 +316,7 @@ fn compute_block_hash(sections: &Sections) -> String { 'scan: for y in 0..16 { for z in 0..16 { for x in 0..16 { - if !section.states.get(x, y, z).is_air() { + if !section.states().get(x, y, z).is_air() { all_air = false; break 'scan; } @@ -329,7 +329,7 @@ fn compute_block_hash(sections: &Sections) -> String { for y in 0..16 { for z in 0..16 { for x in 0..16 { - let state = section.states.get(x, y, z); + let state = section.states().get(x, y, z); let state_id = u32::from(state.0); ctx.consume([(state_id >> 24) as u8]); ctx.consume([(state_id >> 16) as u8]); @@ -562,7 +562,7 @@ fn diff_chunk(sections: &Sections, reference: &ChunkBlockData, min_y: i32) -> Ve match ref_section { Some(Some(ref_ids)) => { - if section.states.has_only_air() { + if section.states().has_only_air() { // Steel says all air, vanilla has data for (idx, &vanilla_id) in ref_ids.iter().enumerate() { if vanilla_id != 0 { @@ -585,7 +585,7 @@ fn diff_chunk(sections: &Sections, reference: &ChunkBlockData, min_y: i32) -> Ve let idx = y_local * 256 + z * 16 + x; let vanilla_id = ref_ids[idx]; let steel_id = - u32::from(section.states.get(x, y_local, z).0) as i32; + u32::from(section.states().get(x, y_local, z).0) as i32; if vanilla_id != steel_id { diffs.push(BlockDiff { x, @@ -602,12 +602,12 @@ fn diff_chunk(sections: &Sections, reference: &ChunkBlockData, min_y: i32) -> Ve } Some(None) | None => { // Vanilla says all air (or section missing). Check if Steel also has air. - if !section.states.has_only_air() { + if !section.states().has_only_air() { for y_local in 0..16usize { for z in 0..16usize { for x in 0..16usize { let steel_id = - u32::from(section.states.get(x, y_local, z).0) as i32; + u32::from(section.states().get(x, y_local, z).0) as i32; if steel_id != 0 { diffs.push(BlockDiff { x, diff --git a/steel-core/src/worldgen/region.rs b/steel-core/src/worldgen/region.rs index f3ba89a8003..837f516cbbf 100644 --- a/steel-core/src/worldgen/region.rs +++ b/steel-core/src/worldgen/region.rs @@ -34,7 +34,7 @@ use crate::chunk::{ chunk_pyramid::ChunkStep, full_chunk::FullChunkRef, heightmap::{Heightmap, HeightmapType}, - section::{ChunkSection, SectionHolder, SectionWriteGuard, Sections}, + section::{SectionHolder, SectionWriteGuard, Sections}, status::ChunkStatus, }; use crate::entity::SharedEntity; @@ -984,7 +984,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = Self::local_coord(pos.x()); let local_y = Self::local_coord(pos.y()); let local_z = Self::local_coord(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); let Some(state) = replacement(old_state) else { Self::record_ore_write_time(ore_profile, started_at); return false; @@ -1059,7 +1059,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); if let Some(state) = replacement(old_state) { let old_state = Self::set_bulk_block_state( chunk.holder, @@ -1079,7 +1079,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); if let Some(state) = replacement(old_state) { let old_state = Self::set_bulk_block_state( chunk.holder, @@ -1147,12 +1147,12 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof } else { section.read() }; - if section_guard.states.has_only_air() { + if section_guard.states().has_only_air() { Self::record_ore_read_time(ore_profile, started_at); return air; } - let state = section_guard.states.get( + let state = section_guard.states().get( Self::local_coord(pos.x()), Self::local_coord(pos.y()), Self::local_coord(pos.z()), @@ -1239,7 +1239,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof fn set_bulk_block_state( holder: &ChunkHolder, - section: &mut ChunkSection, + section: &mut SectionWriteGuard<'_>, local_x: usize, local_y: usize, local_z: usize, @@ -1519,7 +1519,7 @@ mod tests { chunk_holder::ChunkHolder, chunk_ticket_manager::ChunkTicketLevel, heightmap::{Heightmap, HeightmapType}, - section::{ChunkSection, Sections}, + section::{ChunkSection, SectionHolder, Sections}, status::ChunkStatus, }; @@ -1679,7 +1679,8 @@ mod tests { let stone = vanilla_blocks::STONE.default_state(); let air = vanilla_blocks::AIR.default_state(); - let mut pre_light_section = ChunkSection::new_empty(); + let pre_light_section = SectionHolder::new(ChunkSection::new_empty()); + let mut pre_light_section = pre_light_section.write(); let pre_light_holder = published_holder(ChunkStatus::Empty); WorldGenBulkSectionAccess::set_bulk_block_state( @@ -1693,7 +1694,8 @@ mod tests { assert_eq!(pre_light_section.non_empty_block_count(), 0); - let mut initialized_section = ChunkSection::new_empty(); + let initialized_section = SectionHolder::new(ChunkSection::new_empty()); + let mut initialized_section = initialized_section.write(); let initialized_holder = published_holder(ChunkStatus::InitializeLight); WorldGenBulkSectionAccess::set_bulk_block_state( @@ -1706,8 +1708,19 @@ mod tests { ); assert_eq!(initialized_section.non_empty_block_count(), 1); + WorldGenBulkSectionAccess::set_bulk_block_state( + &initialized_holder, + &mut initialized_section, + 1, + 2, + 3, + air, + ); + assert_eq!(initialized_section.non_empty_block_count(), 0); + assert!(initialized_section.stable_air_box_is_clear(0, 15, 0, 15, 0, 15)); - let mut initialized_building_section = ChunkSection::new_empty(); + let initialized_building_section = SectionHolder::new(ChunkSection::new_empty()); + let mut initialized_building_section = initialized_building_section.write(); initialized_building_section.set_block_state_for_generation(1, 2, 3, stone); let old_state = WorldGenBulkSectionAccess::set_bulk_block_state( diff --git a/steel-protocol/src/packets/game/c_explode.rs b/steel-protocol/src/packets/game/c_explode.rs new file mode 100644 index 00000000000..07a1586e8a6 --- /dev/null +++ b/steel-protocol/src/packets/game/c_explode.rs @@ -0,0 +1,220 @@ +use glam::DVec3; +use steel_macros::{ClientPacket, WriteTo}; +use steel_registry::{ + packets::play::C_EXPLODE, particle_type::ParticleData, sound_event::SoundEventHolder, +}; +use thiserror::Error; + +/// A block-debris particle and the client-side scaling applied to it. +#[derive(WriteTo, Clone, Debug)] +pub struct ExplosionParticleInfo { + pub particle: ParticleData, + pub scaling: f32, + pub speed: f32, +} + +/// A weighted entry in an explosion's block-particle palette. +#[derive(WriteTo, Clone, Debug)] +struct WeightedExplosionParticleInfo { + value: ExplosionParticleInfo, + #[write(as = VarInt)] + weight: i32, +} + +/// A validated weighted palette for an explosion's block particles. +#[derive(WriteTo, Clone, Debug)] +pub struct ExplosionParticlePalette(Vec); + +impl ExplosionParticlePalette { + /// Validates Vanilla's non-negative weights and signed 32-bit list limits. + pub fn try_new( + entries: Vec<(ExplosionParticleInfo, i32)>, + ) -> Result { + if i32::try_from(entries.len()).is_err() { + return Err(ExplosionParticleWeightError::TooManyEntries(entries.len())); + } + + let mut total = 0_i32; + let mut weighted = Vec::with_capacity(entries.len()); + for (value, weight) in entries { + if weight < 0 { + return Err(ExplosionParticleWeightError::Negative(weight)); + } + total = total + .checked_add(weight) + .ok_or(ExplosionParticleWeightError::TotalOverflow)?; + weighted.push(WeightedExplosionParticleInfo { value, weight }); + } + Ok(Self(weighted)) + } +} + +/// An invalid block-particle weight supplied for an explosion packet. +#[derive(Error, Clone, Copy, Debug, PartialEq, Eq)] +pub enum ExplosionParticleWeightError { + /// Vanilla rejects individual negative weights. + #[error("explosion particle weight cannot be negative: {0}")] + Negative(i32), + /// Vanilla rejects weighted lists whose total exceeds `i32::MAX`. + #[error("total explosion particle weight exceeds i32::MAX")] + TotalOverflow, + /// Vanilla's list codec uses a signed 32-bit entry count. + #[error("explosion particle palette has too many entries: {0}")] + TooManyEntries(usize), +} + +/// Sent to apply the client-visible effects of a server-side explosion. +#[derive(ClientPacket, WriteTo, Clone, Debug)] +#[packet_id(Play = C_EXPLODE)] +pub struct CExplode { + pub center: DVec3, + pub radius: f32, + pub block_count: i32, + pub player_knockback: Option, + pub explosion_particle: ParticleData, + pub explosion_sound: SoundEventHolder, + pub block_particles: ExplosionParticlePalette, +} + +#[cfg(test)] +mod tests { + use glam::DVec3; + use steel_registry::{ + RegistryEntry, init_vanilla_registry, particle_type::ParticleData, + sound_event::SoundEventHolder, sound_events, vanilla_particle_types, + }; + use steel_utils::{Identifier, codec::VarInt, serial::WriteTo}; + + use super::{ + CExplode, ExplosionParticleInfo, ExplosionParticlePalette, ExplosionParticleWeightError, + }; + + #[test] + fn writes_fields_in_vanilla_wire_order() { + init_vanilla_registry(); + let packet = CExplode { + center: DVec3::new(1.25, -2.5, 3.75), + radius: 4.0, + block_count: 17, + player_knockback: Some(DVec3::new(-0.25, 0.5, 1.0)), + explosion_particle: ParticleData::simple(&vanilla_particle_types::EXPLOSION), + explosion_sound: SoundEventHolder::registry(&sound_events::ENTITY_GENERIC_EXPLODE), + block_particles: palette(vec![ + ( + ExplosionParticleInfo { + particle: ParticleData::simple(&vanilla_particle_types::POOF), + scaling: 0.5, + speed: 1.0, + }, + 2, + ), + ( + ExplosionParticleInfo { + particle: ParticleData::simple(&vanilla_particle_types::SMOKE), + scaling: 1.0, + speed: 0.75, + }, + 3, + ), + ]), + }; + + let mut encoded = Vec::new(); + assert!(packet.write(&mut encoded).is_ok()); + + let mut expected = Vec::new(); + expected.extend_from_slice(&1.25_f64.to_be_bytes()); + expected.extend_from_slice(&(-2.5_f64).to_be_bytes()); + expected.extend_from_slice(&3.75_f64.to_be_bytes()); + expected.extend_from_slice(&4.0_f32.to_be_bytes()); + expected.extend_from_slice(&17_i32.to_be_bytes()); + expected.push(1); + expected.extend_from_slice(&(-0.25_f64).to_be_bytes()); + expected.extend_from_slice(&0.5_f64.to_be_bytes()); + expected.extend_from_slice(&1.0_f64.to_be_bytes()); + write_registry_id(&mut expected, vanilla_particle_types::EXPLOSION.id()); + assert!( + VarInt(sound_events::ENTITY_GENERIC_EXPLODE.packet_holder_id()) + .write(&mut expected) + .is_ok() + ); + + assert!(VarInt(2).write(&mut expected).is_ok()); + write_registry_id(&mut expected, vanilla_particle_types::POOF.id()); + expected.extend_from_slice(&0.5_f32.to_be_bytes()); + expected.extend_from_slice(&1.0_f32.to_be_bytes()); + assert!(VarInt(2).write(&mut expected).is_ok()); + write_registry_id(&mut expected, vanilla_particle_types::SMOKE.id()); + expected.extend_from_slice(&1.0_f32.to_be_bytes()); + expected.extend_from_slice(&0.75_f32.to_be_bytes()); + assert!(VarInt(3).write(&mut expected).is_ok()); + + assert_eq!(encoded, expected); + } + + #[test] + fn writes_direct_explosion_sound_holder() { + init_vanilla_registry(); + let sound_id = Identifier::from_steel("test_explosion"); + let packet = CExplode { + center: DVec3::ZERO, + radius: 0.0, + block_count: 0, + player_knockback: None, + explosion_particle: ParticleData::simple(&vanilla_particle_types::EXPLOSION), + explosion_sound: SoundEventHolder::Direct { + sound_id: sound_id.clone(), + fixed_range: Some(24.0), + }, + block_particles: palette(Vec::new()), + }; + + let mut encoded = Vec::new(); + assert!(packet.write(&mut encoded).is_ok()); + + let mut expected = vec![0; 24]; + expected.extend_from_slice(&0.0_f32.to_be_bytes()); + expected.extend_from_slice(&0_i32.to_be_bytes()); + expected.push(0); + write_registry_id(&mut expected, vanilla_particle_types::EXPLOSION.id()); + assert!(VarInt(0).write(&mut expected).is_ok()); + assert!(sound_id.write(&mut expected).is_ok()); + expected.push(1); + expected.extend_from_slice(&24.0_f32.to_be_bytes()); + assert!(VarInt(0).write(&mut expected).is_ok()); + + assert_eq!(encoded, expected); + } + + #[test] + fn rejects_invalid_explosion_particle_weights() { + let particle = || ExplosionParticleInfo { + particle: ParticleData::simple(&vanilla_particle_types::POOF), + scaling: 1.0, + speed: 1.0, + }; + + assert!(matches!( + ExplosionParticlePalette::try_new(vec![(particle(), -1)]), + Err(ExplosionParticleWeightError::Negative(-1)) + )); + assert!(matches!( + ExplosionParticlePalette::try_new(vec![(particle(), i32::MAX), (particle(), 1),]), + Err(ExplosionParticleWeightError::TotalOverflow) + )); + } + + fn palette(entries: Vec<(ExplosionParticleInfo, i32)>) -> ExplosionParticlePalette { + let Ok(palette) = ExplosionParticlePalette::try_new(entries) else { + panic!("test explosion particle palette should be valid"); + }; + palette + } + + fn write_registry_id(output: &mut Vec, id: usize) { + let Ok(id) = i32::try_from(id) else { + panic!("test registry id should fit in i32"); + }; + assert!(VarInt(id).write(output).is_ok()); + } +} diff --git a/steel-protocol/src/packets/game/mod.rs b/steel-protocol/src/packets/game/mod.rs index 865c5e74a4c..8264e469221 100644 --- a/steel-protocol/src/packets/game/mod.rs +++ b/steel-protocol/src/packets/game/mod.rs @@ -19,6 +19,7 @@ mod c_cooldown; mod c_damage_event; mod c_entity_event; mod c_entity_position_sync; +mod c_explode; mod c_game_event; mod c_hurt_animation; mod c_level_event; @@ -123,6 +124,9 @@ pub use c_cooldown::CCooldown; pub use c_damage_event::CDamageEvent; pub use c_entity_event::CEntityEvent; pub use c_entity_position_sync::CEntityPositionSync; +pub use c_explode::{ + CExplode, ExplosionParticleInfo, ExplosionParticlePalette, ExplosionParticleWeightError, +}; pub use c_game_event::CGameEvent; pub use c_game_event::GameEventType; pub use c_hurt_animation::CHurtAnimation; From afa98c5150806fbb61529851824d309798416c93 Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Wed, 7 Oct 2026 13:26:11 +0000 Subject: [PATCH 02/26] refactor: Address explosion review feedback - Inline single-use TNT constants and remove the unused NO_FUSE sentinel. Fire charges now use ItemStack::consume_one. - Drop the dense cache entry's explicit padding. The entry is already eight bytes without it. - Share vanilla's BlockPos hash between the redstone HashMap-order helper and the explosion HashSet emulation via BlockPos::java_hash_code. - Move vanilla Util.shuffle to Random::shuffle, and merge the two identical inline-to-hashed visited block-position sets. - Shorten the collision cursor order comment. --- .../src/behavior/blocks/redstone/java_hash.rs | 7 +-- .../src/behavior/blocks/redstone/tnt_block.rs | 15 +++---- steel-core/src/entity/block_effects.rs | 16 +++---- .../entities/objects/explosives/primed_tnt.rs | 10 +---- steel-core/src/entity/mod.rs | 44 ++----------------- steel-core/src/physics/collision.rs | 4 +- .../world/explosion/server/block_effects.rs | 14 +----- .../explosion/server/block_rays/cache.rs | 9 ---- .../server/block_rays/java_block_pos_set.rs | 7 +-- steel-utils/src/random/mod.rs | 11 +++++ steel-utils/src/types/position.rs | 9 ++++ 11 files changed, 41 insertions(+), 105 deletions(-) diff --git a/steel-core/src/behavior/blocks/redstone/java_hash.rs b/steel-core/src/behavior/blocks/redstone/java_hash.rs index 7f2d5bf3f84..42cdc08517f 100644 --- a/steel-core/src/behavior/blocks/redstone/java_hash.rs +++ b/steel-core/src/behavior/blocks/redstone/java_hash.rs @@ -14,11 +14,6 @@ pub(super) fn sort_small_map_positions(positions: &mut [BlockPos]) { #[must_use] pub(super) const fn bucket(pos: BlockPos) -> u32 { - let hash = pos - .z() - .wrapping_mul(31) - .wrapping_add(pos.y()) - .wrapping_mul(31) - .wrapping_add(pos.x()) as u32; + let hash = pos.java_hash_code() as u32; (hash ^ (hash >> 16)) & 15 } diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block.rs b/steel-core/src/behavior/blocks/redstone/tnt_block.rs index 459c6bfa408..b4f2ab10b27 100644 --- a/steel-core/src/behavior/blocks/redstone/tnt_block.rs +++ b/steel-core/src/behavior/blocks/redstone/tnt_block.rs @@ -26,11 +26,6 @@ use crate::player::Player; use crate::world::game_event::GameEventContext; use crate::world::{ClipHitResult, Explosion, SignalGetter as _, World}; -const PRIMING_SOUND_VOLUME: f32 = 1.0; -const PRIMING_SOUND_PITCH: f32 = 1.0; -const FLINT_AND_STEEL_DAMAGE_PER_USE: i32 = 1; -const FIRE_CHARGE_ITEMS_PER_USE: i32 = 1; - /// Vanilla TNT block behavior. #[block_behavior] pub struct TntBlock { @@ -78,8 +73,8 @@ impl TntBlock { &sound_events::ENTITY_TNT_PRIMED, SoundSource::Blocks, position, - PRIMING_SOUND_VOLUME, - PRIMING_SOUND_PITCH, + 1.0, + 1.0, None, ); world.game_event( @@ -199,9 +194,9 @@ impl BlockBehavior for TntBlock { let used_item = inv.with_item(|item| { let used_item = item.item(); if is_flint_and_steel { - item.hurt_and_break(FLINT_AND_STEEL_DAMAGE_PER_USE, has_infinite_materials); - } else if !has_infinite_materials { - item.shrink(FIRE_CHARGE_ITEMS_PER_USE); + item.hurt_and_break(1, has_infinite_materials); + } else { + item.consume_one(has_infinite_materials); } used_item }); diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index d3037cce92b..8412e50412a 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -11,21 +11,20 @@ const CLIP_EPSILON: f64 = 1.0e-7; const CORNER_HIT_EPSILON: f64 = 1.0e-5; const ENTITY_INSIDE_SWEEP_INFLATE_EPSILON: f64 = 1.0e-7; -/// Deduplicates candidate positions within one geometric sweep of one movement segment. -/// Ordinary sweeps use inline storage; unusually large sweeps switch to a hash set. -enum MovementVisitedBlocks { +/// Deduplicates block positions inline, switching to a hash set for unusually large sweeps. +pub(super) enum VisitedBlockPositions { Inline(SmallVec<[BlockPos; 16]>), Hashed(FxHashSet), } -impl Default for MovementVisitedBlocks { +impl Default for VisitedBlockPositions { fn default() -> Self { Self::Inline(SmallVec::new()) } } -impl MovementVisitedBlocks { - fn insert(&mut self, pos: BlockPos) -> bool { +impl VisitedBlockPositions { + pub(super) fn insert(&mut self, pos: BlockPos) -> bool { match self { Self::Hashed(visited) => visited.insert(pos), Self::Inline(inline) => { @@ -68,7 +67,8 @@ pub(super) fn for_each_block_intersected_between( return Some(last_iteration + 1); } - let mut visited = MovementVisitedBlocks::default(); + // Deduplicates candidates within this segment's geometric sweep. + let mut visited = VisitedBlockPositions::default(); let aabb_at_start = aabb_at_target.translate(-travel); if !for_each_between_corners_in_direction(aabb_at_start, travel, |pos| { last_iteration = 0; @@ -139,7 +139,7 @@ pub(super) fn collided_with_aabb_moving_from( reason = "keeps the vanilla BlockGetter.addCollisionsAlongTravel port auditable" )] fn add_collisions_along_travel( - visited: &mut MovementVisitedBlocks, + visited: &mut VisitedBlockPositions, travel: DVec3, aabb_at_target: WorldAabb, visitor: &mut impl FnMut(BlockPos, i32) -> bool, diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs index 194db14b56a..49b91fc54cf 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -37,9 +37,6 @@ const GRAVITY: f64 = 0.04; const AIR_DRAG: f32 = 0.98; const INITIAL_HORIZONTAL_SPEED: f64 = 0.02; const INITIAL_VERTICAL_SPEED: f32 = 0.2; -const SHORT_FUSE_RANDOM_RANGE_DIVISOR: i32 = 4; -const SHORT_FUSE_MINIMUM_DIVISOR: i32 = 8; -const MIN_SHORT_FUSE_RANDOM_RANGE: i32 = 1; const EXPLOSION_HEIGHT_FRACTION: f32 = 0.0625; const GROUND_HORIZONTAL_DRAG: f64 = 0.7; const GROUND_VERTICAL_BOUNCE: f64 = -0.5; @@ -163,8 +160,6 @@ unsafe impl DowncastType for PrimedTntEntity { impl PrimedTntEntity { /// Default fuse duration in ticks. pub const DEFAULT_FUSE_TIME: i32 = 80; - /// Sentinel used by Vanilla callers for an absent fuse. - pub const NO_FUSE: i32 = -1; /// Creates an unprimed TNT entity for the entity factory. #[must_use] @@ -241,10 +236,7 @@ impl PrimedTntEntity { /// Returns Vanilla's shortened chain-reaction fuse for `fuse`. #[must_use] pub fn get_random_short_fuse(world: &World, fuse: i32) -> i32 { - let random_range = - (fuse / SHORT_FUSE_RANDOM_RANGE_DIVISOR).max(MIN_SHORT_FUSE_RANDOM_RANGE); - world.with_random(|random| random.next_i32_bounded(random_range)) - + fuse / SHORT_FUSE_MINIMUM_DIVISOR + world.with_random(|random| random.next_i32_bounded((fuse / 4).max(1))) + fuse / 8 } fn owner_reference(&self) -> Option { diff --git a/steel-core/src/entity/mod.rs b/steel-core/src/entity/mod.rs index 11f5b15b2b2..a86610fe3fe 100644 --- a/steel-core/src/entity/mod.rs +++ b/steel-core/src/entity/mod.rs @@ -12,7 +12,6 @@ use rand::{SeedableRng as _, rngs::StdRng}; use rustc_hash::FxHashSet; use simdnbt::borrow::NbtCompound as BorrowedNbtCompoundView; use simdnbt::owned::{NbtCompound, NbtList, NbtTag}; -use smallvec::SmallVec; use steel_math::wrap_degrees; use steel_protocol::packets::game::{ AnimateAction, AttributeSnapshot, CAnimate, CDamageEvent, CEntityEvent, CHurtAnimation, @@ -301,44 +300,6 @@ enum BlockEffectSegmentResult { Removed, } -/// Deduplicates block and fluid effects across every movement segment processed for one entity -/// tick. Each segment's geometric sweep separately deduplicates its candidate positions. -enum VisitedBlockPositions { - Inline(SmallVec<[BlockPos; 8]>), - Hashed(FxHashSet), -} - -impl Default for VisitedBlockPositions { - fn default() -> Self { - Self::Inline(SmallVec::new()) - } -} - -impl VisitedBlockPositions { - fn insert(&mut self, pos: BlockPos) -> bool { - match self { - Self::Hashed(visited) => visited.insert(pos), - Self::Inline(inline) => { - if inline.contains(&pos) { - return false; - } - if inline.len() < inline.inline_size() { - inline.push(pos); - return true; - } - - let mut visited = FxHashSet::default(); - visited.reserve(inline.len() + 1); - visited.extend(inline.drain(..)); - let inserted = visited.insert(pos); - debug_assert!(inserted, "inline duplicate check must precede the fallback"); - *self = Self::Hashed(visited); - true - } - } - } -} - #[derive(Debug, Clone, Copy)] struct BlockEffectFireSnapshot { was_on_fire: bool, @@ -549,7 +510,7 @@ fn apply_block_effect_segment( to: DVec3, max_iterations: i32, effect_collector: &mut InsideBlockEffectCollector, - visited_blocks: &mut VisitedBlockPositions, + visited_blocks: &mut block_effects::VisitedBlockPositions, ) -> BlockEffectSegmentResult { let aabb = entity.make_bounding_box_at(to).deflate(1.0E-5); if aabb.is_empty() { @@ -686,7 +647,8 @@ fn apply_effects_from_block_movements(entity: &dyn Entity, movements: &[EntityMo apply_step_on_block(entity, &world); - let mut visited_blocks = VisitedBlockPositions::default(); + // Deduplicates effects across every movement segment of this tick. + let mut visited_blocks = block_effects::VisitedBlockPositions::default(); let mut effect_collector = InsideBlockEffectCollector::new(); let before_effects = BlockEffectFireSnapshot::from_entity(entity); for movement in movements.iter().copied() { diff --git a/steel-core/src/physics/collision.rs b/steel-core/src/physics/collision.rs index 4bf90b05df6..fcfc051490a 100644 --- a/steel-core/src/physics/collision.rs +++ b/steel-core/src/physics/collision.rs @@ -195,9 +195,7 @@ impl BlockCollisionSearchBounds { self, mut visit: impl FnMut(BlockPos, CollisionCursorType) -> ControlFlow, ) -> ControlFlow { - // Vanilla's Cursor3D advances X first, then Y, then Z. Collision behavior can be - // extensible, so retain that callback order even though the final shape set is usually - // insensitive to traversal order. + // Keep Vanilla's X, then Y, then Z callback order for extensible collision behavior. for z in self.min_z..=self.max_z { for y in self.min_y..=self.max_y { for x in self.min_x..=self.max_x { diff --git a/steel-core/src/world/explosion/server/block_effects.rs b/steel-core/src/world/explosion/server/block_effects.rs index 735835a74d9..c7cb9994332 100644 --- a/steel-core/src/world/explosion/server/block_effects.rs +++ b/steel-core/src/world/explosion/server/block_effects.rs @@ -18,9 +18,7 @@ const MAX_DROPS_PER_COMBINED_STACK: i32 = 16; impl ServerExplosion<'_> { pub(super) fn interact_with_blocks(&self, affected: &mut [BlockPos]) { - self.world.with_random(|random| { - vanilla_shuffle(affected, |bound| random.next_i32_bounded(bound)); - }); + self.world.with_random(|random| random.shuffle(affected)); let mut stacks = Vec::new(); let mut full_chunks = LocalFullChunkHolderCache::new(); @@ -63,16 +61,6 @@ impl ServerExplosion<'_> { } } -fn vanilla_shuffle(values: &mut [T], mut next_index: impl FnMut(i32) -> i32) { - let Ok(length) = i32::try_from(values.len()) else { - return; - }; - for remaining in (2..=length).rev() { - let swap_index = next_index(remaining) as usize; - values.swap(remaining as usize - 1, swap_index); - } -} - struct StackCollector { pos: BlockPos, stack: ItemStack, diff --git a/steel-core/src/world/explosion/server/block_rays/cache.rs b/steel-core/src/world/explosion/server/block_rays/cache.rs index 7b4b3eadb6a..34d6ab86a12 100644 --- a/steel-core/src/world/explosion/server/block_rays/cache.rs +++ b/steel-core/src/world/explosion/server/block_rays/cache.rs @@ -1,7 +1,5 @@ //! Bounded block-ray caches for immutable explosion calculators. -use std::mem; - use steel_registry::blocks::block_state_ext::BlockStateExt as _; use steel_utils::{BlockPos, BlockStateId, PackedBlockPos}; @@ -23,7 +21,6 @@ const EMPTY_DENSE_BLOCK_CACHE_SLOT: u16 = u16::MAX; const DENSE_BLOCK_CACHE_HAS_RESISTANCE: u8 = 1; const DENSE_BLOCK_CACHE_AFFECTED: u8 = 1 << 1; const F64_INTEGER_MANTISSA_BIAS: f64 = 6_755_399_441_055_744.0; -const DENSE_BLOCK_CACHE_ENTRY_SIZE_BYTES: usize = 8; const LONG_HASH_PHI: u64 = 0x9e37_79b9_7f4a_7c15; #[derive(Clone, Copy)] @@ -49,18 +46,13 @@ pub(super) struct ExplosionBlockCache { entries: [ExplosionBlockCacheEntry; BLOCK_CACHE_SIZE], } -#[repr(C)] #[derive(Clone, Copy)] struct DenseExplosionBlockCacheEntry { resistance: f32, state: BlockStateId, flags: u8, - _padding: u8, } -const _: [(); DENSE_BLOCK_CACHE_ENTRY_SIZE_BYTES] = - [(); mem::size_of::()]; - pub(super) struct DenseExplosionBlockCache { min: BlockPos, size_x: usize, @@ -235,7 +227,6 @@ impl ExplosionRayBlockCache for DenseExplosionBlockCache { resistance, state, flags, - _padding: 0, }); self.slots[slot_index] = entry_index as u16; entry_index diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs index 6aca8a9a345..65d033974ee 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs @@ -9,7 +9,6 @@ const JAVA_HASH_MAP_TREEIFY_THRESHOLD: usize = 8; const JAVA_HASH_MAP_MIN_TREEIFY_CAPACITY: usize = 64; const JAVA_HASH_MAP_LOAD_FACTOR_NUMERATOR: usize = 3; const JAVA_HASH_MAP_LOAD_FACTOR_DENOMINATOR: usize = 4; -const JAVA_BLOCK_POS_HASH_MULTIPLIER: i32 = 31; const JAVA_HASH_MAP_SPREAD_SHIFT: u32 = 16; const JAVA_BLOCK_POS_SET_EMPTY_INDEX: u32 = u32::MAX; @@ -149,11 +148,7 @@ impl IntoIterator for JavaBlockPosSet { } const fn java_block_pos_bucket(pos: BlockPos, capacity: usize) -> usize { - let hash = pos - .y() - .wrapping_add(pos.z().wrapping_mul(JAVA_BLOCK_POS_HASH_MULTIPLIER)) - .wrapping_mul(JAVA_BLOCK_POS_HASH_MULTIPLIER) - .wrapping_add(pos.x()) as u32; + let hash = pos.java_hash_code() as u32; let spread = hash ^ (hash >> JAVA_HASH_MAP_SPREAD_SHIFT); spread as usize & (capacity - 1) } diff --git a/steel-utils/src/random/mod.rs b/steel-utils/src/random/mod.rs index 9429934c456..94efa9ab17b 100644 --- a/steel-utils/src/random/mod.rs +++ b/steel-utils/src/random/mod.rs @@ -62,6 +62,17 @@ pub trait Random { self.next_i32(); } } + + /// Vanilla `Util.shuffle`: one descending bounded draw per swapped position. + fn shuffle(&mut self, items: &mut [T]) { + let Ok(size) = i32::try_from(items.len()) else { + panic!("shuffle length {} exceeds Java's int range", items.len()); + }; + for remaining in (2..=size).rev() { + let swap_to = self.next_i32_bounded(remaining) as usize; + items.swap(remaining as usize - 1, swap_to); + } + } } /// A trait for positional random number generators. diff --git a/steel-utils/src/types/position.rs b/steel-utils/src/types/position.rs index afda88a054a..f5bd016e51a 100644 --- a/steel-utils/src/types/position.rs +++ b/steel-utils/src/types/position.rs @@ -233,6 +233,15 @@ impl BlockPos { self.0.z } + /// Returns vanilla `Vec3i.hashCode`, which orders Java `HashSet` iteration. + #[must_use] + pub const fn java_hash_code(&self) -> i32 { + self.y() + .wrapping_add(self.z().wrapping_mul(31)) + .wrapping_mul(31) + .wrapping_add(self.x()) + } + /// Returns the position one block above (Y + 1). #[must_use] pub const fn above(&self) -> Self { From ceab24d8b5c8e09695f51b5af8433bc6ae04dc5f Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Wed, 7 Oct 2026 16:26:49 +0000 Subject: [PATCH 03/26] perf: Build stable-air occupancy from section palettes Recounting a full overworld chunk took 174 us with occupancy tracking, against 15 us without it, because every position made three registry lookups. Classifying each palette entry once and checking the cached air flag first brings it to 28 us (overworld_recalculate_counts, 3 runs each). The certificate contents are unchanged. --- steel-core/src/chunk/section.rs | 48 +++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/steel-core/src/chunk/section.rs b/steel-core/src/chunk/section.rs index b5679e0ff28..f08f2b8f5d7 100644 --- a/steel-core/src/chunk/section.rs +++ b/steel-core/src/chunk/section.rs @@ -10,6 +10,7 @@ use std::{ }; use parking_lot::{RwLockReadGuard, RwLockWriteGuard}; +use smallvec::SmallVec; use steel_registry::blocks::block_state_ext::BlockStateExt; use steel_registry::{REGISTRY, RegistryEntry}; use steel_registry::{vanilla_biomes, vanilla_blocks}; @@ -297,28 +298,39 @@ enum StableAirOccupancy { impl StableAirOccupancy { fn from_palette(states: &BlockPalette) -> Self { - let has_stable_air = states.maybe_has(is_stable_vanilla_air); - if !has_stable_air { + let data = match states { + BlockPalette::Homogeneous(state) if is_stable_vanilla_air(*state) => { + return Self::Empty; + } + BlockPalette::Homogeneous(_) => return Self::Full, + BlockPalette::Heterogeneous(data) => data, + BlockPalette::Building(_) => return Self::Unknown, + }; + // Classify each palette entry once instead of every block position. + let mut air_states = SmallVec::<[BlockStateId; 3]>::new(); + let mut occupied_count = 0_u16; + for &(state, count) in &data.palette { + if is_stable_vanilla_air(state) { + air_states.push(state); + } else { + occupied_count += count; + } + } + if air_states.is_empty() { return Self::Full; } - if !states.maybe_has(|state| !is_stable_vanilla_air(state)) { + if occupied_count == 0 { return Self::Empty; } let mut rows = Box::new([0; STABLE_AIR_OCCUPANCY_ROW_COUNT]); - let mut occupied_count = 0_u16; - for index in 0..BlockPalette::VOLUME { - if is_stable_vanilla_air(states.get_at_index(index)) { - continue; + // Palette cubes iterate in Y, Z, X order, so each run of 16 values is one row. + for (index, state) in data.iter_values().enumerate() { + if !air_states.contains(state) { + rows[index / BlockPalette::SIZE] |= 1_u16 << (index % BlockPalette::SIZE); } - let x = index % BlockPalette::SIZE; - let yz_index = index / BlockPalette::SIZE; - let z = yz_index % BlockPalette::SIZE; - let y = yz_index / BlockPalette::SIZE; - rows[y * BlockPalette::SIZE + z] |= 1_u16 << x; - occupied_count += 1; } - debug_assert!(occupied_count > 0 && occupied_count < BLOCKS_PER_SECTION); + debug_assert!(occupied_count < BLOCKS_PER_SECTION); Self::Mixed { rows, occupied_count, @@ -416,9 +428,11 @@ impl StableAirOccupancy { } fn is_stable_vanilla_air(state: BlockStateId) -> bool { - state == vanilla_blocks::AIR.default_state() - || state == vanilla_blocks::CAVE_AIR.default_state() - || state == vanilla_blocks::VOID_AIR.default_state() + // The cached air flag avoids three registry lookups for the common non-air state. + state.is_air() + && (state == vanilla_blocks::AIR.default_state() + || state == vanilla_blocks::CAVE_AIR.default_state() + || state == vanilla_blocks::VOID_AIR.default_state()) } impl Sections { From 1dee124fb71db720c3843a0abe863097a3169415 Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Wed, 7 Oct 2026 20:28:00 +0000 Subject: [PATCH 04/26] refactor: Simplify explosion code --- .../entities/objects/explosives/primed_tnt.rs | 26 ++------ steel-core/src/entity/manager/mod.rs | 33 ++++------ steel-core/src/physics/collision.rs | 66 +++---------------- steel-core/src/world/explosion/mod.rs | 9 +-- steel-core/src/world/explosion/server.rs | 5 -- .../src/world/explosion/server/block_rays.rs | 6 -- .../explosion/server/block_rays/cache.rs | 13 ++-- .../explosion/server/block_rays/tests.rs | 62 ++++++++++++++++- .../world/explosion/server/entity_effects.rs | 17 +++-- .../src/world/explosion/server/exposure.rs | 59 ++++++----------- .../src/world/explosion/server/tests.rs | 2 + 11 files changed, 123 insertions(+), 175 deletions(-) diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs index 49b91fc54cf..47ec04236d4 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -109,13 +109,7 @@ impl ImmutableExplosionBlockCalculator for NetherPortalPreservingExplosionDamage if state.get_block() == &vanilla_blocks::NETHER_PORTAL { return None; } - ::explosion_resistance( - &DefaultExplosionDamageCalculator, - reader, - pos, - state, - fluid, - ) + DefaultExplosionDamageCalculator.explosion_resistance(reader, pos, state, fluid) } fn should_explode( @@ -128,20 +122,12 @@ impl ImmutableExplosionBlockCalculator for NetherPortalPreservingExplosionDamage if state.get_block() == &vanilla_blocks::NETHER_PORTAL { return false; } - ::should_explode( - &DefaultExplosionDamageCalculator, - reader, - pos, - state, - power, - ) + DefaultExplosionDamageCalculator.should_explode(reader, pos, state, power) } } static USED_PORTAL_DAMAGE_CALCULATOR: NetherPortalPreservingExplosionDamageCalculator = NetherPortalPreservingExplosionDamageCalculator; -static DEFAULT_TNT_BLOCK_CALCULATOR: DefaultExplosionDamageCalculator = - DefaultExplosionDamageCalculator; /// Vanilla primed TNT entity. #[entity_behavior(class = "PrimedTnt")] @@ -268,13 +254,11 @@ impl PrimedTntEntity { }; let mut options = ExplosionOptions::new(center, explosion_power, ExplosionInteraction::Tnt); options.source = Some(Arc::clone(self) as SharedEntity); - options.immutable_block_calculator = Some(if used_portal { - &USED_PORTAL_DAMAGE_CALCULATOR - } else { - &DEFAULT_TNT_BLOCK_CALCULATOR - }); if used_portal { options.damage_calculator = Some(&USED_PORTAL_DAMAGE_CALCULATOR); + options.immutable_block_calculator = Some(&USED_PORTAL_DAMAGE_CALCULATOR); + } else { + options.immutable_block_calculator = Some(&DefaultExplosionDamageCalculator); } world.explode(options); } diff --git a/steel-core/src/entity/manager/mod.rs b/steel-core/src/entity/manager/mod.rs index 768d6f50616..09469a13e15 100644 --- a/steel-core/src/entity/manager/mod.rs +++ b/steel-core/src/entity/manager/mod.rs @@ -5,10 +5,7 @@ //! of chunk load state; chunks are still the persistence boundary, and only //! full simulated chunks tick entities. -use std::{ - collections::BTreeMap, error::Error, fmt, iter::FusedIterator, mem, ops::ControlFlow, slice, - sync::Arc, -}; +use std::{collections::BTreeMap, error::Error, fmt, iter::FusedIterator, mem, slice, sync::Arc}; use glam::DVec3; use rustc_hash::{FxHashMap, FxHashSet}; @@ -1511,11 +1508,10 @@ impl WorldEntityManager { ) -> SmallVec<[SharedEntity; 8]> { let state = self.state.read(); let mut snapshot = SmallVec::new(); - let _ = Self::visit_entity_query_entries(&state, aabb, candidates, |entry| { + Self::visit_entity_query_entries(&state, aabb, candidates, |entry| { if Self::is_accessible(&state, entry) { snapshot.push(Arc::clone(&entry.entity)); } - ControlFlow::<()>::Continue(()) }); snapshot } @@ -1567,24 +1563,18 @@ impl WorldEntityManager { fn entity_query_entries<'a>(state: &'a ManagerState, aabb: &WorldAabb) -> Vec<&'a EntityEntry> { let mut entries = Vec::new(); - let _ = Self::visit_entity_query_entries( - state, - aabb, - EntityCollisionCandidates::All, - |entry| { - entries.push(entry); - ControlFlow::<()>::Continue(()) - }, - ); + Self::visit_entity_query_entries(state, aabb, EntityCollisionCandidates::All, |entry| { + entries.push(entry); + }); entries } - fn visit_entity_query_entries<'a, B>( + fn visit_entity_query_entries<'a>( state: &'a ManagerState, aabb: &WorldAabb, candidates: EntityCollisionCandidates, - mut visit: impl FnMut(&'a EntityEntry) -> ControlFlow, - ) -> ControlFlow { + mut visit: impl FnMut(&'a EntityEntry), + ) { let bounds = EntitySpatialCellBounds::from_aabb(aabb); let mut populated_cells = SmallVec::<[&OrderedEntityIds; 8]>::new(); let spatial_index = match candidates { @@ -1613,10 +1603,10 @@ impl WorldEntityManager { if let [cell] = populated_cells.as_slice() { for entity_id in cell.iter() { if let Some(entry) = state.live_by_id.get(entity_id) { - visit(entry)?; + visit(entry); } } - return ControlFlow::Continue(()); + return; } let mut entity_ids = FxHashSet::default(); @@ -1629,9 +1619,8 @@ impl WorldEntityManager { .collect::>(); entries.sort_unstable_by_key(|entry| entry.query_order()); for entry in entries { - visit(entry)?; + visit(entry); } - ControlFlow::Continue(()) } fn entity_ids_in_chunk_order(state: &ManagerState, chunk: ChunkPos) -> Vec { diff --git a/steel-core/src/physics/collision.rs b/steel-core/src/physics/collision.rs index fcfc051490a..de3a87484f4 100644 --- a/steel-core/src/physics/collision.rs +++ b/steel-core/src/physics/collision.rs @@ -4,20 +4,15 @@ use std::{ops::ControlFlow, sync::Arc}; use glam::DVec3; use smallvec::SmallVec; -use steel_registry::{ - blocks::{BlockRef, block_state_ext::BlockStateExt}, - vanilla_blocks, vanilla_entities, -}; +use steel_registry::{blocks::block_state_ext::BlockStateExt, vanilla_blocks, vanilla_entities}; use steel_utils::{BlockLocalAabb, BlockPos, BlockStateId, WorldAabb}; use crate::behavior::{ BLOCK_BEHAVIORS, BlockCollisionBoxes, BlockCollisionContext, blocks::PowderSnowBlock, }; use crate::entity::{Entity, EntityCollisionCandidates}; -#[cfg(test)] -use crate::physics::block_has_extensible_collision_behavior; use crate::physics::shapes::join_is_not_empty; -use crate::physics::{COLLISION_EPSILON, block_may_expand_collision_cursor}; +use crate::physics::{COLLISION_EPSILON, block_state_may_expand_collision_cursor}; use crate::world::{BlockRegionBounds, World}; const BLOCK_COLLISION_EPSILON: f64 = 1.0e-7; @@ -276,27 +271,11 @@ struct CollisionShape { fn should_resolve_collision_shape( block_state: BlockStateId, cursor_type: CollisionCursorType, -) -> bool { - should_resolve_collision_shape_for_block( - block_state.get_block(), - block_state - .get_static_collision_shape() - .has_large_collision_shape(), - cursor_type, - ) -} - -fn should_resolve_collision_shape_for_block( - block: BlockRef, - has_large_static_shape: bool, - cursor_type: CollisionCursorType, ) -> bool { match cursor_type { CollisionCursorType::Inside => true, - CollisionCursorType::Face => { - block_may_expand_collision_cursor(block, has_large_static_shape) - } - CollisionCursorType::Edge => block == &vanilla_blocks::MOVING_PISTON, + CollisionCursorType::Face => block_state_may_expand_collision_cursor(block_state), + CollisionCursorType::Edge => block_state.get_block() == &vanilla_blocks::MOVING_PISTON, CollisionCursorType::Corner => false, } } @@ -904,6 +883,7 @@ mod tests { }; use steel_utils::{ChunkPos, Identifier, types::UpdateFlags}; + use crate::physics::block_may_expand_collision_cursor; use crate::{ behavior::init_behaviors, test_support::{fresh_test_world, insert_ready_full_chunk}, @@ -1439,11 +1419,9 @@ mod tests { ); assert!(!PLUGIN_BLOCK.config.dynamic_shape); - assert!(block_has_extensible_collision_behavior(&PLUGIN_BLOCK)); let has_large_static_shape = false; let has_special_colliding_blocks = block_may_expand_collision_cursor(&PLUGIN_BLOCK, has_large_static_shape); - assert!(has_special_colliding_blocks); let bounds = BlockCollisionSearchBounds { min_x: 0, @@ -1461,39 +1439,15 @@ mod tests { BlockPos::new(1, 1, 2), ]; - let mut compatibility_callbacks = Vec::new(); - let _ = bounds.try_for_each_candidate(|pos, cursor_type| { - if fixture_positions.contains(&pos) - && should_resolve_collision_shape_for_block( - &PLUGIN_BLOCK, - has_large_static_shape, - cursor_type, - ) - { - compatibility_callbacks.push(pos); + let mut callbacks = Vec::new(); + let _ = bounds.try_for_each_region_candidate(has_special_colliding_blocks, |pos, _| { + if fixture_positions.contains(&pos) { + callbacks.push(pos); } ControlFlow::<()>::Continue(()) }); - let mut optimized_callbacks = Vec::new(); - let _ = bounds.try_for_each_region_candidate( - has_special_colliding_blocks, - |pos, cursor_type| { - if fixture_positions.contains(&pos) - && should_resolve_collision_shape_for_block( - &PLUGIN_BLOCK, - has_large_static_shape, - cursor_type, - ) - { - optimized_callbacks.push(pos); - } - ControlFlow::<()>::Continue(()) - }, - ); - - assert_eq!(compatibility_callbacks, fixture_positions); - assert_eq!(optimized_callbacks, compatibility_callbacks); + assert_eq!(callbacks, fixture_positions); } #[test] diff --git a/steel-core/src/world/explosion/mod.rs b/steel-core/src/world/explosion/mod.rs index fd4eff9297a..a0badbdb735 100644 --- a/steel-core/src/world/explosion/mod.rs +++ b/steel-core/src/world/explosion/mod.rs @@ -447,14 +447,7 @@ impl World { } else { options.large_explosion_particle }; - #[expect( - clippy::manual_unwrap_or, - reason = "make the protocol saturation behavior explicit" - )] - let packet_block_count = match i32::try_from(affected_block_count) { - Ok(count) => count, - Err(_) => i32::MAX, - }; + let packet_block_count = i32::try_from(affected_block_count).unwrap_or(i32::MAX); self.players.iter_players(|_, player| { if player.position().distance_squared(options.center) < MAX_PACKET_DISTANCE_SQUARED { diff --git a/steel-core/src/world/explosion/server.rs b/steel-core/src/world/explosion/server.rs index a3da6c6e39c..788fccef577 100644 --- a/steel-core/src/world/explosion/server.rs +++ b/steel-core/src/world/explosion/server.rs @@ -24,11 +24,6 @@ mod exposure; const SMALL_EXPLOSION_RADIUS: f32 = 2.0; -#[cfg(test)] -use block_rays::RAY_COUNT; -#[cfg(test)] -use exposure::{EntityExplosionExposure, seen_percent}; - pub(super) struct ServerExplosion<'a> { world: &'a Arc, fire: bool, diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs index 34696546de2..0fcf71454db 100644 --- a/steel-core/src/world/explosion/server/block_rays.rs +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -19,12 +19,6 @@ mod java_block_pos_set; use java_block_pos_set::JavaBlockPosSet; -#[cfg(test)] -use cache::{ - ExplosionBlockCache, ImmutableRayCachePolicy, bounded_floor_to_i32, - visit_immutable_ray_positions_cached, -}; - const RAY_GRID_SIZE: i32 = 16; const RAY_GRID_LAST_INDEX: i32 = RAY_GRID_SIZE - 1; const RAY_GRID_INTERIOR_SIZE: i32 = RAY_GRID_SIZE - 2; diff --git a/steel-core/src/world/explosion/server/block_rays/cache.rs b/steel-core/src/world/explosion/server/block_rays/cache.rs index 34d6ab86a12..42e94e5bef0 100644 --- a/steel-core/src/world/explosion/server/block_rays/cache.rs +++ b/steel-core/src/world/explosion/server/block_rays/cache.rs @@ -436,11 +436,8 @@ pub(super) fn visit_immutable_ray_positions_cached< if remaining_power > 0.0 { let already_affected = cache.affected(cache_index); - let should_explode = if cache_policy.always_allows_block_explosion { - !already_affected - } else { - calculator.should_explode(reader, pos, state, remaining_power) - }; + let should_explode = cache_policy.always_allows_block_explosion + || calculator.should_explode(reader, pos, state, remaining_power); if should_explode && !already_affected { affected.insert(pos); cache.mark_affected(cache_index); @@ -466,10 +463,8 @@ fn ray_block_pos(position: glam::DVec3) -> BlockP } } -/// Floors a finite in-range coordinate without Rust's saturating float-to-int conversion. -/// -/// Adding `1.5 * 2^52` maps every integral binary64 value in the i32 range exactly into the -/// mantissa; its low 32 bits are the integer's two's-complement representation. +/// Floors a finite in-range coordinate. Adding `1.5 * 2^52` places the integer's two's-complement +/// representation in the low 32 mantissa bits, avoiding a saturating float-to-int conversion. #[inline] pub(super) fn bounded_floor_to_i32(value: f64) -> i32 { debug_assert!( diff --git a/steel-core/src/world/explosion/server/block_rays/tests.rs b/steel-core/src/world/explosion/server/block_rays/tests.rs index 5d4a85f5575..4166ac12c4d 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests.rs @@ -4,10 +4,15 @@ use glam::DVec3; use sha2::{Digest as _, Sha256}; use steel_registry::fluid::FluidState; use steel_registry::{init_vanilla_registry, vanilla_blocks}; +use steel_utils::locks::SyncMutex; use steel_utils::random::{Random, legacy_random::LegacyRandom}; use steel_utils::types::UpdateFlags; use steel_utils::{BlockPos, BlockStateId, ChunkPos}; +use super::cache::{ + ExplosionBlockCache, ImmutableRayCachePolicy, bounded_floor_to_i32, + visit_immutable_ray_positions_cached, +}; use super::*; use crate::behavior::init_behaviors; use crate::test_support::{fresh_test_world, insert_ready_full_chunk}; @@ -177,7 +182,6 @@ fn deterministic_empty_world_rays_match_the_java_hash_set_fixture() { fn precomputed_ray_steps_match_java_bit_digest() { let mut digest = FNV1A_64_OFFSET_BASIS; for step in RAY_STEPS.iter() { - assert!(step.x.abs() < 1.0 && step.y.abs() < 1.0 && step.z.abs() < 1.0); for bits in [step.x.to_bits(), step.y.to_bits(), step.z.to_bits()] { for byte in bits.to_le_bytes() { digest ^= u64::from(byte); @@ -383,6 +387,62 @@ fn immutable_rays_match_sequential_lane_in_a_complete_region() { } } +#[test] +fn cached_ray_decisions_keep_uncached_order_at_already_affected_positions() { + struct RecordingCalculator(SyncMutex>); + + impl ImmutableExplosionBlockCalculator for RecordingCalculator { + fn can_cache_explosion_resistance(&self) -> bool { + true + } + + fn should_explode( + &self, + _reader: &dyn ExplosionBlockReader, + pos: BlockPos, + _state: BlockStateId, + power: f32, + ) -> bool { + self.0.lock().push((pos, power.to_bits())); + true + } + } + + init_vanilla_registry(); + let fixture = fresh_test_world("cached_ray_callback_order"); + let world = &fixture.world; + insert_ready_full_chunk(world, ChunkPos::new(0, 0)); + let context = ExplosionRayContext { + center: DVec3::new(8.5, 64.5, 8.5), + bounds: ExplosionWorldBounds::from_world(world), + }; + let rays = [DVec3::X, -DVec3::X].map(|direction| ExplosionRay { + step: direction * RAY_STEP, + initial_power: 1.0, + }); + let uncached = RecordingCalculator(SyncMutex::new(Vec::new())); + let mut expected_affected = JavaBlockPosSet::default(); + for ray in rays { + visit_immutable_ray_positions(ray, context, world.as_ref(), &uncached, |pos| { + expected_affected.insert(pos); + }); + } + assert!( + uncached + .0 + .lock() + .windows(2) + .any(|pair| pair[0].0 == pair[1].0) + ); + + let cached = RecordingCalculator(SyncMutex::new(Vec::new())); + let affected = + calculate_cached_immutable_rays::<_, true>(&rays, context, world.as_ref(), &cached); + + assert_eq!(*cached.0.lock(), *uncached.0.lock()); + assert_eq!(affected, expected_affected.into_iter().collect::>()); +} + #[test] fn bounded_floor_matches_rust_floor_cast_across_i32_domain() { let mut values = vec![ diff --git a/steel-core/src/world/explosion/server/entity_effects.rs b/steel-core/src/world/explosion/server/entity_effects.rs index f96d8922007..1a025837d3d 100644 --- a/steel-core/src/world/explosion/server/entity_effects.rs +++ b/steel-core/src/world/explosion/server/entity_effects.rs @@ -26,18 +26,17 @@ impl ServerExplosion<'_> { return; } - let double_radius = self.radius * DAMAGE_RADIUS_SCALE; - let radius = f64::from(double_radius); + let double_radius = f64::from(self.radius * DAMAGE_RADIUS_SCALE); let bounds = WorldAabb::from_min_max( DVec3::new( - (self.center.x - radius - ENTITY_QUERY_PADDING).floor(), - (self.center.y - radius - ENTITY_QUERY_PADDING).floor(), - (self.center.z - radius - ENTITY_QUERY_PADDING).floor(), + (self.center.x - double_radius - ENTITY_QUERY_PADDING).floor(), + (self.center.y - double_radius - ENTITY_QUERY_PADDING).floor(), + (self.center.z - double_radius - ENTITY_QUERY_PADDING).floor(), ), DVec3::new( - (self.center.x + radius + ENTITY_QUERY_PADDING).floor(), - (self.center.y + radius + ENTITY_QUERY_PADDING).floor(), - (self.center.z + radius + ENTITY_QUERY_PADDING).floor(), + (self.center.x + double_radius + ENTITY_QUERY_PADDING).floor(), + (self.center.y + double_radius + ENTITY_QUERY_PADDING).floor(), + (self.center.z + double_radius + ENTITY_QUERY_PADDING).floor(), ), ); let source_id = self.source.as_ref().map(|source| source.id()); @@ -69,7 +68,7 @@ impl ServerExplosion<'_> { if entity.ignore_explosion(self) { continue; } - let distance = entity.position().distance(self.center) / radius; + let distance = entity.position().distance(self.center) / double_radius; if distance > 1.0 { continue; } diff --git a/steel-core/src/world/explosion/server/exposure.rs b/steel-core/src/world/explosion/server/exposure.rs index bd60b4f856a..85cb62ca6f6 100644 --- a/steel-core/src/world/explosion/server/exposure.rs +++ b/steel-core/src/world/explosion/server/exposure.rs @@ -8,8 +8,6 @@ use crate::behavior::BlockCollisionContext; use crate::behavior::blocks::PowderSnowBlock; use crate::chunk::paletted_container::BlockPalette; use crate::entity::Entity; -#[cfg(test)] -use crate::world::World as ServerWorld; use crate::world::raycast::{ExplosionExposureRaycast, collision_path_axis_block_bounds}; use crate::world::{BlockRegionBounds, MAX_BLOCK_REGION_WORKSET_SLOTS}; @@ -166,46 +164,17 @@ impl EntityExplosionExposure { sample_count } - #[cfg(test)] - #[inline] - fn sample_is_visible(self, world: &ServerWorld, center: DVec3, from: DVec3) -> bool { - world.is_block_collision_path_clear(from, center, self.collision_context) - } - - fn exposure(visible_samples: u32, sample_count: usize) -> f32 { - visible_samples as f32 / sample_count as f32 - } - - #[cfg(test)] - pub(super) fn calculate_uncached(self, world: &ServerWorld, center: DVec3) -> f32 { - if self.has_negative_step() { - return 0.0; - } - - self.calculate_with_visibility(|from| self.sample_is_visible(world, center, from)) - } - pub(super) fn calculate_with_visibility( self, mut is_visible: impl FnMut(DVec3) -> bool, ) -> f32 { - let mut visible_samples = 0; + let mut visible_samples = 0_u32; let sample_count = self.for_each_sample(|from| { if is_visible(from) { visible_samples += 1; } }); - Self::exposure(visible_samples, sample_count) - } - - #[cfg(test)] - fn calculate_cached(self, world: &ServerWorld, center: DVec3) -> f32 { - if self.has_negative_step() { - return 0.0; - } - - let mut raycast = ExplosionExposureRaycast::new(world, self.collision_context); - self.calculate_cached_with(&mut raycast, center) + visible_samples as f32 / sample_count as f32 } pub(super) fn calculate_cached_with( @@ -231,10 +200,24 @@ fn exposure_axis_step(axis_length: f64) -> f64 { } #[cfg(test)] -pub(super) fn seen_percent(world: &ServerWorld, center: DVec3, entity: &dyn Entity) -> f32 { - let exposure = EntityExplosionExposure::capture(entity); - if exposure.has_negative_step() { - return 0.0; +pub(super) mod tests { + use super::*; + use crate::world::World; + + impl EntityExplosionExposure { + pub(in super::super) fn calculate_uncached(self, world: &World, center: DVec3) -> f32 { + if self.has_negative_step() { + return 0.0; + } + self.calculate_with_visibility(|from| { + world.is_block_collision_path_clear(from, center, self.collision_context) + }) + } + } + + pub(in super::super) fn seen_percent(world: &World, center: DVec3, entity: &dyn Entity) -> f32 { + let exposure = EntityExplosionExposure::capture(entity); + let mut raycast = ExplosionExposureRaycast::new(world, exposure.collision_context); + exposure.calculate_cached_with(&mut raycast, center) } - exposure.calculate_cached(world, center) } diff --git a/steel-core/src/world/explosion/server/tests.rs b/steel-core/src/world/explosion/server/tests.rs index e1fc79965e4..dee234aaf66 100644 --- a/steel-core/src/world/explosion/server/tests.rs +++ b/steel-core/src/world/explosion/server/tests.rs @@ -17,6 +17,8 @@ use steel_utils::{ WorldAabb, }; +use super::block_rays::RAY_COUNT; +use super::exposure::{EntityExplosionExposure, tests::seen_percent}; use super::*; use crate::behavior::{BlockCollisionContext, init_behaviors}; use crate::block_entity::{ From dfc06c9366e27cd1bd50d913221d79709ca7aad7 Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Sat, 10 Oct 2026 23:11:06 +0000 Subject: [PATCH 05/26] fix: preserve Java tree-bin order for explosions Implement linked tree-bin ordering through insertion, root movement and resize transitions. Verify against OpenJDK 25 and captured radius-4 TNT iteration, callbacks and per-position fuses. Document JVM-dependent equal-hash ties without changing RNG policy. Cover the visited-position storage transition from 16 to 17 unique positions. --- steel-core/src/entity/block_effects.rs | 26 +- .../server/block_rays/java_block_pos_set.rs | 157 ++-- .../block_rays/java_block_pos_set/tests.rs | 137 ++++ .../tests/HashSetOracle.java | 82 ++ .../tests/openjdk25-sequential-identity.txt | 184 +++++ .../java_block_pos_set/tests/openjdk25.txt | 735 ++++++++++++++++++ .../block_rays/java_block_pos_set/tree.rs | 257 ++++++ .../explosion/server/block_rays/tests.rs | 2 + .../server/block_rays/tests/gameplay.rs | 164 ++++ .../block_rays/tests/vanilla-tnt-order.txt | 160 ++++ 10 files changed, 1832 insertions(+), 72 deletions(-) create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt create mode 100644 steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs create mode 100644 steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index 8412e50412a..10d5101fbb4 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -11,7 +11,7 @@ const CLIP_EPSILON: f64 = 1.0e-7; const CORNER_HIT_EPSILON: f64 = 1.0e-5; const ENTITY_INSIDE_SWEEP_INFLATE_EPSILON: f64 = 1.0e-7; -/// Deduplicates block positions inline, switching to a hash set for unusually large sweeps. +/// Deduplicates up to 16 positions without allocating, then switches to a hash set. pub(super) enum VisitedBlockPositions { Inline(SmallVec<[BlockPos; 16]>), Hashed(FxHashSet), @@ -522,6 +522,30 @@ fn sign_i32(value: f64) -> i32 { mod tests { use super::*; + #[test] + fn visited_positions_stay_inline_through_sixteen_and_preserve_duplicates_after_seventeen() { + let mut visited = VisitedBlockPositions::default(); + for x in 0..16 { + assert!(visited.insert(BlockPos::new(x, 64, 0))); + } + for x in 0..16 { + assert!(!visited.insert(BlockPos::new(x, 64, 0))); + } + let VisitedBlockPositions::Inline(inline) = &visited else { + panic!("sixteen unique positions must remain inline"); + }; + assert!(!inline.spilled()); + + assert!(visited.insert(BlockPos::new(16, 64, 0))); + let VisitedBlockPositions::Hashed(hashed) = &visited else { + panic!("the seventeenth unique position must use the hash set"); + }; + assert_eq!(hashed.len(), 17); + for x in 0..17 { + assert!(!visited.insert(BlockPos::new(x, 64, 0))); + } + } + fn visited_positions(from: DVec3, to: DVec3, aabb_at_target: WorldAabb) -> Vec { let mut positions = Vec::new(); assert!( diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs index 65d033974ee..afc8bd88191 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs @@ -1,94 +1,104 @@ //! Explosion-local emulation of Java `HashSet` iteration order. +//! +//! Distinct hashes follow `OpenJDK` 25's linked tree-bin order. `BlockPos` inherits +//! `Comparable`, so equal-hash ties use JVM identities. Here ties use insertion +//! indices, matching increasing JVM identities for plain `BlockPos` keys. Different +//! identities can also change other entries' order in that bin and its later splits. use std::mem; use std::vec::IntoIter; +use rustc_hash::FxHashMap; use steel_utils::BlockPos; -const JAVA_HASH_MAP_TREEIFY_THRESHOLD: usize = 8; -const JAVA_HASH_MAP_MIN_TREEIFY_CAPACITY: usize = 64; -const JAVA_HASH_MAP_LOAD_FACTOR_NUMERATOR: usize = 3; -const JAVA_HASH_MAP_LOAD_FACTOR_DENOMINATOR: usize = 4; -const JAVA_HASH_MAP_SPREAD_SHIFT: u32 = 16; -const JAVA_BLOCK_POS_SET_EMPTY_INDEX: u32 = u32::MAX; +#[cfg(test)] +mod tests; +mod tree; + +const EMPTY: u32 = u32::MAX; +const TREEIFY_THRESHOLD: usize = 8; +const MIN_TREEIFY_CAPACITY: usize = 64; #[derive(Default)] pub(super) struct JavaBlockPosSet { - buckets: Vec, - entries: Vec, + buckets: Vec, + entries: Vec, + // Allocate tree links only for entries that have actually entered a tree bin. + trees: FxHashMap, } #[derive(Clone, Copy)] -struct JavaBlockPosBucket { +struct Bucket { head: u32, + // EMPTY marks a tree bin; empty list bins use a dummy tail and an EMPTY head. tail: u32, } -impl JavaBlockPosBucket { +impl Bucket { const EMPTY: Self = Self { - head: JAVA_BLOCK_POS_SET_EMPTY_INDEX, - tail: JAVA_BLOCK_POS_SET_EMPTY_INDEX, + head: EMPTY, + tail: 0, }; + + const fn is_tree(self) -> bool { + self.tail == EMPTY + } } -struct JavaBlockPosEntry { +struct Entry { pos: BlockPos, next: u32, } impl JavaBlockPosSet { + #[inline] pub(super) fn insert(&mut self, pos: BlockPos) -> bool { if self.buckets.is_empty() { - self.buckets.resize(16, JavaBlockPosBucket::EMPTY); + self.buckets.resize(16, Bucket::EMPTY); self.entries.reserve(16); } - let index = java_block_pos_bucket(pos, self.buckets.len()); + let hash = spread(pos); + let index = hash as u32 as usize & (self.buckets.len() - 1); let bucket = self.buckets[index]; - let mut current = bucket.head; let mut bin_len = 0; - while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { - let entry = &self.entries[current as usize]; - if entry.pos == pos { + if bucket.is_tree() { + if self.find_in_tree(bucket.head, hash, pos) { return false; } - current = entry.next; - bin_len += 1; + } else { + let mut current = bucket.head; + while current != EMPTY { + let entry = &self.entries[current as usize]; + if entry.pos == pos { + return false; + } + current = entry.next; + bin_len += 1; + } } let Ok(entry_index) = u32::try_from(self.entries.len()) else { panic!("JavaBlockPosSet entry arena exceeded its u32 index space"); }; assert_ne!( - entry_index, JAVA_BLOCK_POS_SET_EMPTY_INDEX, + entry_index, EMPTY, "JavaBlockPosSet entry arena exhausted its u32 index space" ); - self.entries.push(JavaBlockPosEntry { - pos, - next: JAVA_BLOCK_POS_SET_EMPTY_INDEX, - }); - if bucket.tail == JAVA_BLOCK_POS_SET_EMPTY_INDEX { - self.buckets[index] = JavaBlockPosBucket { - head: entry_index, - tail: entry_index, - }; + self.entries.push(Entry { pos, next: EMPTY }); + if bucket.is_tree() { + self.insert_tree(index, entry_index); } else { - self.entries[bucket.tail as usize].next = entry_index; - self.buckets[index].tail = entry_index; - } - - // HashMap attempts to treeify after adding a ninth entry to one bin, but grows the - // table instead while its capacity is below 64. That split changes iteration order. - if self.buckets.len() < JAVA_HASH_MAP_MIN_TREEIFY_CAPACITY - && bin_len >= JAVA_HASH_MAP_TREEIFY_THRESHOLD - { - self.resize(); + self.append(index, entry_index); + // Java treeifies when adding the ninth bin entry. + if bin_len >= TREEIFY_THRESHOLD { + if self.buckets.len() < MIN_TREEIFY_CAPACITY { + self.resize(); + } else { + self.treeify(index); + } + } } - // Steel intentionally keeps list bins at larger capacities. HashMap tree-bin order can - // depend on JVM identity hashes and is not a reproducible Vanilla ordering contract. - if self.entries.len() - > self.buckets.len() * JAVA_HASH_MAP_LOAD_FACTOR_NUMERATOR - / JAVA_HASH_MAP_LOAD_FACTOR_DENOMINATOR - { + if self.entries.len() > self.buckets.len() * 3 / 4 { self.resize(); } true @@ -99,30 +109,36 @@ impl JavaBlockPosSet { self.buckets.len() } + fn append(&mut self, bucket_index: usize, entry_index: u32) { + debug_assert!(!self.buckets[bucket_index].is_tree()); + let bucket = &mut self.buckets[bucket_index]; + self.entries[entry_index as usize].next = EMPTY; + if bucket.head == EMPTY { + bucket.head = entry_index; + } else { + self.entries[bucket.tail as usize].next = entry_index; + } + bucket.tail = entry_index; + } + fn resize(&mut self) { - let new_capacity = self.buckets.len().saturating_mul(2); - if new_capacity == self.buckets.len() { + let old_capacity = self.buckets.len(); + // HashMap stops growing at MAXIMUM_CAPACITY. + if old_capacity >= 1 << 30 { return; } - let resized = vec![JavaBlockPosBucket::EMPTY; new_capacity]; - let old_buckets = mem::replace(&mut self.buckets, resized); - for bucket in old_buckets { + let old_buckets = mem::replace(&mut self.buckets, vec![Bucket::EMPTY; old_capacity * 2]); + for (index, bucket) in old_buckets.into_iter().enumerate() { + if bucket.is_tree() { + self.split_tree(index, bucket.head, old_capacity); + continue; + } let mut current = bucket.head; - while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { - let entry_index = current as usize; - let next = self.entries[entry_index].next; - let index = java_block_pos_bucket(self.entries[entry_index].pos, new_capacity); - let new_bucket = self.buckets[index]; - self.entries[entry_index].next = JAVA_BLOCK_POS_SET_EMPTY_INDEX; - if new_bucket.tail == JAVA_BLOCK_POS_SET_EMPTY_INDEX { - self.buckets[index] = JavaBlockPosBucket { - head: current, - tail: current, - }; - } else { - self.entries[new_bucket.tail as usize].next = current; - self.buckets[index].tail = current; - } + while current != EMPTY { + let next = self.entries[current as usize].next; + let target = spread(self.entries[current as usize].pos) as u32 as usize + & (self.buckets.len() - 1); + self.append(target, current); current = next; } } @@ -137,7 +153,7 @@ impl IntoIterator for JavaBlockPosSet { let mut ordered = Vec::with_capacity(self.entries.len()); for bucket in self.buckets { let mut current = bucket.head; - while current != JAVA_BLOCK_POS_SET_EMPTY_INDEX { + while current != EMPTY { let entry = &self.entries[current as usize]; ordered.push(entry.pos); current = entry.next; @@ -147,8 +163,7 @@ impl IntoIterator for JavaBlockPosSet { } } -const fn java_block_pos_bucket(pos: BlockPos, capacity: usize) -> usize { +const fn spread(pos: BlockPos) -> i32 { let hash = pos.java_hash_code() as u32; - let spread = hash ^ (hash >> JAVA_HASH_MAP_SPREAD_SHIFT); - spread as usize & (capacity - 1) + (hash ^ (hash >> 16)) as i32 } diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs new file mode 100644 index 00000000000..e063cf85f38 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs @@ -0,0 +1,137 @@ +use std::collections::BTreeSet; +use std::fmt::Write as _; + +use sha2::{Digest, Sha256}; + +use super::*; + +#[test] +fn linked_order_matches_openjdk_25_after_each_insertion_and_resize() { + check_oracle(include_str!("tests/openjdk25.txt")); + check_oracle(include_str!("tests/openjdk25-sequential-identity.txt")); +} + +fn check_oracle(oracle: &str) { + let mut set = JavaBlockPosSet::default(); + let mut case = ""; + let mut saw_tree = false; + for line in oracle.lines() { + if let Some(name) = line.strip_prefix("case ") { + case = name; + set = JavaBlockPosSet::default(); + continue; + } + let fields = line.split_whitespace().collect::>(); + let pos = BlockPos::new( + fields[0].parse().expect("oracle coordinate"), + fields[1].parse().expect("oracle coordinate"), + fields[2].parse().expect("oracle coordinate"), + ); + assert_eq!(set.insert(pos), fields[3] == "true", "{case}: {pos:?}"); + assert_eq!( + set.bucket_count(), + fields[4].parse::().expect("oracle count"), + "{case}: {pos:?}" + ); + let trees = set.buckets.iter().filter(|bucket| bucket.is_tree()).count(); + assert_eq!( + trees, + fields[5].parse::().expect("oracle count"), + "{case}: {pos:?}" + ); + saw_tree |= trees != 0; + check_links(&set); + let mut digest = Sha256::new(); + for bucket in &set.buckets { + let mut node = bucket.head; + while node != EMPTY { + let entry = &set.entries[node as usize]; + digest.update(format!( + "{} {} {}\n", + entry.pos.x(), + entry.pos.y(), + entry.pos.z() + )); + node = entry.next; + } + } + let mut actual = String::new(); + for byte in digest.finalize() { + write!(actual, "{byte:02x}").expect("string write"); + } + assert_eq!(actual, fields[6], "{case}: {pos:?}"); + let mut shape = String::new(); + for (index, bucket) in set.buckets.iter().enumerate() { + if bucket.is_tree() { + write!(shape, "{index}:").expect("string write"); + tree_shape(&set, bucket.head, &mut shape); + } + } + let mut actual_shape = String::new(); + for byte in Sha256::digest(shape.as_bytes()) { + write!(actual_shape, "{byte:02x}").expect("string write"); + } + assert_eq!(actual_shape, fields[7], "{case}: {pos:?} tree shape"); + } + assert!(saw_tree, "the oracle must exercise actual tree bins"); +} + +fn check_links(set: &JavaBlockPosSet) { + let mut all = BTreeSet::new(); + for bucket in &set.buckets { + let mut list = BTreeSet::new(); + let mut node = bucket.head; + let mut prev = EMPTY; + while node != EMPTY { + assert!(all.insert(node), "list cycle or repeated bucket entry"); + list.insert(node); + if bucket.is_tree() { + assert_eq!(set.links(node).prev, prev, "broken reverse iteration link"); + } + prev = node; + node = set.entries[node as usize].next; + } + if !bucket.is_tree() { + continue; + } + assert_eq!(set.links(bucket.head).parent, EMPTY, "head is not the root"); + let mut tree = BTreeSet::new(); + let mut pending = vec![bucket.head]; + while let Some(node) = pending.pop() { + assert!(tree.insert(node), "tree cycle or repeated child"); + let links = set.links(node); + for child in [links.left, links.right] { + if child != EMPTY { + assert_eq!(set.links(child).parent, node, "broken parent link"); + pending.push(child); + } + } + } + assert_eq!( + tree, list, + "tree and iteration list contain different entries" + ); + } + assert_eq!(all.len(), set.entries.len(), "unreachable set entry"); +} + +fn tree_shape(set: &JavaBlockPosSet, node: u32, out: &mut String) { + if node == EMPTY { + out.push('.'); + return; + } + let pos = set.entries[node as usize].pos; + let links = set.links(node); + write!( + out, + "({},{},{},{}", + pos.x(), + pos.y(), + pos.z(), + if links.red { 'r' } else { 'b' } + ) + .expect("string write"); + tree_shape(set, links.left, out); + tree_shape(set, links.right, out); + out.push(')'); +} diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java new file mode 100644 index 00000000000..48ba1cab42b --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java @@ -0,0 +1,82 @@ +// Run with Minecraft 26.2's server/library classpath, OpenJDK 25 and +// --add-opens java.base/java.util=ALL-UNNAMED. Collection oracle, not gameplay. +// The --sequential-identities mode also requires +// -XX:+UnlockExperimentalVMOptions -XX:hashCode=3. +import java.lang.reflect.*; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.*; +import net.minecraft.core.BlockPos; + +public class HashSetOracle { + static Field map, table, key, left, right, red; + static boolean sequentialIdentities; + static int previousIdentity; + static HashSet set; + static void start(String name) {System.out.println("case " + name);set=new HashSet<>();} + static void add(int spread) throws Exception { + // Invert HashMap.hash to choose a signed spread hash, using real BlockPos. + add(new BlockPos(spread^(spread>>>16), 0, 0)); + } + static void add(BlockPos pos) throws Exception { + if(sequentialIdentities) { + int identity=System.identityHashCode(pos); + if(identity<=previousIdentity)throw new IllegalStateException("sequential identity hashes required: use -XX:hashCode=3"); + previousIdentity=identity; + } + boolean added=set.add(pos); + Object[] bins=(Object[])table.get(map.get(set)); + long trees=Arrays.stream(bins).filter(b->b!=null&&b.getClass().getSimpleName().equals("TreeNode")).count(); + StringBuilder order=new StringBuilder(); + for(BlockPos p:set) order.append(p.getX()).append(' ').append(p.getY()).append(' ').append(p.getZ()).append('\n'); + String digest=HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(order.toString().getBytes(StandardCharsets.UTF_8))); + StringBuilder shape=new StringBuilder(); + for(int i=0;i0&&!sequentialIdentities)throw new IllegalArgumentException("expected --sequential-identities"); + map=HashSet.class.getDeclaredField("map");map.setAccessible(true); + key=Class.forName("java.util.HashMap$Node").getDeclaredField("key");key.setAccessible(true); + Class tree=Class.forName("java.util.HashMap$TreeNode"); + left=tree.getDeclaredField("left");left.setAccessible(true);right=tree.getDeclaredField("right");right.setAccessible(true);red=tree.getDeclaredField("red");red.setAccessible(true); + table=HashMap.class.getDeclaredField("table");table.setAccessible(true); + if(sequentialIdentities) { + start("equal-hash-sequential-identities"); + for(int z=0;z<40;z++)add(new BlockPos(-961*z,0,z)); + for(int z=0;z<40;z++)add(new BlockPos(-961*z,0,z)); + start("equal-hash-rebuild-after-split");warmup(); + for(int z=0;z<12;z++)add(new BlockPos(-961*z,0,z)); + for(int i=0;i<8;i++)add(64+i*128); + fillUntil(49);add(new BlockPos(-961*12,0,12)); + fillUntil(97);add(new BlockPos(-961*13,0,13)); + return; + } + start("control-initial-sixteen");for(int h=0;h<12;h++)add(h);add(3); + start("control-resize-without-trees");for(int h=0;h<130;h++)add(h); + start("collision-resizes-before-treeification");for(int h=0;h<15;h++)add(h*64);add(128); + start("split-to-lists-and-treeify-again");warmup();for(int h=0;h<9;h++)add(h*64);fillUntil(49);for(int h=9;h<21;h++)add(h*64);add(640); + start("split-tree-and-list");warmup();for(int h=0;h<13;h++)add(h*128);for(int h=0;h<3;h++)add(h*128+64);fillUntil(49);add(13*128);add(64); + start("split-two-trees-then-untreeify");warmup();for(int h=0;h<16;h++)add(h*64);fillUntil(49);add(1024);add(1088);fillUntil(97);add(1152); + start("unsplit-tree-retains-shape");warmup();for(int h=0;h<14;h++)add(h*256);fillUntil(49);add(14*256);fillUntil(97);add(15*256);fillUntil(193);add(16*256); + start("overflowing-three-dimensional-hashes");warmup(); + Random coords=new Random(428); + for(int i=0;i<25;i++) { + int spread=(i%2==0?Integer.MIN_VALUE:0)+(i/2)*64; + int raw=spread^(spread>>>16), y=coords.nextInt(), z=coords.nextInt(); + add(new BlockPos(raw-(y+z*31)*31,y,z)); + } + start("signed-hash-comparison");warmup();for(int h:new int[]{0, Integer.MIN_VALUE, 64, Integer.MIN_VALUE+64, -64, 128, -128, Integer.MIN_VALUE+128, 192, -192, Integer.MAX_VALUE-63})add(h);add(Integer.MIN_VALUE); + + } +} diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt new file mode 100644 index 00000000000..adee8c963cf --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt @@ -0,0 +1,184 @@ +case equal-hash-sequential-identities +0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-961 0 1 true 16 0 d6a6531cdfb49fca2419e941ccc457424f5732758f4e70c91ee08c20d235fa23 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-1922 0 2 true 16 0 6cb4ed34dc91bbaa0d8a0266f624f550fd7371740184bcaab75a2e62ab5fd520 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-2883 0 3 true 16 0 f670f1e579f1606ea556d019d70c1c70ca36a132f0fe2fb711f36b051a80d67c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-3844 0 4 true 16 0 0782b23169ff830a7e8df9b4c4336e9908a5b618936d718a617dbc0866f93965 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-4805 0 5 true 16 0 92c131f85ba57172f6ef57267c35e1857786f591a11e244deb47985910a1f099 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-5766 0 6 true 16 0 abf7f527a15b390fe5733abc7acdb925b079c2ff885322aa8f5589ee1e6cac92 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-6727 0 7 true 16 0 6c796ec96389ca6cfca7e5efaaf471480bc843642007be3e69e7f470e19f782f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-7688 0 8 true 32 0 2c40726d335d09f9ac592403037ea85f4119aaa0e5a37d30ccceffea1061d57f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-8649 0 9 true 64 0 43ae91711870b9b55a68184065080060c7cab20d8bf9eef81b8808e02cadf6b7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-9610 0 10 true 64 1 daf20b594761396b22bc4010a41ff55595f32082779bb688b3f8d894dcd0edf2 84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca +-10571 0 11 true 64 1 4a8338e6843a7d60b19524ded28b0dc059f803630db8d3f276194062cf77e27d b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39 +-11532 0 12 true 64 1 d44f108d60f8482a660065e505d27ef2e46073232086a3ed64cfe4f4866321c8 b78ebc662f60e63bfb405ba6bc7f6c08e2182547bce1400cedbdf1bcc398fd89 +-12493 0 13 true 64 1 f34b88eb8bc718fbd41c430fa915c46b61203e273da761402e5b00e960251b71 570275ca3b9b4944601bafb2d7a82937862fce41b785acaa499138d4ba2320f4 +-13454 0 14 true 64 1 069f90a71a6d817b2db6575425e67facc5916d60929c412cf4e85bd94d7462b9 70d228f8cb5adcbc87aaa1a2a6858a0795cac2b2e805fef7095de635172b41c7 +-14415 0 15 true 64 1 db6992057b9ba2d862ec1c3130aea7b5c06413f7d2b35b08d07737a3617a663d ef887de5be1e543580d1ba3c9c864467f7845bc53f347cc530d56c96d494c412 +-15376 0 16 true 64 1 76db8af81c29dfb29154fd8c3e55db095fc74edd591367b659dbde03d59882b8 db5d2d544ca06fd3ddb3438a7d449478cac9608c4ce874bb793220ce13ba1462 +-16337 0 17 true 64 1 7b3edda6525338f416c78b70d5775addd8db22dd8628d93de36cd485004c2fc7 9fdaedecf5af2930aee1ab8bbc30d795d16e3510042da2b149d1d1172990b240 +-17298 0 18 true 64 1 81112a160eb871be093acd07fe44b24a14f90bf293503f64381ccb659f9897b8 0a7d05282271ab28563c6255dc126e00e8c990f3e09497b5a0c52ee332c7599e +-18259 0 19 true 64 1 cdb89cacc4756f858e17bd59989f30432fd1a6e4912b82d415233d96b70bb043 908e37aa30b2690bb95e1ef971016b7716a101ca6b69599392aa5e1eb1c5f537 +-19220 0 20 true 64 1 8b4c98a175713a69e7a39c5c1ccbf6f2a89bcae693ca24c31b19698609237dd0 563d08119299c94b013c9ddd150ad54c90de5f29b4b87c54144155ec0171864e +-20181 0 21 true 64 1 6683fdb4d582ee74dda1b74f682a1d32ef54580ccf6f69eec82b8439dd29c2bb 0d388687314a5852a16f7b40468c194ba94bb756e7699f377c86e2d310c7b2a2 +-21142 0 22 true 64 1 c7de33105dd48cea06df66b17a3a38f724dec841e9a0e298e1f44dd86afa20be cfcad2fc0e3ba75e18fd14d22133409234b75f996b22cc44d7d2bb9c2e7ed89d +-22103 0 23 true 64 1 c653fcc4eb5db571f293ca8b7b228d0544cee709378f5750f6d1816af09a02ad b8bf175f1204adb56a775b5c643313a3f7f642e6f74c321789c6f366d3e43a22 +-23064 0 24 true 64 1 1b4c6fcde97e76a295facb7dec9f1d550baf95fc2d7cb63a38d4b1c49c5895dc 727bf5a5e698bb7bfc66022f7eac0ec6dc6bef46e90750ee86501b71f7d99963 +-24025 0 25 true 64 1 993c50fd0595f1f8f396f78222f73058256037c0701006b9604834d87dc205e5 1449819139f42b851bdded67a306bbc38f991c7db4740b10e2d9016b0211349d +-24986 0 26 true 64 1 8f84cce6f4b53cf2a394541a25085a70a93d4a46271d8005191cdced8ca15f06 8fbc8fb6069cc09077fc762fda3845bceeb1e2b64751f9dde3a7a1bb5a2fa26e +-25947 0 27 true 64 1 b3d3123e481e7e62376a0df3259d20f56e27f9208f58ea6f7e02ba9cdf14e6e5 378bb234dc80cfa68fbb9e19b5f5d320758235454f00a68a76d473c120b20880 +-26908 0 28 true 64 1 113b77d0076533415ae3cc64e0e484dbf19f978dd283cd791d09676d976a2114 9d9db3b20eb9232d566bdb8a887f6d834957f9530d865a5ecb2df5343f0efa98 +-27869 0 29 true 64 1 8b85c249b492245ed7f7556656b545b72970dd3e0adb0554c6a0d698a842d152 2d4cea9148bc69fb9c7da49f0425ecab77f8c31a7595539fe0f5635670476079 +-28830 0 30 true 64 1 9d79923f219c004b189fa8b9aeb0e61299290454e28099798918f0d7ebf3834b d9d3c6b81d39eb30686d491a2124feff758bfa4224b6fb0aac6724f73278a95c +-29791 0 31 true 64 1 48cc02b217db0ce88a9d7f2962528e89513862cbd9a8bd0561fb4719c871c325 672d63946b4c94c0658a67235d236b3dde70feac49efffba4468616fdbcc9411 +-30752 0 32 true 64 1 c323a6afe71851d3fdc10a55414ba5cab722e81a3b3c8d5a70835cedff1eae83 bfd6951f83c11e3caf9cec5f9d2cdb1ea44be9caeaa25c64d4248881698d25d2 +-31713 0 33 true 64 1 5b2a9db04b6b27f6010eaf03d05af6b6be95abb8455e282a863a61a80e396a10 74bae8284c0972d5bc5735b9716375fa1110ef05ce6a2ba8832b95b87673068a +-32674 0 34 true 64 1 f2a68e6ed1be399c7e05aea8d1773e4b0a6ce8cbc680ac0e4e0e37e324c880af a1f35a34df27609ef104f0123902335709e78afaa40591e8a8d9fa845d903107 +-33635 0 35 true 64 1 0df0d72efdd77cdb5cd5eb8de80f5b71d968b54855bc3f8ee97d7ef4c0faf956 3b1d10d5264b5e2c3206a7d338b4b5e09f873969b893f4bbefef59c775029adc +-34596 0 36 true 64 1 3724862237ec4000879297e6b56eae5dec4ef78b76df8d29a14f2855584955af 16f188b175398518f638953266664293ca12507574a37cd297e8eabf4e2cf1d7 +-35557 0 37 true 64 1 ebfb5279a8b869a7e8e3f2de372b76b9ada20f90e0d6ca14f0ad6795c61fdc5e 126026f7603a9412df5f1eb0275d8dfa91e2579ce716dd47a4797fbee05fc645 +-36518 0 38 true 64 1 7984f3a1f54ded99dfb88142c1a205bb3b5daf29fb805cc15f42efc32f9c5cf3 cd4ab893ff909a144c37f7805d81e488bc32427febb63104a8a7c2bad4df7a53 +-37479 0 39 true 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +0 0 0 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-961 0 1 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-1922 0 2 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-2883 0 3 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-3844 0 4 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-4805 0 5 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-5766 0 6 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-6727 0 7 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-7688 0 8 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-8649 0 9 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-9610 0 10 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-10571 0 11 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-11532 0 12 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-12493 0 13 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-13454 0 14 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-14415 0 15 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-15376 0 16 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-16337 0 17 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-17298 0 18 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-18259 0 19 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-19220 0 20 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-20181 0 21 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-21142 0 22 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-22103 0 23 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-23064 0 24 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-24025 0 25 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-24986 0 26 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-25947 0 27 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-26908 0 28 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-27869 0 29 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-28830 0 30 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-29791 0 31 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-30752 0 32 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-31713 0 33 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-32674 0 34 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-33635 0 35 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-34596 0 36 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-35557 0 37 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-36518 0 38 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +-37479 0 39 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e +case equal-hash-rebuild-after-split +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-961 0 1 true 64 0 28f2321938e91464724d62b132c2f9cf7fb0a2baaa6befc9687934c4368a2e02 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-1922 0 2 true 64 0 ed07bc1e4a9c7734c2036f91f06d491a989db5bed82a77e1caa0a191a399bb2b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-2883 0 3 true 64 0 a0605ca52e48537e4e4549ce64bae2dec257487e72aea756bdaa4bbf7d83a4e2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-3844 0 4 true 64 0 672ba16f45b976346fc4ccb484100bf9c5f1170432fecb4770129511ea9a41f8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-4805 0 5 true 64 0 899284429a4dfb8f5be1bb62bc8c04e5cb2626e81ca1816ae48b555de321898f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-5766 0 6 true 64 0 b86f2ae672461be3dbc4fb1be156e114c773542663767aea7dff21d418d90d20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-6727 0 7 true 64 0 031f818ccaf1a0f6f3ac2695a5f9396ead2b0cdbea6a9691eff0e89ec30b4756 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-7688 0 8 true 64 1 dcdb4d5436aa1cdc03b47f05ff26b33571707ca1d57032698d5a3cced8eb81cf 75328e67ca3609924e3438d04b5afba893309762643ec69321741c1a0f64e55b +-8649 0 9 true 64 1 6ef7a69d8b4474527f202668739628a9e84e8aee57babb31cc6de596950dfe4d 7f2c5e0a7aded0b2c93c77aa27b6df7976285865977c3f6a03eec123c1968e8f +-9610 0 10 true 64 1 5ddf4fb23faa9c9cadd10a231a4b59c868dc1e721218bdd16643d9030d5c70c6 84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca +-10571 0 11 true 64 1 b2b41c4135ab2534db7afdc937e1be1291e068da834de83130d3bf65a51d0006 b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39 +64 0 0 true 64 1 d44012d5a8ed3653116e274ef413b876f79946247a682bdd9b2fc3db8b269a3b c6af71d6450f9fa424506e20f0685616c89fe043a5dda19fbde83cc7dfc1207e +192 0 0 true 64 1 7ab4bea925c1e1571e245aca6a4ab0e2406373715e21cc746a3513a038add9cc 62c009a4a11fb122e75e62b432c5ad7d1bc1bb82b6d72008535176714378714b +320 0 0 true 64 1 6fc46d6c4985c56e9c8917daafc02e6a131de058c1ed9a59a710d4140b80053c 38ec63211d138852829eb8487aa68cb4c20c3f632e5c81d25948c35befe41639 +448 0 0 true 64 1 bb752e67c8ffb88c8015fe8bf231dc3f83d6ea7c05be9bd1012115434b400749 767e9ce7a87d89a4846460dc74a883f62cd2b6ab50136a2486e49867684d78d4 +576 0 0 true 64 1 5fa455a6d315c342715490a477a70c30006a6659614879cae764e898d4c83ae4 77840e858d833d2c07b8a8268c5e1536a41aacca07f44680da897966b85d9170 +704 0 0 true 64 1 a3613e36165f18d9fe8e1046bb04664aad6109a65317e1f3cf1e9a3c288af2b0 3afd26cc69fbddc34af7c0339a91bda92ddfe07e617c0eb9f665a632d683a0ad +832 0 0 true 64 1 a3f108c97653742c1916cf89e79cbcc90d0779310f3741ed84b0086ee0ff596c 3a63beba0d2b43f8d749d16ee4ce63604715f7d4895adf69dbe8e2dcff3ea330 +960 0 0 true 64 1 9b27867c5693c0750508e589991ffbea799567656d25b2247648f7616c80a725 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b +33 0 0 true 64 1 34cd9f42fdb5ec5552e05d587bd65ef4d9bb2910ccc8f4d9d7e2f5f1558a6679 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b +34 0 0 true 64 1 479869cf52945b2811ea6e932b61f5f85e417555d76dc7da0040428aec50d1c4 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b +35 0 0 true 64 1 1232cf3630377c25240f08168a7f0fb24fef6820ab40fe2d895d26bae9acc7f7 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b +36 0 0 true 128 2 58bddd158d069b4a2f196e37235229d9a81ef08a1a1a4b818b9352483b0a1ff5 3b685fe49ef22d30c6786b08b8c27bf481e5f4d00dd9bc0c343ab50f1bfe629d +-11532 0 12 true 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +33 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +34 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +35 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +36 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +37 0 0 true 128 2 81ca95157f58410f9a0cfbee34db2fc8cf5d7c38c6be4b58de764090fbbe00a1 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +38 0 0 true 128 2 7e416ded09240ca979d531b444983b40ccd3a9ded76428f8bc0c9ffc61243bd0 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +39 0 0 true 128 2 426e9bbfe7eb695d7196e2684a0c1d84c5dd7f0e070dc339d436c28206de96a4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +40 0 0 true 128 2 627e964d4df803a029a8368d3e922d9ee0e7dd08d149cb9e01d181349afbda08 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +41 0 0 true 128 2 1cd561a978f7853d42e13064290aa124467be6e898c04aa85fd7f9c5081693e5 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +42 0 0 true 128 2 5f24daead54530e127923a342164f3ace1a5877836675aa47196616fdd7f9574 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +43 0 0 true 128 2 d82898824c3b6ef18d8ae955a6cfa5f4d587096d79f03abeb2753ba05ea39027 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +44 0 0 true 128 2 43cb7761d8fd4aedf0c779c029a6e0a3d0ff424d6fa4a81128a840fa97427356 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +45 0 0 true 128 2 036a22cfeecfe626de7af464c0f5d714e5820122b7dcff8da90d3be1baf45230 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +46 0 0 true 128 2 f55359b81f8e2d499fb44f7b66f35cf58c369126552decc9b6bfc9f1a7a798f5 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +47 0 0 true 128 2 b886e62743d53a30d851716697c8f1a3d9f739fa931807578b0fae5663251fa7 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +48 0 0 true 128 2 de325163135313f0d3a0ac02bad4df4a1ebcd7b4002a0b715920a0eac07181e6 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +49 0 0 true 128 2 3d3034b9091cf442697b49a8ceeb38a88647a79637c1a86a836c70c818b2f9e9 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +50 0 0 true 128 2 8944b73b66cb104bd6d4fe191d8b492cb94d0d014e39948da1e4f7cf9dbf77bb 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +51 0 0 true 128 2 97796bac3f0defd81b0107eca78b172089e2fa94de73966b96894d0e865c8708 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +52 0 0 true 128 2 4ad7ef6ff5268c8cfb1bf07d45b437987189dac882481f4236cf7f3962be748d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +53 0 0 true 128 2 e15512c30cdd2b2fefbccfb28d948a73bf8e270659854090ebfe974f8014b866 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +54 0 0 true 128 2 4bbc0655dca0176a06116313a8250df5b3abc8806391864dab4638e12660c590 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +55 0 0 true 128 2 2920265c3294bfaafc6a0fe861dc3286e0a2e58fba57e655498830dcbbb4e91f 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +56 0 0 true 128 2 0b4723197fb30f51f8324a9345329eb26537384ad7a279cedc9a49517ede1c21 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +57 0 0 true 128 2 967d783bee8347d24535c9e2109b6b96f19e978800880db01c895e9bba76d849 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +58 0 0 true 128 2 a2b1aa1ce8bf0b09ae9184a24762e9117f3b8b24aca9eace218a3f402a42eb98 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +59 0 0 true 128 2 53e998b5c0fc11cb84735c34b0a4f7bf75875b43753113c9384fc5cd33b40f7b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +60 0 0 true 128 2 c6b4566964cee98292c5da1eb27a83984e2d0dad8260ee2738934e1c96775a27 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +61 0 0 true 128 2 d0f468d617714b8cc0816e9e0774bfc562ce77bfe25dd46b439b6787ee46b9c8 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +62 0 0 true 128 2 3a6cffbd7773e3dbd5df13f8302ea4aadaca610a0274b5ed3885e3ea5a9e9cd1 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +63 0 0 true 128 2 7b12fc9cfc3682a72333b8684bf24d7db1d3265806cbbc0110e7f79f6884c611 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +65 0 0 true 128 2 7f91bef82718e5772ac27a284d115fa788fdeaab6d54d31da9f40048f6e328d4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +66 0 0 true 128 2 38f3868ea0556fef5f51da993555c8d60b2b5a5b9206647ba044c82d90a4d7ee 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +67 0 0 true 128 2 4aa7087bbe3e3e010226b048d350122b6442e3dcf8dffb13966aa4f4a178c206 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +68 0 0 true 128 2 a3f6a41680a6a130f8f9dc079f63b400d66ab382f8453c28c55a4c3efd3547bd 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +69 0 0 true 128 2 0a31f19dc364caba577b9687297c236e90d2fee166c0aae401a6d34e785daa9d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +70 0 0 true 128 2 b16289a9b4473eb6db7ff1107a2cced757ef974256861d6607c902b07cdbd8a4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +71 0 0 true 128 2 70037c27de33d04d6b52f8f31bb00eb894ae841f3ce7a78c3af6cd375a4b6522 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +72 0 0 true 128 2 7c6c6950efcc128dc0059fbe5d51ac1d500bbfb00fd4946df471881448b3b71f 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +73 0 0 true 128 2 f6ff623325609ec3585f8bf254f11fd246aee17af0dbacbb35ba963504737d81 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +74 0 0 true 128 2 74521421e0e3195e4d8d02fb69ddf11e5cd404a5d1aa01fa17f6a48c1f22eee2 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +75 0 0 true 128 2 b902ee1a8d822bf990e9c974bdac88847d0b95faa21f6a28ec30a4eeeb49f68b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +76 0 0 true 128 2 61f36b928ac7bc49bda4515600637a032af4c0c1320cd6d9416b1f7d5df58a75 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +77 0 0 true 128 2 615e2921695e0af402e876698e65e229ecee3e579f05e24a0bb171dd964fc0db 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +78 0 0 true 128 2 f03979dc4e93ad8ec89d042f145edd27a9c2b61b74d3b418828fdfe7384a9f0d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +79 0 0 true 128 2 ee281edd421da3ea1f270f5929090d14e93092f0c0068ca395d765a89a031f16 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +80 0 0 true 128 2 d47edbde3765827b9c958382f3d9d34f65e3d52e33d98a229928e9346b61afc3 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +81 0 0 true 128 2 44732290e14ba7102004e7f517662afc8e219147138b5a61b5c82d43d4dc3270 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +82 0 0 true 128 2 4a1c410eac5a048f3a3718609fb373422acdc616ce953e5f3205822730b24f26 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +83 0 0 true 128 2 8fc19a64f73c7ea721400c7cbc19137769e89d34cfeefa59633c82a213bab96b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 +84 0 0 true 256 1 483b278321ce9f366efb1240e44251f0af4ab866ea680315ef96a5cee3d59cdb 94ca30e3be6601240004b38ab375c316c5d4af052423ba6058019e1e22d96734 +-12493 0 13 true 256 1 32d900d9e0043e975a69fdc9345b35c3b293088edce67f9e69166e6df0cf3a20 ae31d94b5ba98a6be4ac62effefc8e2eb9732294235edc0d1bff4d1721ad025e diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt new file mode 100644 index 00000000000..12281d7a8bf --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt @@ -0,0 +1,735 @@ +case control-initial-sixteen +0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1 0 0 true 16 0 b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 false 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +case control-resize-without-trees +0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1 0 0 true 16 0 b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 32 0 bd040b4c699899cc871720d10ff6db9a3473c3a00843ef2a47650c18e8b5ce60 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 e8fd3997b27b020552b8ffc75253014ecb6d32c75e3c2b7a5e3a7077169d31b2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 e614c27b7c3f68966686beddaec8c9d0e6a736782f9815188d772810ffa5cfe7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 456da144547ca79d2d0f452a3b5d5d0de0717c3f7ea912439a55ec5283415b47 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 e9a3c80dcdc7aecf6f6e464e680caf8442928ff16a2df19a376ebfe2e814824f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 dda2124909b295d83849102f952fb56f3d5ae665feb68c5f13701956dc50b543 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 05bc57e705e3083c3bbb0542020d54766e80a6936947e91bb237399bdc57a249 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 ce507f4bd955c21e907c5ab512821ba76a5333a33301ddce8a54f9d68e6065c3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 61395173eb8ba1c3af19e055472188ffe3cdd8f12104fb6676d338da1116b1ab e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 920d45a8f6fe792d01dba2210d9dcda47ce18b954d797581f7a304925d2c241e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 39f97f2383870373d5cdcb4e2840e9a0d6340bff18155556e9ad5784f9321f26 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 798dc0d94f1d34fbf73e61d8b09f13e88ac60584ddfef416f6cc0a5054ca7de1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 64 0 b428c9d223ead8e831dd55743387d221e5dd15fb794af5fb0c0cdb1eaff6febc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +26 0 0 true 64 0 08dec168fa3ec57ac20bba12f7159978cf61de9faea52788cdf8da8f9d1d14c6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +27 0 0 true 64 0 f71328d5f28cdca59ad849d78a7eeb96a1b8b89eae244866170a81ab9d9a5fc9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +28 0 0 true 64 0 f74d25c360000cacfa1c1001299c45ad36bd88186d727adff1dcd662bdc16934 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +29 0 0 true 64 0 8622356616071041f8c5afde5c6d1905ae0cfdf8a9c083eeb3d6cb1f0294eca3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +30 0 0 true 64 0 30b48c5be69741a7f01f49a47af0225b82b1aee003bcbcc9a455dd4976cb179b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +31 0 0 true 64 0 55e060353390792958f43a710622ca91ba9c55c7c9f1bbb2421db3bb4506ae1e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +32 0 0 true 64 0 634185dc55a65cdc9e2ce81f552963760813490f09d480c4f8b9c52ac05cc5fd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +33 0 0 true 64 0 29b324e71f63025a0aefc7faca6bb67a8c0bf74059e11240f0ba93604608ce08 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +34 0 0 true 64 0 20fc00b062269e4d829083458a351d2b2a7fac66b50e8860de26bd08e43d3c9a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +35 0 0 true 64 0 4882fca6660f8723ff9660129e9b9116c0b0c947adb18d2aae739e2002cb3641 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +36 0 0 true 64 0 b817dd1b94144abbe80eab5a45836ae1083205235b68a0863da3908d9d9b1873 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +37 0 0 true 64 0 bad150910d3afd1ec912a23c44385132d2303453fb029c3abbe84cdaa75c7a56 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +38 0 0 true 64 0 92ff6b629797572226ac15a37f8e8ecd1a941efbd5fe4849d73ad45f566cea5c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +39 0 0 true 64 0 09901528278808d533e2ae273af84c89b2c6d5c3992308fd62acf1eb1d145c12 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +40 0 0 true 64 0 39e27cccf7838fe719334a10b3fc5341fba46a7e9f2d973f88806540fcef7cba e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +41 0 0 true 64 0 20788ef1868b6713a20d53b6e25b547b4dbc18296ea03df1f4d2cf4fd55ac29e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +42 0 0 true 64 0 a1ec77e660556e004b53d06b113717214f66d8b76856eb9813b3b2c4ec4dc8f6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +43 0 0 true 64 0 4e1b07aba6a9e734991a9d4105cb99e08bc9e6c87279ac1090430d4fd58ec04d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +44 0 0 true 64 0 e8c411018c27499d45a145ce6b173494f72c1fadd40cfe0fc353148256bf99cd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +45 0 0 true 64 0 ad87f8e1a54a9e1405a9d9254124b7bf04723c9b92709d480e3d86e207e2cd1f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +46 0 0 true 64 0 513ecbdb7767f25f1f0ff18efc9cf0ed8305564d0be57ef8b8d394a35d4312ec e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +47 0 0 true 64 0 e3e2f84b65499960efc2a4ae5fe314acd42cf2c9035ea6ebdcdf15b494a5ff20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +48 0 0 true 128 0 e1a3fa4d7da04b00751ce2ebf940a4f97f47cde2cb00550a0698bad63dbc5ca5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +49 0 0 true 128 0 241d95c35b8f1597b9024ca4414fce30ccfa52a2a0c91eb66ccb5f1ab2cc7c17 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +50 0 0 true 128 0 739bbe489385ee2319b7fe133afde50a1c624d26c910aec2908ffc7b516bd17a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +51 0 0 true 128 0 baed0246834058891cfcd43100a4bd2fb563179f828cb4d2906c5cdee7f5fc80 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +52 0 0 true 128 0 17d3c5e449d13c652e85f5db23adfd32dbc9d2a7f51f039383b5910ad24ea1b6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +53 0 0 true 128 0 c508dd36bf642807d70eaf8737273ee27d74f80f5af3634c90af88e2a83146f7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +54 0 0 true 128 0 5b7d012494eb8d3f4834039c9bfa0973519cd732a62ee6f075884bad439f3c06 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +55 0 0 true 128 0 9531b3ffab274a103b63a76a681a74b77be854a3778e7d5845dc4bd71819d37e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +56 0 0 true 128 0 235f2987e6a26e48dc5cc15893cca6f0bf9d5d64df7511885ec56f7a21a00036 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +57 0 0 true 128 0 0bc5e3dafa121abf81047ed6763f3129345adb1e73d55e77b204e2d8aced8caf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +58 0 0 true 128 0 604117d2ce40121669a191c7997f24db09443046d775333bd8e057f12f508611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +59 0 0 true 128 0 4db2023164e1b306db0eb21902211461b1fede3880bbb861c9ec0bd0f33439cb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +60 0 0 true 128 0 eecf48926325970f68ce3a4df2bd8822370fd58f86bd8e2b0c2cc9f2e3f4441c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +61 0 0 true 128 0 63b132c71f0b41db8e688770d39bc576af5f6a5285a08454247c160fd5b38ba3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +62 0 0 true 128 0 b71e96c6f415261133b2d1d09fb919cfe63855c32cff69bcd2d18603c34531bc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +63 0 0 true 128 0 2f000e4f88896c94219c4588ce6c31b6a0ac7bf2d3a1f3a10bcf950a75b47846 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +64 0 0 true 128 0 c6e3b4a5ea6f274c0fe46e3141558c67adc2a3fb5c3bec3f7f422aef3d9e9b36 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +65 0 0 true 128 0 3d335a80226152e8c234acd6267059fda97f351ff033f2e58a842872d5868c13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +66 0 0 true 128 0 07a3b99f7d8e337e9c9562e4010f87bda53ef618dbddc1d3090df2e1e9d488b8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +67 0 0 true 128 0 282b76144b6a8359d248e2147e9b32b28ddb3ab1fb2c69c066870fc4ed72173f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +68 0 0 true 128 0 f2f7211d8122e766c086ca2262fdbe6522d46687e7e7053d696ce61e80b9eacc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +69 0 0 true 128 0 b03d7171a648580a66f5d0b6166179aa504fcd9726a1da1231bdb084a948efac e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +70 0 0 true 128 0 bc870029844cdf8f4e14aac43b43f514beedeb007925887663ffd109a9a37dc6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +71 0 0 true 128 0 cfe90a75af10d3a2b92db2cfe066834063c4b75cb2626755856dcf99d0273ff0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +72 0 0 true 128 0 d19f8ed1973f3faa5f2c5c4c0aca0f6b786b758b2decfd320e99ce630d189ff0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +73 0 0 true 128 0 03b32c3dc357dbe05d06d858e7e3246cffd8455b38fb876b2d421a3d63db4845 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +74 0 0 true 128 0 2ae522d7b8fc3c9fcf8bd792b92a6860d23c430c4ad8d6dc73adca392062f4f5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +75 0 0 true 128 0 7c4082746cd62e0fcebb1f5a8104ac2db71d21ee3bf97e69be29df28426b0c54 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +76 0 0 true 128 0 ce8e41d72a42178b79a717627c6ed733e656015fa29954840565784ac58b4593 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +77 0 0 true 128 0 ae5cbf8b0b9b6a77c47a1ac0e2325c1fe326dd62af5632c7220238f34bce2919 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +78 0 0 true 128 0 38cd3cad6abf6c2da81a00bcd8d1ca34f23ca39726835553f366b3727dbac255 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +79 0 0 true 128 0 653848f003a2c2760585b8828b4958f6541aca7b61950ca13628d56464bf841d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +80 0 0 true 128 0 d49d77865b84c3a7cdf783f5a84b7be5ceb30cb8c45a6f71682cf3b984548ab2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +81 0 0 true 128 0 8700cd3d5c90ae04ec8ba338e1c66e1094588a416ccc7561ca6f717650ea3e3b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +82 0 0 true 128 0 c3dfafd3708e5242c6004585e5ddf55492c99e3caa32565a1faa450123e1dfad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +83 0 0 true 128 0 8d27190f8054c80b12304093acad95a123a8d3e227fb254d3f599a7b772a4483 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +84 0 0 true 128 0 75967a043d2511ceb414346b3e2c5885c875eb97359774e320800f82aaabc431 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +85 0 0 true 128 0 1491a4e3171186aaa83e999bd234f6ffaf0b9fa8db971032bb324c950edf0d5b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +86 0 0 true 128 0 fd3860fbf2180351752501c55a676fd5d65889a3f3f2b0e6ac811de4c616477b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +87 0 0 true 128 0 f74d27c2225be37a38e3bb76ba968a8947c5da8b1d1f4cc632edecc88358dd45 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +88 0 0 true 128 0 4e1f6526ad508fd29133bd6b213caaa655eccb95a71e30df37af901501a7f118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +89 0 0 true 128 0 86dbe26695073d54a83dcccb4109904826a96e00e15f7c67606fdc9be6a33c0b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +90 0 0 true 128 0 e1fcfe54589354cce2553be418a61379cdce338c7497bc0e02fccc2091c85bad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +91 0 0 true 128 0 6b214a974c04569e9ea89d056b1b281a03bc7cdbd6ccc6e91fa3b1eda791ad46 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +92 0 0 true 128 0 fd3e63f5a2416029c93e6b81a73e086a0e144fd167a72959a1bffbe9a15a14ae e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +93 0 0 true 128 0 5763138049348184e0e2c757a43cf516de46153a1be6934ad9ca41fac42a2d08 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +94 0 0 true 128 0 9647f46a2f9a9c473fb2006a1586af3ccc8606d7f1c4254fd29c37c96ed69b09 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +95 0 0 true 128 0 90db01392bdfd1eb23992f04686fe3fa9da451d17ed09fd476d76e8baebd1990 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +96 0 0 true 256 0 973e269d23020bace974571580315565a5a81abe32bb575b0e58dcf0693b4a89 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +97 0 0 true 256 0 243263b7abc6d8b410e59b09ed52866e539afe8a11d330cffc720d192f849f6e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +98 0 0 true 256 0 b6b6309d0d53df6e2e3453e14f290ff6f94d75dedbe1fc59c5e085e3793b6444 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +99 0 0 true 256 0 e67528897a8de849b61f7a2e18a48582e0c7175ca50c333f7982adc36f4a94a1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +100 0 0 true 256 0 8760f83e86bb18235e32053c069ab88084bb643139bfc2ad230346618164b239 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +101 0 0 true 256 0 fc0c9fb527ede41e3f8e0ad6b3e99d4599d330653f172e440c96182412632460 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +102 0 0 true 256 0 6f72a6b227e3326d1a75c9809879df0df3c4427545e69c8a4d1ec78d78946faa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +103 0 0 true 256 0 178a387c6d2a59ec501e4bd80212a5a1215afe78ec450631b47c21da228786ef e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +104 0 0 true 256 0 96dd19472f1465731ec3667b323269e6c71d66c26bbfa4c35208ea57d8b176ee e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +105 0 0 true 256 0 9e2e3103b64f55e4ae990bec98fdefe7d709f8fd818ffd38717d5c0435ab6825 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +106 0 0 true 256 0 af55410aea8d74ec0eaebcd8b151687ce65451d987bfb68f964d83fad52d60f6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +107 0 0 true 256 0 b48fbe3dd6b6cebe136e59edef4e66fc277edca38bc70fa71b3e12b0ec508196 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +108 0 0 true 256 0 b548022db689363aa78379d652ea691e94e58c34961f4bdd2aa3c9c506883bb1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +109 0 0 true 256 0 c03e6719a25498430226cb264f084e2bc04583b9a8015f33a1e202754e1150f7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +110 0 0 true 256 0 1da7ea240c05ee4619dde9497fc4ae26fc664ba55d776b38d1547d9def199167 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +111 0 0 true 256 0 752375aace4815277db268064d07730a6d260d24d19a03bfdea2f87c1ba28b04 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +112 0 0 true 256 0 156bb9688142e7ce8d3a2871a329ae5e49653235b519d91ebe36262f4bedc2f9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +113 0 0 true 256 0 e2c467c4a663d0f6feb6fcf5a31fb6fbffcf54ba515135585398dc9f33a59d88 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +114 0 0 true 256 0 060b007f65cca7b15678d4ec780fcef696e07b6898cb1d0d1d705f6378298a84 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +115 0 0 true 256 0 9c23608a13c11cfad5119f30c45903a0024445fea15f3699bab2305b572c695a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +116 0 0 true 256 0 304810dd4aa63d6bc21a83b762481cefaed49a7d26f53b0f8b0eec317b38c9cb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +117 0 0 true 256 0 c0bc7beae38699a230200b45d4de8f8a62606d92c97dff6f677c1c71bdc81823 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +118 0 0 true 256 0 6e127416ba68530fa1488758e66c6480a080d87284537e1ac3769d13dda730f9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +119 0 0 true 256 0 95bc47f6b9fa225965cc572dd394f707d4bdb3fa047b4a87a7e429723a2616d7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +120 0 0 true 256 0 bfa96a0805705a10e7c2252d42d79b7f00a70e7064a2200bcb14ecea88c6bfec e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +121 0 0 true 256 0 53128be53877cc0951bdf564f12878479eaa29d394e2746e3890f3c57e53e3bd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +122 0 0 true 256 0 32c500e507f4fcea1bd712f53ccaafaf643041e52c7662c0b10aac19304fed15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +123 0 0 true 256 0 d8e0afdb2b1e2d4b098919875e5067de17579855795f493b3615bcc60a246f0c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +124 0 0 true 256 0 ac3d3a7277c728ea6c160adb6b031b9617a7e0bda2713d36402e8dd342dde3cd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +125 0 0 true 256 0 857f7644fb1a082214fbc7ab1b85969c34ad8259e39b3147a68326efbdfa7bc9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +126 0 0 true 256 0 fd5ee8335ed8f13c52b49c655c720655150ff1e8893a5ebf7829c27dfd854d9a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +127 0 0 true 256 0 f8350e97d9a0a5382bfb52804a3b301276d0e16123b246eeb4a368fc72fe0860 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 256 0 ada636bb30d32d830579f51f599209312fa776402f03a9606d2d40d496b47dae e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +129 0 0 true 256 0 953e7c5fe433b7bae8340fae9bdc17fd2898774ab2b19db0e22b8657fd5c13a1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +case collision-resizes-before-treeification +0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +64 0 0 true 16 0 15b8e296e318d4f07cf46716611157ecbe102168ce40f12eb058c1bc8cc52af2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 16 0 16882ce8b10032f0603149e29b8bd8462526eb9b3f39c99e37f1333bb4760249 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +192 0 0 true 16 0 ce2c86195416b6060c1cd874f8a0962f61a0b668cefb7d6312616cf39a637b36 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +256 0 0 true 16 0 78641378f6554873fb864e4194ecdff433bca53f22f635149cca0e9ed4479dba e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +320 0 0 true 16 0 05c2f50ece14c333bb6163ab3b2b0ff43980714bdda65ff6f4f889610be5b807 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +384 0 0 true 16 0 c34af995a927ca99217a14c182d2baa27fd75546825061fb940c7a7bc8357420 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +448 0 0 true 16 0 b3a7ffb37c93d5fdda74a0bf76eaf23333ab22a40df9026739c188931ee7d1eb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +512 0 0 true 32 0 e08198bfb8e435d23b22d5a64bdbc76f96adb84e78e2ce0d7908f6b1725f0010 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +576 0 0 true 64 0 98a698a0285bfc6c9a77daf1804ec4d6c5776fa6db65f22c371f6d1321d384d5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +640 0 0 true 64 1 d79759b6efc7cca5abd3d8b93fd7dcff096ae7cd2d031b10d0c581b7caf000ee 0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa +704 0 0 true 64 1 210c2d82d8f23dfc6e324c7ffe628d7ff639c1cdba5781d02df105b787c25907 33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e +768 0 0 true 64 1 680b2385ed8075123be0949fff2d9a64ee2a83f429c97ff41d9cbced4b00561c 103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c +832 0 0 true 64 1 5d7e02e447fbde77c3091a3d7198f49b664fe9633863d883bc5c8333d2aee470 19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816 +896 0 0 true 64 1 8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 +128 0 0 false 64 1 8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 +case split-to-lists-and-treeify-again +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +64 0 0 true 64 0 9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 64 0 c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +192 0 0 true 64 0 f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +256 0 0 true 64 0 dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +320 0 0 true 64 0 a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +384 0 0 true 64 0 3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +448 0 0 true 64 0 99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +512 0 0 true 64 1 10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +33 0 0 true 64 1 74fa7598312e57412585f54360b45ca74a053a3371424dbaa5a674e337fe9ad3 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +34 0 0 true 64 1 337b8e3ac9094219ff1424939e3cfe69005ce4d38c7507b9e0a2ca33f6cde97e fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +35 0 0 true 64 1 261518dc617c1bce90ee63f07f7bab37b5b56e2221edb4d754ee83fe20749448 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +36 0 0 true 64 1 9fc8912560ebd5d2af71a7e1896ba340acf1fbc767f937f723cf35a6292fd7b1 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +37 0 0 true 64 1 06c22a7f6ac63d7d0277e56d88e4fbda21daeb73f376503dfe842c3fbbfbf3de fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +38 0 0 true 64 1 cfb15c9b2407fa96fd86d628331d26813df93e1ba671c47adc4b3475ec9eba00 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +39 0 0 true 64 1 8bca0a05bb458e402384f6175068addb9e802546929a8355e674456face0dd22 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +40 0 0 true 64 1 7f33eb30aaff727789a679b7db28280861b53ebc2e6efadadd34d815d878d791 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +41 0 0 true 64 1 c88eca1372e2dda34ddf5e9c55f2cdb30546d034632fa437473bfd16532e5c00 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +42 0 0 true 64 1 005f07817a86ab001b8535904ab805f6aecac9aed4372d5995c80d9e3567453c fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +43 0 0 true 64 1 df490232b0b5cec29aa17820236ae5dfaa07309b013b1414fa7a9f535fb3e9aa fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +44 0 0 true 64 1 2d8eac5fefe6230281d4df059e95e82033115386bcc9a5927a52ffb76b8971ea fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +45 0 0 true 64 1 20e13a2bc6eb489d32421a142f21b84258ae56eec4ba603f787e3d5d702aecee fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +46 0 0 true 64 1 cf74506bfc094a888feee4d6f771dd0f0161966ce450e0f36e57ec2150b1ab96 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +47 0 0 true 128 0 86e4e51ab3794cc9f5373645cfd3ef563b6408c05835aab1fc36577cb7e3dd17 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +576 0 0 true 128 0 4c841a696aa02076087987f2bae9c03155ee8e4b121e1cb1b94c579e1bb45741 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +640 0 0 true 128 0 383f67a53d42dde1712fd6afaa8a295c654a16ab289359b4914ae9ec0e93b70c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +704 0 0 true 128 0 f09a85cdc693ba9327699fe40502c073c252eda3cdba8e65cbac29bd780a51c7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +768 0 0 true 128 0 d715149336c4b95abbdb0ccbdd89d2bf6a7fa2a4301ceb0b0e073332a2b687aa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +832 0 0 true 128 0 ed0572bf2caaec4063823accb4ceec8877e4c202ac0a261d14246cd88df681d0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +896 0 0 true 128 0 5a60e660ce5fb979be0030dee87182a6ec6d95cf5ded5ba572ec679cfdc297df e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +960 0 0 true 128 0 309a76c5f12235c69b0beac9c9ff154c8d61cfa7accdc5fc3b03744e4437c8c2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1024 0 0 true 128 1 a3d2858b7913d65d466ae360eed2c15ca9df2da07d5daa493a341e00164b47fd 4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf +1088 0 0 true 128 2 db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +1152 0 0 true 128 2 c0e6a3558abde3b100ce9072bc9ab6dfc0d11556303b22091a0980554266b65c f465512a719c69f51b0cc0168b19fa3fdac821f073298e0803e20da82b3bdbbb +1216 0 0 true 128 2 4bc3faa5dd64eefc41d86431c22a2851a773c6ebd51ba4d8814965e2fa17a9d9 635ce30db786756077e2f13fd7e8f44b887ffdb4117bf2243cf3fd1a67627cd8 +1280 0 0 true 128 2 87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e 81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf +640 0 0 false 128 2 87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e 81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf +case split-tree-and-list +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 64 0 b78199ef26bb1ca970f06edf1868b0453038474c0189dd304bd19fb8d7e3a754 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +256 0 0 true 64 0 65cda444c639049cc359d59bca2ee1291271ba98772fb4efbe1803847f7d76cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +384 0 0 true 64 0 f38d2f439f143859b95f909ec22b042b5744f19b85f04b60f23a58b4621867a2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +512 0 0 true 64 0 0ff6f0c7ab6c88cdda2238366c6d113609156cf1a596c11fd22aec4a395363be e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +640 0 0 true 64 0 2f69abc26249c64e02c9bf93f9cf9780020c652761a6d93b898c87dca8432ac2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +768 0 0 true 64 0 ce430551672bc0736fa61699e30b1e4251a52c0edac72ed0894f5fe45934d03f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +896 0 0 true 64 0 4a8b68223bb91e4d7ea1e8ead25729b8d8b15585b947aaf8fa65762cd6494b06 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1024 0 0 true 64 1 52198185bd42421b5f42472e09cf81dd752429ca68128f8d77577672d8cdfbf2 4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf +1152 0 0 true 64 1 29269f28279478f49442ddeb316511669ea0b05ba2afd64748504e17d8915eb0 9d11d8c45174163e45cd775852192b5f3c601d4cc3e00b5c4062d3cd9ffb1151 +1280 0 0 true 64 1 2058098fb8a951f6331a42b03fb1f4fffd935d51faf16524308334bdcd5bb9b7 4ae62b44b3f7d02d9a036edbbed4351bdf1e88471cacf4c3649e17fae9be621f +1408 0 0 true 64 1 a93b02668b935b7e1036ed720e59038fca6b18fb51af6ced1972d4fd956d7f93 44bf06d402ac3bf46208d23e997eac9d85a2a74695adc4a45820e29e6e524d07 +1536 0 0 true 64 1 226a007cbc1be62b0da3ce1d43179cc487c23e5b576e0b933ae5afc74408256f f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b +64 0 0 true 64 1 2ddb2670968f5e0ba92fb4d456359da168b80e1f79527cf1086de8f0a92756fc bba9b12b91ec2d8cbf357b01939459a5e81d7ee0d52f9df2be33d60fa5879b7b +192 0 0 true 64 1 4d8fcf95bba4233a8357c4e8889eedf311e229cbc93d72479982a45ea7d22391 d48080bbb1d91cf68559ab959da3a42256b6871f2073e7a16632d484ac95bd6b +320 0 0 true 64 1 ce3ad225e4b7cd750ed69cc7b26b46d7b43e75eab2421eb467972487eb239c78 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +33 0 0 true 64 1 859a413b7e585ee358afa8bc17fd673df50b2293b36ceb8620d64cddcb683676 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +34 0 0 true 64 1 ed90392a38baed8d084b6052b00442b4745459c2a6db575e311e8eaab0cfdbe7 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +35 0 0 true 64 1 99ebebb66472b46e141ee4c4530b473bb17c176c689caa155dbc63c3c870bb7b ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +36 0 0 true 64 1 0f967074d74586d94e777fba69c7738144a292257927c7e349542284e3414e96 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +37 0 0 true 64 1 9037845ee6c7370506b982d54c2f3f844929450099b7317ff600df834d51dc10 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +38 0 0 true 64 1 6c0d3b26a0d013f0620b8ff2fcaa73793643fb3b8938155230e649e2a7558bef ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +39 0 0 true 64 1 e3e535af2e3f5c5674d36e9649293d2e23017787146d5dd1d1b12e749cdb5619 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 +40 0 0 true 128 1 d05a3191e19d867d8cabf405353ca2cd40724ad4e939164ea36492bf05254aea f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b +1664 0 0 true 128 1 7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2 5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799 +64 0 0 false 128 1 7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2 5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799 +case split-two-trees-then-untreeify +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +64 0 0 true 64 0 9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 64 0 c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +192 0 0 true 64 0 f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +256 0 0 true 64 0 dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +320 0 0 true 64 0 a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +384 0 0 true 64 0 3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +448 0 0 true 64 0 99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +512 0 0 true 64 1 10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 +576 0 0 true 64 1 b01ee63d0ca97c7ff5fc33f25054ce5bbc0f3ae5fd96d16a35a852f55dc1ecbf 531a39d6203888b7cf2233cfc01fa5c27000c558083f7e191d0e21308cb75a28 +640 0 0 true 64 1 d31317af96cbe10e265788dfff1235854fa02d489be1e276904684e0724a31b3 0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa +704 0 0 true 64 1 3f06308ddd03feb8b194ab029a8d3c268a6ff5380a2b48f667edc3ccc415b8c2 33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e +768 0 0 true 64 1 6e3db8553740d08187c9f968350f06db1944c44255eeb185aa2cb59310c36bcc 103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c +832 0 0 true 64 1 8a70c584875601d81e01d80db8e9a082bd24db9dcce3643e0ec2a7a68deb5711 19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816 +896 0 0 true 64 1 eb6bfff5a1121e1b2593933b788bb2c6483da0db030729523e405f4089714740 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 +960 0 0 true 64 1 4d4c7c82dbd48903ca8209bb3525470e1fdda1be4ec2da4d5d2b4571569596f2 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +33 0 0 true 64 1 61eff08a3baa241f13634e9406aa5da53488ddd0ab15ec5d17854f91cb929ffd b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +34 0 0 true 64 1 4599dc62b328bd6f29a70cb79ecb99413bf63b4a198836e64c7c0f4317bddb92 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +35 0 0 true 64 1 2276239a003d02c9c01c481170cd4edb890ce20f5a5e68fc776061cb0478dd50 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +36 0 0 true 64 1 7108f5a2322417122f7886202203533ef612e7ec7126196dd7b3baea473a9ad5 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +37 0 0 true 64 1 046dc2e6a810d1b60c61771dc28f2e689545c336492f478eb47b504c2526a456 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +38 0 0 true 64 1 70fe5fe50418532d35f6c936d41b310d566708a0c6af122996e990c36c7a9e1b b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +39 0 0 true 64 1 39e2c133c42b885d3973c311d0fd2ae80a6557aafde2e34ef22cb58b21bb761a b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 +40 0 0 true 128 2 a888f38ad0588622f4ed4f46e54f2546c79fb15017a15322e422de99fb7060dc bed83c0b1895a8b232cd3048a42d2a3baeacbe447d1163c29f27a793bc0add6c +1024 0 0 true 128 2 cdc50dfb2dc31318f379eb0c0b0afa74256f8e994d08440cc9087cd9d9fea924 740624f785d60814ad1b10d053b6fb7a3592c2f4fe18b4212b60bfd51c980083 +1088 0 0 true 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +33 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +34 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +35 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +36 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +37 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +38 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +39 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +40 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +41 0 0 true 128 2 3ccc01cd8cfdc61feb802550718e0f787c06757f50dd3ab1fca746b621229b96 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +42 0 0 true 128 2 2e0e9648fb1191ee4592bd70092117e6e96207b6c3641bfc33d0a0317c348d7b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +43 0 0 true 128 2 3b91c16ee7900ea3b5939439afd261470c3b37fe4576615349b48cc5083e2e9b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +44 0 0 true 128 2 bd93d34e20882961a53e95a7d150021d9253e5206f366350fa6436f3dbbf9e89 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +45 0 0 true 128 2 2b9533a1d216ecd266502a7ee464d8d3b4e8a634ed17766ad916d65f5e850d75 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +46 0 0 true 128 2 017b31c64164e6b89d3495ab25e2cf93137b1ca69f5ac00234eae7a24c224a40 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +47 0 0 true 128 2 db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +48 0 0 true 128 2 0d466999062222d7479af2d7a1de605b199c98b0e5e131b042a3c59e226abdcf 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +49 0 0 true 128 2 02f6a23605a0437ab0d1002609e5749ee373e90c1c33f3998df2402dba6057a1 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +50 0 0 true 128 2 ef8af77355ac51a6ea98bc146e8ddd265139b9fd2b9f3e64e788785164fe2ca8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +51 0 0 true 128 2 3361a7a7b92eb51bc4a8bbad523c8cd9431f0bae52b4f53214b4ebbd457883a6 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +52 0 0 true 128 2 c53ae02483e6cb5e6d4280dd18019a86bc7983431bbbec13d4b782c4b87b500e 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +53 0 0 true 128 2 e8b94e181889ccd9bdb6df9fb992e2e2ac5efa429020d8b53d778665090ffffc 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +54 0 0 true 128 2 6455ece4299525a8b03c6fd853afdbeb505840c4911025a08d3fc2e1ba85304b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +55 0 0 true 128 2 bdf1ddd938f479cea520582174d02d6b91d852a8b9ce3451a8e2446642f0fded 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +56 0 0 true 128 2 9c4f30c20cf9b4ec359941df1e5fafad5299fadf5efc9867e59db746d5e9e923 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +57 0 0 true 128 2 fc487be5b93639d516233e31321d930d9c1c71491a7f2adc7cc35d7e83074d38 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +58 0 0 true 128 2 6108479245c62c06aacecbc33f0907de16c86ba8e8147308aba22f915186a76c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +59 0 0 true 128 2 d0da7a405abe930d11afba76ca5e6ccd49d8c53c418a4aaae372b4aa076bd0eb 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +60 0 0 true 128 2 80619a5df249a65b309f15f60748ad3c9eccbd5298faa0739fb3c3c0e9e969c7 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +61 0 0 true 128 2 234566a1f45eec8b55f23a3d6d5024f28a42c71316218968d65f3c825b881dbb 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +62 0 0 true 128 2 40e2b139cc37e572578e992102ba110f29c193a7b8dcc4a824858530c0b1c566 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +63 0 0 true 128 2 f1198b28030d058975c4ea8ddfde0d9ed397c0627e7739667512dd1f30b7136b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +65 0 0 true 128 2 844ed0cca40294719622f7a5244e9eaa2119526d5525ba13ae3b3a9e9bf602e3 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +66 0 0 true 128 2 737586e22d8404af99c9c863cef2b3ca281da7c92f52c9433ad100ffcb0258a4 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +67 0 0 true 128 2 ec35df7827b3da2d29c8bf31805c7aff1c954e96a9b8a2f97c399dcd0f77a75d 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +68 0 0 true 128 2 be0b1d5e39585b54703154e3b6d51967cbb4d50aee663267814f691b5c33a2c7 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +69 0 0 true 128 2 a1533222b0b5385d9665b41036d48d7270efd6c4780e37adbb9fe1f73459fc8a 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +70 0 0 true 128 2 0cf65aa96a45774b0180079805fbba112a366531cf113000514a4510f6d25559 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +71 0 0 true 128 2 cdb78ee58b1f1bad0c5be4baaab0ec79471c99bd07466752ca8d5e018558f6f5 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +72 0 0 true 128 2 078a594a77f0d723933f0241fc239192a0a11dc1101b75545a74dc65d9c15bb6 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +73 0 0 true 128 2 046d4555fa313408d626a7b11cb3f25f8accc65508e8dc7477f16a9b65046af0 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +74 0 0 true 128 2 ca6ab9ad4f63add965e0d0e0e40ff4d8223d01c0b82082342668d6fa94e04156 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +75 0 0 true 128 2 a8cab7f7238e1247180ae1e087c8b5cedfd72d602ded67b81080c6de3ddf91a8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +76 0 0 true 128 2 a98c9cdbe06f49e10afbd317b80cdb1edcd7fc929e77e115f2e5223c0419545c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +77 0 0 true 128 2 8780d4356c825eb702ad3bfb8c3d0de9da3f28d200910c2a081957f08218a6b0 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +78 0 0 true 128 2 83e3a86c2d7b4c08b62bbd22473e7052e8e13e20257b3875e4b10198a70922f8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +79 0 0 true 128 2 1c4e24a41cb5d433503faf3636aafed824727cb4994a1fb78d451c5e3cafa236 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +80 0 0 true 128 2 33a5b55dd13a08d785e2d263381abca5ca387daeabea6933ab92fbce5d48f51b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +81 0 0 true 128 2 6e0036dcb731edee32cc9bf35f2c4a64f3e28539dd28ef6c8c1eb4df6e8c4f77 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +82 0 0 true 128 2 b092156fefefdbe327098323d224454ec04c3ff21d98ea8e768825df19466aff 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +83 0 0 true 128 2 e26fe3f36f09b2d1114dd217dea7a59669c6f29ab1aa19d9d3399c236261260c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +84 0 0 true 128 2 37ac1020f6f194e18a2927533db22e8211a08823dc209576ba5cf14214066fa3 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +85 0 0 true 128 2 0c6bb724d200edbc95d472b9476ae9728d5616a654083c959158b24ed4f3705a 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +86 0 0 true 128 2 0269da117e5f662d0b995302da84ab315c447ca8d9871c42fa34705d846f09b2 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 +87 0 0 true 256 0 8c0d641bad96e785ed5351338f6671479a5d92abbe0608b5726b8f3a21972c88 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1152 0 0 true 256 0 bf8f9f14aa073901ccf2866514f64ac72d6841463cbbffba9ca5997cc6ff12eb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +case unsplit-tree-retains-shape +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +256 0 0 true 64 0 a478bb8c30b66f77108ab0b45caa8ebcecce9482d6bec317fdeb24c06814f77f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +512 0 0 true 64 0 ae8ad6418957aea5000c47bfe689bccd3e85fa75f8503fe13d8f150fc96a2dfc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +768 0 0 true 64 0 4bec31b6a9b3669f759dd56296b90f10c05c5ebddd383c4e886a909c7f5e141a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1024 0 0 true 64 0 a387ac29ebd18c21d385b3752135e91f16010abb678861cd63ee1d014a7e5a3d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1280 0 0 true 64 0 93dabf0ca5af28828eaeeba9bdb489ae70b8ab9836ac0344bb264d25ab052cf1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1536 0 0 true 64 0 5f9678bcfec30ac864303c89c527d643ebc39e8e3959db661091aff5414f3748 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1792 0 0 true 64 0 58fc68308a10925f00838fd5925f71c3e1fcd82c63fe134ae255c3c2ac31c3dd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2048 0 0 true 64 1 4730fb1093e41dcb9b8ec61247362d5e576f81182123be69be18fe6845978728 ddc69b8f7753ebfcc5bdff53e39dff4832e4aaa1082b7e906966c42316ca0976 +2304 0 0 true 64 1 7a424d1fc373be358d3d9ec245478c13042e2abc1dfa58b6232c51d255aa9a92 54e4f282ac513b8ca619d1f6867d93b186e85215a0c3c2f874786c825f27cc1f +2560 0 0 true 64 1 d8b526cbd01254dc95b910884f55bc7ab1156e042f2a7e4c30b9b60efa2ca7a2 d46f38deef2ff7fc9b0570c21887556c09e8428a158a71fdc52110869f1c141b +2816 0 0 true 64 1 5f6c46ce2262b67bbdf1909d35b2187d102a2f629b92d26434f215edccdf8596 528573c7a4f186bc1a5a7f9e99130009d87f0e3b4ef217e2a357043ab8914461 +3072 0 0 true 64 1 8a97bbc7db7da74c60a9bbb2fed3974adb5b763a0b007654b9fe1a7bbaf39e06 c1cae48efac87ce79879b940782d22de6eefee3f9f5c07befb9045827129e9ac +3328 0 0 true 64 1 40719c2f20379c09c4d1612db07c3656fb7d2dc341a3ff4b507ae85e77cd8817 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +33 0 0 true 64 1 950ded5f9c39bfeccf2e3ba6c6dda9c63dd331399df581d1c7c36cb1fe2c1a2d d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +34 0 0 true 64 1 f36254f2a6ad9eda41d53f4df2d3d4944931a1581a74d734f99295a383aab8fb d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +35 0 0 true 64 1 dd2db5efeb1a7535e5f91ee73dc2ae1d2fe65074256d9a8d3b6f090d30118675 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +36 0 0 true 64 1 516d4748202f94c3ea65ef8ac3835e3cf9c701bfe205d103af883174b565c571 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +37 0 0 true 64 1 6c6e6d68ab58db6f3f1feb1291abe7ed56cb98e5c1c38494e6bc5cf516d619f6 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +38 0 0 true 64 1 7fcc8e29f197ffef1448b5adbc6920b4006485fb6cedf903b863913ca3cd030b d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +39 0 0 true 64 1 61ef7f6ab2adad7c5db6e3fa529251e61541dd733614e61350b64f59f41ac760 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +40 0 0 true 64 1 5c0b1c0dd5d698f20aaf04f6500a11c88783d597932a590441c29a2aee810b31 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +41 0 0 true 64 1 755567bd07c916a5fb611659a81e6619bccc698c1c7c7f2de24eaf34dc184886 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +42 0 0 true 128 1 b58897eec4519e77d9c74dc6427b79b1bc81f1c8ef3ea257a0ed4a07b05533bc d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 +3584 0 0 true 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +33 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +34 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +35 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +36 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +37 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +38 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +39 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +40 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +41 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +42 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +43 0 0 true 128 1 05237242ffe4362e8011982fa80d1f8f95fdae194807c896f3564c03d10ba72c 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +44 0 0 true 128 1 75346660f88c9a02e09d0fcfcae4774e455ca28b451dfc79b4ae29a10b4055b2 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +45 0 0 true 128 1 0e948d6c42cedd8d5dcd2a99d3fad83f8eb61bf956c3cdc256994e2131359673 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +46 0 0 true 128 1 6f752248a0e2e17d495dbef0d98f9a9c0ec0d545f627e1b01a36b4fc34ae0184 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +47 0 0 true 128 1 e07388a68cbc03672fc61020a251dd573fd74d6b9eef010d3c6871ff986475eb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +48 0 0 true 128 1 c25e02dc14660701db6adcd3f3eb11d2eda38c86b1433fd9cdd59355024576ae 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +49 0 0 true 128 1 c8e55a8de5a307bb1eefe72bd969039f54f9fa6ac42e68948f17ce20cf57da98 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +50 0 0 true 128 1 c0be431ec284f336df9e679c4ba2175f8cf399c3b553ee33928bdd550879e0ae 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +51 0 0 true 128 1 818ddb15573df1045986d47a0cae81ae0fc5e5ab965e00373131cbbb8de0131b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +52 0 0 true 128 1 19c246794cdb9af948af72c8b16ecedb9f13cd066be144524ff603d5ee55b428 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +53 0 0 true 128 1 b38d7c9cc8af5ba04ac5f699b19e92a4262ff0a1f610dc322e774b61c811d57c 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +54 0 0 true 128 1 1bb72c8d78d723512da752689c2908cb5e8c308ff8807d57406a2a79637ecf5a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +55 0 0 true 128 1 0b756cf2684e72ad89dc068f07498aa16fc95a72da830b6eda7ae7dcb915f4c4 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +56 0 0 true 128 1 b8d890029b2f86ea9a91bc04c9ef868326cf18d76a1429836eaf98083a8dd37e 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +57 0 0 true 128 1 e6c79b8be77b814f361b79243228d8caa359c9a6bba68c318356e4fdfacae6fb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +58 0 0 true 128 1 3503667ec4bcfeacad1214fde9331f761ae03e458b91a3bf70aed8f9efdff2ed 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +59 0 0 true 128 1 f5738f36ddaea8dbf34bb82a9ad83a4144b52da4a20fd4f6ac68baf6601d0954 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +60 0 0 true 128 1 bee7b3649a30fd73d0636927c087053db95fbb6779a6879b8fd9d7ba8e5ee87f 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +61 0 0 true 128 1 70cf274a0c0867a30c743863cd561f6c21beb002159269aaffccf8aabd19b361 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +62 0 0 true 128 1 50acf44d23a23d495b15ac94fa178c54870ca45eaf1d3d0c5735e1950b2f9c49 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +63 0 0 true 128 1 d9f3456da6660ee5d8c690d8ff4a3741e9cae3ba6ae9e01570d2531a039b9ffc 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +65 0 0 true 128 1 4932aadcc37f950630bd3368c9f9a2c9f2cf3bbef0a70b62188e92f80a4579ac 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +66 0 0 true 128 1 fa31f8d1b22cd611123e0a6594a2cf073595988c51e4a40322bc1891b24b8ea9 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +67 0 0 true 128 1 0cee3f43430d82a7fdf3c47e43be594e5f2630609be499f0da0f916cdc629ff0 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +68 0 0 true 128 1 53b937950557ae59ea765a77ddf768d205ade40b9d533536d7e5963db82bae94 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +69 0 0 true 128 1 277eabe39f3c041c26be53843649a753d69971a66706439cfb7d10e09d47f833 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +70 0 0 true 128 1 db7ba54337cbf904b29da1e7ff8c4012fde7fdf1a5aa8f7f88c3faebff67a792 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +71 0 0 true 128 1 cd6d98ceb4f45721aeaf001bd84038d952635874c745122c037edbc1b311ee11 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +72 0 0 true 128 1 4d0be0aeb34dc6e937badbb4a0621dd18630b2f5f944d33c9db9ecd5c9261a02 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +73 0 0 true 128 1 7bcee9e1df91c562cfe5035feb0b1960a5e2d42b7b1eb91b9efd31f6bede8fc6 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +74 0 0 true 128 1 3494ccbb48891a4cdf29d7f3726586f4ab49b2d01850314385153d933130f98d 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +75 0 0 true 128 1 a003d215d6b2c61333b842cab1b725bd0209023452adcbc053a7e630e6f99f89 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +76 0 0 true 128 1 adc3aa0dde25a793f7205d2106526025851dd4bf53681552a52b73de20e3f70b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +77 0 0 true 128 1 dc08d76987887151aa558f779c9880a2881eba5b46b3a97de775a14fffa17953 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +78 0 0 true 128 1 c95ec6970dacecb4a40127afc892f071248e025681fe702c580d2fa22dbe3879 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +79 0 0 true 128 1 0c440e2813b082d24833f5310cc548bde774bd847cb5f36d1cb56a13e51dcf70 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +80 0 0 true 128 1 60d1ca1c586c0d63c9e3cb1c904072e32f1c6716c223f717225df5e38fa337d4 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +81 0 0 true 128 1 d46cb69be72232d7aca2933707ec894e2a50d611354355ab55cf528ef254ac3b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +82 0 0 true 128 1 712033931e1e416382f7214cbf099ec5ff3522c395c6231e7597d6bba71706a7 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +83 0 0 true 128 1 b12864d4a177cff6a56ac5badc42f7600e7c0cdaf6ddc9340968f35827194e08 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +84 0 0 true 128 1 e5a1c471a0ef01b70abd17582e76ee575cf9859907e91db97b5315be8bae4edb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +85 0 0 true 128 1 038e7505d20626165995cab81af14db2848c3185238801a78599e7229d7e6221 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +86 0 0 true 128 1 da18d23bdb40e1bd83a49fcbc824338677e45412646c0499f4f96d8116d1294a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +87 0 0 true 128 1 d05b8d164ceed11ea78cd93ecd2ff4b79bcf0d1f6d9b70b640b82d2b8d8ef03a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +88 0 0 true 128 1 ed3cf90b95e4421e68cffb3b35e3b52d4067a9b7084979fd6bd4e9fdfb195833 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +89 0 0 true 128 1 ffa244203bbcca255d8e1f8b2335c00bdce22331032ec3c0179ffe9da89e3c36 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +90 0 0 true 256 1 a5c8ec8eb239c8c8f7ee915ff42cf67e4d137c62c6e60141dfbd72850e6ca716 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb +3840 0 0 true 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +33 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +34 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +35 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +36 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +37 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +38 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +39 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +40 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +41 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +42 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +43 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +44 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +45 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +46 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +47 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +48 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +49 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +50 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +51 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +52 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +53 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +54 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +55 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +56 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +57 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +58 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +59 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +60 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +61 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +62 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +63 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +65 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +66 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +67 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +68 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +69 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +70 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +71 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +72 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +73 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +74 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +75 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +76 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +77 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +78 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +79 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +80 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +81 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +82 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +83 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +84 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +85 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +86 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +87 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +88 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +89 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +90 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +91 0 0 true 256 1 0e6a4ee3fc5e7a2003cb96e18d55d2e36710424dac4a92f2e2dec60bd3b19440 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +92 0 0 true 256 1 f3a5279412d0a65f69a3180f5c25132744008d5714e45c9a6db3b448efd1bff8 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +93 0 0 true 256 1 4684ad4f099310999d7099aa479026648b410a616c265ea57cce5be04d9d5a5d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +94 0 0 true 256 1 c1e0e14e2235b956f587c326ecabd6e645fca49ca9d4ccfc3b54c051846b29b4 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +95 0 0 true 256 1 8ba5eece1ed93890b1a5eb2908488d464910bbb6dfdea4ff64afe1026f018429 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +96 0 0 true 256 1 893bd3363de2b52c2f4395e4f55f462d550b8381a3c1a97a633cc4ee5a67d58b aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +97 0 0 true 256 1 4c1f51aecedb5d9d3c041da5cadd58cacd04c72c075c816bbb07c27849b7f761 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +98 0 0 true 256 1 1b9dd7ef0c41e339b900365da773032a1d01ecfbf86b6ef024696ea2e8636048 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +99 0 0 true 256 1 9927fd71ba266b888006436764be66e248389b8478739f89c4517e7d68c73e72 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +100 0 0 true 256 1 ff6d815ea258e27bfe4079f1df0e5dc85c7d4848a05a117b5100e9bae1ca77d2 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +101 0 0 true 256 1 099d0613c49b6e813e0ecbfe5e68724560bfb349f5eee0be8188f5c3e05d9094 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +102 0 0 true 256 1 397fb0004634916f2fe0851a6b4216af7502d421ca45c92178bbd810529bf1c8 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +103 0 0 true 256 1 7a7ec537ba825db12a3981142ec3d4f8f026d85af8569c8ea29f3ed8817bb23a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +104 0 0 true 256 1 3d856afe77c075d20fda66e9769d07022b2e87f0e5287d3ffc8f61bfa4362113 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +105 0 0 true 256 1 e308e38825e9564bb72cfc394073016057b7ef7e0c5d574f39f82bcb6b666acf aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +106 0 0 true 256 1 19d2909aecef708e46b8975fa3a5faaa4273ac945cfc44d9fc6ef67b2ad69b03 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +107 0 0 true 256 1 ecda6d348c58adb60992ef830e83fd1e283c16a09e5578ca255fa11d64d4e6ab aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +108 0 0 true 256 1 0ea2f08f790389bf4d87f6b56f37f3bed71e64e9c69dedb7d0a06a0fdc18a97b aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +109 0 0 true 256 1 c175f80948434a0b37a3527c57ee103b746d0f000382a4cb7310e74b01287876 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +110 0 0 true 256 1 3ccf3c5fa2832b9545e4a7855ce59b60ec29d158cc650497c87a441e42b527ad aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +111 0 0 true 256 1 2ee4fb24802bb16366ba51118415a11033ad7532db7ccd68bbdf172cd1c1a3f3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +112 0 0 true 256 1 5adcea262dff9be57f812d3a617d93974d6f6e829d1f1377a9b055702dbb43a3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +113 0 0 true 256 1 0b09852fc4c1cf81d3a5e37655d623ae115a2f5a4f9e0f0158c038745a23d322 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +114 0 0 true 256 1 cd1bdca638603605e1e4edd4abb532a262b36c09c63a50cdb77042b73749e0d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +115 0 0 true 256 1 fd5cc68e9f6e732355c1686dfc47044604ed5bab4ac1627dfe8d4af5aed6845c aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +116 0 0 true 256 1 696257e05791a737a20d6d7ec2792c5b87e8222661e9a3ade7520a82fd475b78 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +117 0 0 true 256 1 48cbf09c346d1b0f32ee9f045ecbda4f19ba05b67345585d0b58138108bb3c31 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +118 0 0 true 256 1 265fcd80d136a232bee56a98091ab02ec1fe490bbbae55a5fdca6bf565071417 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +119 0 0 true 256 1 f096c4b39abdd517267b23138b2a9e51675b43dcb379a5986024ccdc925672f9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +120 0 0 true 256 1 6438e5a65e31ed24581e55d4cd491dc47e60dc0c177d1b301900410309c903be aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +121 0 0 true 256 1 b7557914763965b1a7baf856ef6df5761f882dfaa3ed96b680b91f298b6bd6be aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +122 0 0 true 256 1 2b6662b2f50b627465531f437676b8e7b622d82356a7bd6239987f31e59efe67 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +123 0 0 true 256 1 85460cca22cba37f07442aa71d04ca6b7c479ed2a6cc459eb900f070d0e1cc42 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +124 0 0 true 256 1 c9a72134eef697282ce8c872cf9a6c104164ccd51d81dd4248cac7266f049169 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +125 0 0 true 256 1 d567e6dfcddf3dfe00958852e0f1ff440d655410a87897b4bb1195c927633f79 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +126 0 0 true 256 1 bf2019864bf008b6dfc7f6d5288b84b16057eaa4350de51ffb4a9fb294cab65f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +127 0 0 true 256 1 4328e641b307b021434c094958541372b3cfa57860295781966af1327c42346d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +129 0 0 true 256 1 d2f17af24526ec97892d1c519bd850a1e2bee6ef0411b91898051e127a355d7f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +130 0 0 true 256 1 bc0d1762db14673d87bc8d4c93fe83d79bc6c737a1f8e5dd01a01236fbf12909 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +131 0 0 true 256 1 61d3d2c70b2a9827a5c326afa74ab1f1f5ff5822705a7744e472d2303ee87ebc aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +132 0 0 true 256 1 4f2540b180916ee40c87f9c918fb47987a4fd28a554cf61300e7b7c13c306f58 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +133 0 0 true 256 1 dda9353ea35cf39bb9f07345247eee41a5ead2b5adb0fb21281afb389f7e1426 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +134 0 0 true 256 1 64f2c490cd93446beb312ea6587515f365c6df9f59186c72be42e3243a744b1c aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +135 0 0 true 256 1 2250b7b97ca5f791d5ca4b4c482b4f4c04e257e11936d4741571c0a690ef06fb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +136 0 0 true 256 1 ead2765d770d911c7b57760a6ff3d08881dfad51be47e56fa055d178868868ff aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +137 0 0 true 256 1 9b3e1fcb10a80f63d0dff6db9d40e51b29435ad2c49cbadf91b57395c84d1acd aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +138 0 0 true 256 1 80e8368549f275e07711435fb0241144889e5bee84b49579d4cad8e2ee9a3e93 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +139 0 0 true 256 1 245b80b65f88cd54b883a55048b784192556627c45f7317004ca8008041de78a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +140 0 0 true 256 1 d27efea039e72cb592256844594d7ff87c809c9c2050f172f3aebb0680c3c482 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +141 0 0 true 256 1 94fd5ddf81a1d84bda2ea772971332910511dd3cf1792dbce3a21c94a3d54f6e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +142 0 0 true 256 1 5d4867a53133e60c913511c356f6301f1efc625b69d9581fbc1293d637c167c5 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +143 0 0 true 256 1 ab1b9047f7d6ba974d897651bae052e78d02ed1be5fbc8eb3326eeac97f40bfb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +144 0 0 true 256 1 1b5a754c8aec53a0531827d5c0af4d4fc7851dee38da4624e8926cd51df59903 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +145 0 0 true 256 1 5891da9146f0e437275701316bece9912d7e5825e4c78bd7f87e11cbc920873e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +146 0 0 true 256 1 2f026211607597bb95411e9479fa9edf727c666afe1879ef2b7c0c2fc323a8d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +147 0 0 true 256 1 5399860da986247586e4c3145f23ca4e1dca5e1aa9e639469dcfc1e1ba5468d9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +148 0 0 true 256 1 74b626d241a25760a59c9348cd793638d416b3e2c36f4473328402770180b484 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +149 0 0 true 256 1 b224be6b0a90307ba2370ed6bd416684dea417585ca82e2e5e18f9e172f1f0bc aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +150 0 0 true 256 1 74e21e7e01b7f4416db5d8cbb9160fde4ca40ed0a3675233391b15d3de98db2a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +151 0 0 true 256 1 197ba5383f67f081652f2d39eaab2fe75ea6195cb987451354c53764ba14daa0 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +152 0 0 true 256 1 282ffd4270c1c3d130bc4d4c14cb2a382f15e95dc39c4877ff45b18f974acb7d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +153 0 0 true 256 1 0e3c752eda114643f5370d321cd26bb79acbc3a0aa56290ee1761dca9b046e65 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +154 0 0 true 256 1 29fb5f3cf6fce8d521899a60087ea62cf917a38600506eb1ee2023c77cf11518 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +155 0 0 true 256 1 a1bea921ae252bacd324d54cca91ae71eac170304a3e0e6b2509d395047b2dce aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +156 0 0 true 256 1 adcbdccc905b4ea49eae5c3e0f2d2c9cb5446f4e29a54bf04b085ac589adc311 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +157 0 0 true 256 1 68d0fd95a0994bd95504d3e8169b7a375c72b0204d0f79529be23b5a2b0d33ac aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +158 0 0 true 256 1 8b5cc8e091ca0ccbe2a84cf298a9827fd43592da46f413cf9db812a94c70e238 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +159 0 0 true 256 1 c2e6ab45d650a0c1baa9759f9df96643e4f3ed5b2ec147d29e2aa1767eaa2a39 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +160 0 0 true 256 1 ea05d214aa6d13343e18baec3cebee6cda7f8b5acf98a8381436d8072dd084e6 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +161 0 0 true 256 1 6e3da0f456c4c6d8aeab0aa9494c695ae76d98c70051177eecf7f5c7e6f5f6ed aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +162 0 0 true 256 1 a5c5930e2347b05b8983feab6de33d49f6210adbabe391d827a88844fc6c986e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +163 0 0 true 256 1 04f6100f84b2808e0eff54079adec3c82b91b5c9b2f622c2a949c79675147f2a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +164 0 0 true 256 1 8adbe144d476795d6fa952a70eac2adf2b01b696088a50e9972c9c7766299465 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +165 0 0 true 256 1 3dad537864a673f61bba48ef4bbeebd98daa7c55c1de5b2a52ff07062b33ab81 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +166 0 0 true 256 1 2c30d66e2bf9edca12839cc4324d54780c437e676bb7b6fc36b4d0edafaa48d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +167 0 0 true 256 1 10246f2abc45b2d877be7921328395f023ed8eabde0edc3f1913c5244e874e2f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +168 0 0 true 256 1 0f176be4d9d01de2dd31d0f07a35f5880d3de4128ee8e47d26c01da12a5b38a9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +169 0 0 true 256 1 db5f194c063882417eff0507ee0ba0ce439b79638787d2c34d0a40dbf72361d9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +170 0 0 true 256 1 8f8ce4bdd579e04fc09878efe34078290135d4b60a5f735df06a222cdd2fe58d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +171 0 0 true 256 1 824fe8ec4e23947b0c0d82c275e16e57c0a3bd59eb50011c9f228bb039bc97fb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +172 0 0 true 256 1 eb266c7accaf765dc5ad36ed5e580ccb0b0957cc7228c36bf250c155c4f8a356 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +173 0 0 true 256 1 068756ea014db3ece578f2e0ca680e86f68a532813a44c9b8f49fc60e5e5c008 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +174 0 0 true 256 1 3cff71d77364b057013d57a0ee56a5b5833302cbd8636530c002585a6e9cd043 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +175 0 0 true 256 1 647e552a9233e84c6ab5a921aaffc6dc41252fc5962673ebd6e2df7f180802a9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +176 0 0 true 256 1 cae4386e60929dd5d6fc6b65a4817bc7a4634d628d20f5455a2d12cc3f002ec2 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +177 0 0 true 256 1 da5e2c96d595707df915847731a455041edc911424fe134c37d7f35cefdaf314 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +178 0 0 true 256 1 54b041288ef61baa9e060fbace387490fc3852f2c63f25a9772780d3c9213d03 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +179 0 0 true 256 1 c60291a5bce8d065eb3a7fbc74eb72b0825f6c0d7a03201c2c101fea52ed5d85 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +180 0 0 true 256 1 fcc7fdd6d74c6252d0903d013b41e123f2e469346fb6b098947021d8c448e757 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +181 0 0 true 256 1 0def948b618ab0c7dcd64707633bb896bfa25b31a4df5e8d1495bb25defbba72 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +182 0 0 true 256 1 e7ea19ab133930e634de9bb30f9bf030e2b612c1cbc2f9fead992143e88cedc0 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +183 0 0 true 256 1 869a9e09c2d4ae81267e1cab66db3d689814dc353ada065e906165b0c3c5bb69 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +184 0 0 true 256 1 0f7ab8fdfc4fbea1f04e2bf9a84accbc0d870b9d3ceae002e8b0429f3477a41f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +185 0 0 true 256 1 6a27d86da48f827487a83e3c7f6d7961591c6971a99739ee1bd53153330713ca aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af +186 0 0 true 512 2 a7fd0bb9a5eba8b7a9186aa1ebb695cd0d3fe7e5f1377de75a891ee869951c52 96034df7569f4241ab9a2cfb404d1c74fa2815aafbe4b40febf72f1a2cf33121 +4096 0 0 true 512 2 e23c259850469e325ebf6505decf0948a2bfedfbb8e28e66970d6e1675984bcc fd1da2b8dde7bb78f48c63f31348720e258ef6d9d7197b6c3323b44375f75e40 +case overflowing-three-dimensional-hashes +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2009112444 -1024669950 1548280006 true 64 0 8fddd50a279f63427e3898a1ac0522bb30021771558eed1e9bfbd18247c29f1f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2091821115 -1737564729 1005828012 true 64 0 9258bf83ea976eb12b8b0340e1269a0db68a368d89686836609a2175aae95b65 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +187793916 -1132608087 284384781 true 64 0 393db7bf3f2748e1c051bd4e89f56cbe1a3ae8bf9610d37dc04238f33c000f10 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1148401140 -1824399378 -496532742 true 64 0 0314d12be9c0fc442688138b9b19b73b0a59307b69ddf90259f59a81933a0f89 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +1643660664 -1177107225 -636364081 true 64 0 95b87cf89b6dae9ea03f2913b914491c9051966fe6fdafb8226b0c7ef4b7abf8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +193402067 -1942909294 1144036287 true 64 0 f0109644179b144bcd744a4b1e9e1836abec926cd137146526fcd15f1309046d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +931923172 -1331968731 -907722591 true 64 0 9af26c7b21fab8d24fe8dedef97b2011c15bc5d86b13a89043c94e05b1c2372b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +302026495 1188548138 -1044240021 true 64 0 352ae0f383bd929c4c6bd4fb447b80fc61b49efe6b446856e4a9291c49c48b19 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +488910413 -1052404978 634556481 true 64 1 9b3097f5b5d68cf3705c31d01b268d952721e29c4656cee0b8b518cee71ab13c eb292e8c50c7d2b2e51e17929372bcf54a056308520d9c5e3a111eba8855b2cb +239249819 1537778272 1711037189 true 64 1 7614ec20b4f198897e0d201b9ce2480a7dfad9ab9774edfd473d2fcae1c675d2 6df274ff57948bfdfbcb812244f7fcc4cc9fe18c75d110ba14fabd01c828f0dd +-96289821 -1202460919 -155523898 true 64 1 ec0c61cd2dcc24daf0a3836c9c1604e95c8df6134e432883dbfef89f2c3bbe83 49de073cca5ebc4007db6fbbb8f9b361f76f93c9693a20c425ccd58bbcb7335d +-650120678 -868465859 1306902403 true 64 1 8c60fc6a5bb0724f8ca084a877ca41fd8051b76f01897e2014ee441e39536a31 f04c2c3f6ade5f146d3e8efa8a37361bdd32e0d8828c9e079d49923015e02b10 +-1899624886 50559235 15988249 true 64 1 75a996b11c09b62b0099d57f55a7c4d09d68f40256d9d96f8c918b0754dbc4c4 11b976b9a2021971a6d4be38e0b8487451d1d482c052afd73decb691aafe55eb +-389288696 -1203576701 -1909371045 true 64 1 c70f75a2407b3eeebcdec1d4a2d9732330c50142f6ac0fe40b6568c275862cf4 5bf2675fdea343b501d31ce69e5ecbc7ef88c3bcf14cb21a913e38c6e11ae1db +-366916050 40569969 -870199645 true 64 1 21429b7b9d47d35d2ed63300ef72f22ec5d3995c99949356ea4f2d37605f51a5 46cf5233f90e57be26599ef7e313f14ca3a66f61a690bfbc04fb419f845c9169 +-1203878688 -395921575 -1608320167 true 64 1 fc0c8e99b42871436d9d0e6daa6ca71e32abcb74c2afc47be9ba1d3d48d62dcc b28662001689a519e3143d4ba39e514a9c17d9cdc90fe21aa20ada3ee9b2a1a7 +-1267239570 1150204053 -636901305 true 64 1 38b894868221b7433d462e8102e2fe389cdda5278ebf100ccd4347ba5c3ac40f 0745ea2a5c9e650543ec78ab63a18bef9e4212faab5ffd9bb266150e1832a03e +-24107366 -511222227 -2043816781 true 64 1 e43f9f2842ffc55ffac3f082f805bb60945a467fb8ec62d1b56acf45a2d766b9 f31156e75660f87be9a815a48b0dae676f35f9cf9b97766e929b1bdf2e01d500 +1681984333 2116612351 -259971950 true 64 1 040bac3faeaa3b84552bf5d4eb35a08660196e0a65c7767e547bab8a04cab09a 583187a1129041282654e5ad45335292b67711da9195d2a74ebeabc90a12ed25 +961617563 872639189 449061466 true 64 1 b1b53058d20d3446e5f3562775ca9bd80a1f074a708889f6f3356bf612965b25 bfa52e1da75df8295e4ff41b39c80e40e4ef9bdc28ce1b34833c3fc6cff48014 +-1964818907 -1914891690 -1659087823 true 64 1 351ca3b82f75cdd12561bb9ff5695cdb8ca8266111e24468781d25166008a1c1 6ffb230eceb2cb733353322b4a0dfbf541e9ca386d4a0bbaafafd38af629ae6f +-363487355 1584649975 981661586 true 64 1 46b79011d5a9a6bc6dad2765bbfe5ffe33e6abd4fee5eb973e25015e06ffd834 acdcf0e8e805d1a9ce33fbf58f6cfa743cc2ad87c794bc1a73b2cbec2b63c2fb +-1859474891 -1031833768 -1133493853 true 64 1 e5fcf8aa73744a7024193dd41b09b14aeba84b3b3290d5ad59f6bc8f1e2cd76d e2d86564526cc1862b8b91e8020f6b6e851ce2b7101de636e7703359ae8c90a7 +2144777543 596559531 1801985988 true 128 2 1f7ac6d0570e8faceed114720136f2d22c2dfa1ebf80553a94fd3a3df9617a2c e5293472aff59bde4e53c0ebfc8b6888499381dd2db8b5d4c337df3b084af0e4 +-1493583440 -1101309410 -1444482258 true 128 2 ac8c7b4db72b3ea3f35d50f67dd5253c1b50b01f46efd8a392fdae7a3b6c9541 ac6043f322977bc08fd11e9afc574252b21d4f56477d9afeee9e9e95c4553c1e +case signed-hash-comparison +1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-2147450880 0 0 true 64 0 9e7d99338fb137098f555e67dfb1fbd488b38468eb22be493dbe6d5b5fec532c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +64 0 0 true 64 0 d7b2d45a6f8b1ce418f4a94c2a5fb9b98fdd4aac4741d9f89dc167e5df7d063a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-2147450816 0 0 true 64 0 1957748b3f0333a633f003fc37395245a5fb6f79aeb8428a8ee254497541d914 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-65473 0 0 true 64 0 92f43cd2f8389db92fdfb3f2cab8ce75766ec92ed02256dc0f90d15f32279968 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +128 0 0 true 64 0 83cf2e30bb618fe6e4107b3db2e1f7dd614e7fd3634f70d7ec2cf2776ffa264b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-65409 0 0 true 64 0 f5e8d264f33b2e3b68606f6c7fb0a40b02bf2948b2226eed26a82cd71a93e73c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +-2147450752 0 0 true 64 0 ea945e1f0ca5e3ba362ad4e81097ccaaa0470c3371f1e893a017e4342bbca9df e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +192 0 0 true 64 1 00abc47ace9e53ba1cd9d9863b8fd772b1703186765b56fa9c884cb419db8959 e633ea12ff3a67be1378ebc0da2ef85ca2c8b4bc8ae7b2909b8c3804cd24ecdd +-65345 0 0 true 64 1 579350650332cce744176247a448e8c80452f59732a7cbcb0b5c607c3dffc921 717656903d2345cbfceeb394711d4e8292d1a3127bab1d804b8867fe445610f0 +2147450943 0 0 true 64 1 83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832 fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b +-2147450880 0 0 false 64 1 83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832 fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs new file mode 100644 index 00000000000..accf5764cf8 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs @@ -0,0 +1,257 @@ +use std::cmp::Ordering; +use steel_utils::BlockPos; + +use super::{EMPTY, JavaBlockPosSet, spread}; + +pub(super) struct Links { + pub(super) parent: u32, + pub(super) left: u32, + pub(super) right: u32, + pub(super) prev: u32, + pub(super) red: bool, +} + +impl Links { + const EMPTY: Self = Self { + parent: EMPTY, + left: EMPTY, + right: EMPTY, + prev: EMPTY, + red: false, + }; +} + +impl JavaBlockPosSet { + pub(super) fn links(&self, node: u32) -> &Links { + &self.trees[&node] + } + + pub(super) fn links_mut(&mut self, node: u32) -> &mut Links { + match self.trees.get_mut(&node) { + Some(links) => links, + None => panic!("tree bin entry has no tree links"), + } + } + + fn reset_links(&mut self, node: u32) { + self.trees.insert(node, Links::EMPTY); + } + + pub(super) fn find_in_tree(&self, mut node: u32, hash: i32, pos: BlockPos) -> bool { + while node != EMPTY { + let entry = &self.entries[node as usize]; + let links = self.links(node); + let node_hash = spread(entry.pos); + match hash.cmp(&node_hash) { + Ordering::Less => node = links.left, + Ordering::Greater => node = links.right, + Ordering::Equal => { + if entry.pos == pos || self.find_in_tree(links.right, hash, pos) { + return true; + } + node = links.left; + } + } + } + false + } + + fn goes_left(&self, node: u32, parent: u32) -> bool { + let hash = spread(self.entries[node as usize].pos); + let parent_hash = spread(self.entries[parent as usize].pos); + hash < parent_hash || (hash == parent_hash && node < parent) + } + + fn attach(&mut self, root: u32, node: u32) -> u32 { + let mut parent = root; + loop { + let left = self.goes_left(node, parent); + let child = if left { + self.links(parent).left + } else { + self.links(parent).right + }; + if child != EMPTY { + parent = child; + continue; + } + self.links_mut(node).parent = parent; + if left { + self.links_mut(parent).left = node; + } else { + self.links_mut(parent).right = node; + } + return parent; + } + } + + pub(super) fn split_tree(&mut self, index: usize, head: u32, bit: usize) { + let mut counts = [0; 2]; + let mut current = head; + while current != EMPTY { + let next = self.entries[current as usize].next; + let side = + usize::from(spread(self.entries[current as usize].pos) as u32 as usize & bit != 0); + let target = index + side * bit; + let bucket = self.buckets[target]; + self.links_mut(current).prev = if bucket.head == EMPTY { + EMPTY + } else { + bucket.tail + }; + self.append(target, current); + counts[side] += 1; + current = next; + } + for side in 0..2 { + if counts[side] <= 6 { + // Untreeification preserves the split list's linked order. + continue; + } + let target = index + side * bit; + self.buckets[target].tail = EMPTY; + // Java keeps an unsplit tree's shape. + if counts[1 - side] != 0 { + self.treeify(target); + } + } + } + + pub(super) fn treeify(&mut self, bucket: usize) { + let mut node = self.buckets[bucket].head; + let mut root = EMPTY; + let mut prev = EMPTY; + while node != EMPTY { + self.reset_links(node); + self.links_mut(node).prev = prev; + if root == EMPTY { + root = node; + } else { + self.attach(root, node); + root = self.balance_insertion(root, node); + } + prev = node; + node = self.entries[node as usize].next; + } + self.buckets[bucket].tail = EMPTY; + self.move_root_to_front(bucket, root); + } + + pub(super) fn insert_tree(&mut self, bucket: usize, node: u32) { + self.reset_links(node); + let root = self.buckets[bucket].head; + let parent = self.attach(root, node); + let next = self.entries[parent as usize].next; + self.entries[parent as usize].next = node; + self.entries[node as usize].next = next; + self.links_mut(node).prev = parent; + if next != EMPTY { + self.links_mut(next).prev = node; + } + let root = self.balance_insertion(root, node); + self.move_root_to_front(bucket, root); + } + + fn move_root_to_front(&mut self, bucket: usize, root: u32) { + debug_assert_eq!(self.links(root).parent, EMPTY); + let first = self.buckets[bucket].head; + if root == first { + return; + } + let next = self.entries[root as usize].next; + let prev = self.links(root).prev; + if next != EMPTY { + self.links_mut(next).prev = prev; + } + if prev != EMPTY { + self.entries[prev as usize].next = next; + } + self.links_mut(first).prev = root; + self.entries[root as usize].next = first; + self.links_mut(root).prev = EMPTY; + self.buckets[bucket].head = root; + } + + // Both rotations change only the tree; iteration still follows next/prev. + fn rotate(&mut self, mut root: u32, node: u32, left: bool) -> u32 { + let pivot = if left { + self.links(node).right + } else { + self.links(node).left + }; + let inner = if left { + self.links(pivot).left + } else { + self.links(pivot).right + }; + if left { + self.links_mut(node).right = inner; + } else { + self.links_mut(node).left = inner; + } + if inner != EMPTY { + self.links_mut(inner).parent = node; + } + let parent = self.links(node).parent; + self.links_mut(pivot).parent = parent; + if parent == EMPTY { + root = pivot; + self.links_mut(pivot).red = false; + } else if self.links(parent).left == node { + self.links_mut(parent).left = pivot; + } else { + self.links_mut(parent).right = pivot; + } + if left { + self.links_mut(pivot).left = node; + } else { + self.links_mut(pivot).right = node; + } + self.links_mut(node).parent = pivot; + root + } + + fn balance_insertion(&mut self, mut root: u32, mut node: u32) -> u32 { + self.links_mut(node).red = true; + loop { + let mut parent = self.links(node).parent; + if parent == EMPTY { + self.links_mut(node).red = false; + return node; + } + let mut grandparent = self.links(parent).parent; + if !self.links(parent).red || grandparent == EMPTY { + return root; + } + let left = parent == self.links(grandparent).left; + let uncle = if left { + self.links(grandparent).right + } else { + self.links(grandparent).left + }; + if uncle != EMPTY && self.links(uncle).red { + self.links_mut(uncle).red = false; + self.links_mut(parent).red = false; + self.links_mut(grandparent).red = true; + node = grandparent; + continue; + } + let inner = if left { + self.links(parent).right + } else { + self.links(parent).left + }; + if node == inner { + node = parent; + root = self.rotate(root, node, left); + parent = self.links(node).parent; + grandparent = self.links(parent).parent; + } + self.links_mut(parent).red = false; + if grandparent != EMPTY { + self.links_mut(grandparent).red = true; + root = self.rotate(root, grandparent, !left); + } + } + } +} diff --git a/steel-core/src/world/explosion/server/block_rays/tests.rs b/steel-core/src/world/explosion/server/block_rays/tests.rs index 4166ac12c4d..64ddec5b094 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests.rs @@ -19,6 +19,8 @@ use crate::test_support::{fresh_test_world, insert_ready_full_chunk}; use crate::world::explosion::default_block_explosion_resistance; use crate::world::{BlockInteraction, DefaultExplosionDamageCalculator}; +mod gameplay; + const FIXED_RANDOM_SAMPLE: f32 = 0.5; const STANDARD_TNT_RADIUS: f32 = 4.0; const MAX_TNT_EXPLOSION_POWER: f32 = 128.0; diff --git a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs new file mode 100644 index 00000000000..ff1ee619552 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs @@ -0,0 +1,164 @@ +//! Radius-4 TNT callback order and fuses captured from Minecraft 26.2 on JDK 25. + +use std::collections::BTreeSet; +use std::fmt::Write as _; +use std::sync::Arc; + +use crate::entity::{Entity as _, SharedEntity, entities::PrimedTntEntity, next_entity_id}; +use crate::world::{DefaultExplosionDamageCalculator, Explosion, ExplosionDamageCalculator, World}; +use steel_registry::vanilla_entities; +use steel_utils::{Downcast as _, WorldAabb}; + +use super::*; + +struct RecordingCalculator(SyncMutex>); + +impl ExplosionDamageCalculator for RecordingCalculator { + fn should_block_explode( + &self, + _explosion: &dyn Explosion, + _world: &World, + pos: BlockPos, + _state: BlockStateId, + _power: f32, + ) -> bool { + self.0.lock().push(pos); + true + } +} + +fn format_positions(positions: &[BlockPos]) -> String { + let mut text = String::new(); + for pos in positions { + writeln!(text, "{} {} {}", pos.x(), pos.y(), pos.z()).expect("string write"); + } + text +} + +#[test] +fn radius_four_tnt_matches_vanilla_iteration_callbacks_and_fuses() { + let expected = include_str!("vanilla-tnt-order.txt"); + compare_ray_mode(expected, true); + compare_ray_mode(expected, false); +} + +fn compare_ray_mode(expected: &str, recorded_rays: bool) { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("explosion_java_tree_gameplay"); + let world = &fixture.world; + let tnt = populate_fixture(world); + let source: SharedEntity = Arc::new(PrimedTntEntity::primed( + &vanilla_entities::TNT, + next_entity_id(), + DVec3::new(8.5, 65.0, 8.5), + world, + None, + )); + let center = DVec3::new( + 8.5, + 65.0 + f64::from(source.base().dimensions().height) * 0.0625, + 8.5, + ); + let calculator = RecordingCalculator(SyncMutex::new(Vec::new())); + let explosion = ServerExplosion::new( + world, + Some(source), + None, + recorded_rays.then_some(&calculator as &dyn ExplosionDamageCalculator), + (!recorded_rays) + .then_some(&DefaultExplosionDamageCalculator as &dyn ImmutableExplosionBlockCalculator), + center, + 4.0, + false, + BlockInteraction::DestroyWithDecay, + ); + if !recorded_rays { + let bounds = explosion + .immutable_ray_region_bounds(0) + .expect("fixture ray region"); + world + .try_with_block_region(bounds, |region| assert!(region.has_complete_data())) + .expect("fixture has a resident ray region"); + } + world.set_random_seed_for_test(1); + let mut affected = explosion.calculate_exploded_positions_from_level_random(); + let mut hashes = BTreeSet::new(); + for &pos in &affected { + assert!( + hashes.insert(pos.java_hash_code()), + "fixture has equal full hashes" + ); + } + let iteration = format_positions(&affected); + explosion.interact_with_blocks(&mut affected); + let callbacks = format_positions(&affected); + let mut fuses = Vec::new(); + for entity in world.get_entities_in_aabb(&WorldAabb::new(-16.0, 52.0, -16.0, 32.0, 81.0, 32.0)) + { + if let Some(primed) = entity.downcast_ref::() { + let pos = BlockPos::from(primed.position()); + fuses.push(format!( + "{} {} {} {}", + pos.x(), + pos.y(), + pos.z(), + primed.fuse() + )); + } + } + fuses.sort(); + let mut actual = format!( + "iteration\n{iteration}callbacks\n{callbacks}fuses\n{}\nsurviving-tnt\n", + fuses.join("\n") + ); + for &pos in &tnt { + if world.get_block_state(pos).get_block() == &vanilla_blocks::TNT { + actual.push_str(&format_positions(&[pos])); + } + } + let (insertion_digest, expected) = expected.split_once('\n').expect("capture insertion digest"); + if recorded_rays { + let insertions = format_positions(&calculator.0.lock()); + let mut digest = String::new(); + for byte in Sha256::digest(insertions.as_bytes()) { + write!(digest, "{byte:02x}").expect("string write"); + } + assert_eq!(format!("insertions-sha256 {digest}"), insertion_digest); + } + assert_eq!(actual, expected); +} + +fn populate_fixture(world: &Arc) -> [BlockPos; 3] { + for x in -1..=1 { + for z in -1..=1 { + insert_ready_full_chunk(world, ChunkPos::new(x, z)); + } + } + for x in -4..=20 { + for z in -4..=20 { + for y in 52..=80 { + if !(65..=67).contains(&y) || (x + z - 16_i32).abs() > 1 { + assert!(world.set_block( + BlockPos::new(x, y, z), + vanilla_blocks::OBSIDIAN.default_state(), + UpdateFlags::UPDATE_NONE, + )); + } + } + } + } + let tnt = [ + BlockPos::new(6, 65, 9), + BlockPos::new(10, 65, 5), + BlockPos::new(8, 65, 7), + ]; + for &pos in &tnt { + assert!(world.set_block( + pos, + vanilla_blocks::TNT.default_state(), + UpdateFlags::UPDATE_NONE + )); + } + tnt +} diff --git a/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt b/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt new file mode 100644 index 00000000000..fad156d9288 --- /dev/null +++ b/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt @@ -0,0 +1,160 @@ +insertions-sha256 69372ac012665fd6c906d8a602e817de12ee5e6ba2293f64d27a0876344300e5 +iteration +5 66 10 +3 66 12 +7 66 8 +9 66 6 +11 66 4 +4 66 12 +6 66 10 +8 66 8 +10 66 6 +12 66 4 +5 66 12 +7 66 10 +9 66 8 +11 66 6 +5 67 10 +7 67 8 +9 67 6 +11 67 4 +4 67 12 +6 67 10 +8 67 8 +10 67 6 +12 67 4 +6 65 9 +9 67 8 +4 65 11 +5 67 12 +8 65 7 +7 67 10 +10 65 5 +11 67 6 +7 65 9 +5 65 11 +9 65 7 +11 65 5 +6 65 11 +8 65 9 +10 65 7 +12 65 5 +4 66 11 +6 66 9 +8 66 7 +10 66 5 +5 66 11 +7 66 9 +9 66 7 +11 66 5 +6 66 11 +4 66 13 +8 66 9 +10 66 7 +12 66 5 +4 67 11 +6 67 9 +8 67 7 +10 67 5 +5 67 11 +7 67 9 +9 67 7 +11 67 5 +7 65 8 +5 65 10 +6 67 11 +9 65 6 +8 67 9 +10 67 7 +11 65 4 +12 67 5 +8 65 8 +6 65 10 +4 65 12 +10 65 6 +7 65 10 +5 65 12 +9 65 8 +11 65 6 +callbacks +9 65 6 +8 67 9 +8 65 8 +4 65 11 +9 66 8 +11 66 5 +9 66 6 +5 66 12 +7 67 9 +10 66 7 +6 66 9 +6 65 10 +8 65 7 +5 65 11 +10 65 5 +12 66 5 +3 66 12 +9 65 8 +10 67 5 +11 65 5 +4 67 11 +4 66 12 +7 67 10 +8 66 7 +7 67 8 +12 65 5 +4 66 13 +10 66 6 +9 67 6 +11 67 6 +5 67 11 +8 67 8 +7 66 8 +5 65 12 +6 65 11 +7 66 10 +6 65 9 +9 65 7 +8 66 9 +10 67 6 +5 66 10 +4 65 12 +9 66 7 +12 67 5 +8 65 9 +10 65 6 +6 67 9 +6 67 10 +11 65 6 +9 67 8 +8 66 8 +6 66 10 +10 67 7 +7 66 9 +10 66 5 +11 66 4 +5 67 10 +9 67 7 +7 65 9 +5 67 12 +11 67 4 +5 65 10 +6 66 11 +8 67 7 +12 67 4 +6 67 11 +5 66 11 +4 66 11 +11 67 5 +11 65 4 +10 65 7 +11 66 6 +12 66 4 +4 67 12 +7 65 8 +7 65 10 +fuses +10 65 5 25 +6 65 9 22 +8 65 7 20 +surviving-tnt From b5bc0af504825d0341f2607c542573462648e9c1 Mon Sep 17 00:00:00 2001 From: kdcokenny Date: Mon, 12 Oct 2026 00:17:07 +0000 Subject: [PATCH 06/26] fix: scope explosion randomness without a world RNG Seed a fresh RNG per explosion for ray powers, shuffle and fire. Use runtime randomness for TNT launch angles and shortened fuses instead of retaining a shared world stream. Keep captured iteration and callback ordering exact with injected draws. Check TNT priming order and fuse bounds; the old list-only set still fails the ordering regression. Preserve the tree-bin implementation and Vanilla capture. --- .../src/behavior/blocks/redstone/tnt_block.rs | 2 +- .../entities/objects/explosives/primed_tnt.rs | 21 +++-- .../objects/explosives/primed_tnt/tests.rs | 20 +---- steel-core/src/world/explosion/server.rs | 9 +- .../world/explosion/server/block_effects.rs | 9 +- .../src/world/explosion/server/block_rays.rs | 15 ---- .../server/block_rays/tests/gameplay.rs | 90 +++++++++++++------ steel-core/src/world/mod.rs | 15 ---- 8 files changed, 87 insertions(+), 94 deletions(-) diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block.rs b/steel-core/src/behavior/blocks/redstone/tnt_block.rs index b4f2ab10b27..fe6a80587cc 100644 --- a/steel-core/src/behavior/blocks/redstone/tnt_block.rs +++ b/steel-core/src/behavior/blocks/redstone/tnt_block.rs @@ -151,7 +151,7 @@ impl BlockBehavior for TntBlock { } let entity = Self::primed_entity(world, pos, explosion.indirect_source_entity()); - entity.set_fuse(PrimedTntEntity::get_random_short_fuse(world, entity.fuse())); + entity.set_fuse(PrimedTntEntity::get_random_short_fuse(entity.fuse())); if let Err(error) = world.try_add_entity(entity) { log::debug!("failed to add primed TNT: {error}"); } diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs index 47ec04236d4..c87ced8ce0c 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -16,7 +16,6 @@ use steel_registry::vanilla_blocks; use steel_registry::vanilla_entity_data::PrimedTntEntityData; use steel_registry::vanilla_game_rules::TNT_EXPLODES; use steel_utils::locks::SyncMutex; -use steel_utils::random::Random; use steel_utils::{BlockPos, BlockStateId, DowncastType, DowncastTypeKey}; use crate::block_entity::block_state_nbt; @@ -168,15 +167,19 @@ impl PrimedTntEntity { owner: Option<&dyn Entity>, ) -> Self { let entity = Self::new(entity_type, id, position, Arc::downgrade(world)); - // Vanilla narrows 2Ï€ to `float` before Java widens it for this multiplication. - let angle = world.with_random(Random::next_f64) * f64::from(TAU); - entity.set_velocity(DVec3::new( + entity.set_velocity(Self::initial_velocity(rand::random())); + entity.state.lock().owner = owner.map(|owner| EntityReference::from_uuid(owner.uuid())); + entity + } + + fn initial_velocity(random: f64) -> DVec3 { + // Vanilla narrows 2Ï€ to float before multiplying by the random sample. + let angle = random * f64::from(TAU); + DVec3::new( -angle.sin() * INITIAL_HORIZONTAL_SPEED, f64::from(INITIAL_VERTICAL_SPEED), -angle.cos() * INITIAL_HORIZONTAL_SPEED, - )); - entity.state.lock().owner = owner.map(|owner| EntityReference::from_uuid(owner.uuid())); - entity + ) } /// Creates a primed TNT entity from saved data. @@ -221,8 +224,8 @@ impl PrimedTntEntity { /// Returns Vanilla's shortened chain-reaction fuse for `fuse`. #[must_use] - pub fn get_random_short_fuse(world: &World, fuse: i32) -> i32 { - world.with_random(|random| random.next_i32_bounded((fuse / 4).max(1))) + fuse / 8 + pub fn get_random_short_fuse(fuse: i32) -> i32 { + rand::random_range(0..(fuse / 4).max(1)) + fuse / 8 } fn owner_reference(&self) -> Option { diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs index 7ec0ac25438..4f0603175df 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs @@ -1,7 +1,7 @@ use steel_protocol::packets::game::RelativeMovement; use steel_registry::blocks::properties::BlockStateProperties; use steel_registry::{init_vanilla_registry, vanilla_damage_types, vanilla_entities}; -use steel_utils::random::legacy_random::LegacyRandom; +use steel_utils::random::{Random as _, legacy_random::LegacyRandom}; use steel_utils::types::UpdateFlags; use steel_utils::{ChunkPos, Identifier}; @@ -48,25 +48,11 @@ fn priming_applies_vanilla_float_tau_launch_motion() { // Java's `Math.sin` may differ from Rust's in the final bits. const INITIAL_MOTION_TOLERANCE: f64 = 1.0e-12; - let fixture = fresh_test_world("primed_tnt_initial_motion"); - let world = &fixture.world; - world.set_random_seed_for_test(SEED); - - let entity = PrimedTntEntity::primed( - &vanilla_entities::TNT, - next_entity_id(), - test_tnt_position(), - world, - None, - ); - - let velocity = entity.velocity(); + let mut random = LegacyRandom::from_seed(SEED as u64); + let velocity = PrimedTntEntity::initial_velocity(random.next_f64()); assert!((velocity.x - EXPECTED_INITIAL_MOTION_X).abs() <= INITIAL_MOTION_TOLERANCE); assert_eq!(velocity.y.to_bits(), f64::from(0.2_f32).to_bits()); assert!((velocity.z - EXPECTED_INITIAL_MOTION_Z).abs() <= INITIAL_MOTION_TOLERANCE); - let mut expected = LegacyRandom::from_seed(SEED as u64); - let _ = expected.next_f64(); - assert_eq!(world.with_random(Random::next_i64), expected.next_i64()); } #[test] diff --git a/steel-core/src/world/explosion/server.rs b/steel-core/src/world/explosion/server.rs index 788fccef577..047b4882609 100644 --- a/steel-core/src/world/explosion/server.rs +++ b/steel-core/src/world/explosion/server.rs @@ -6,6 +6,7 @@ use glam::DVec3; use rustc_hash::FxHashMap; use steel_registry::vanilla_game_rules::MOB_GRIEFING; use steel_registry::{vanilla_damage_types, vanilla_entities, vanilla_game_events}; +use steel_utils::random::{Random as _, legacy_random::LegacyRandom}; use crate::entity::damage::DamageSource; use crate::entity::{Entity, SharedEntity}; @@ -89,13 +90,15 @@ impl<'a> ServerExplosion<'a> { self.center, &GameEventContext::new(self.source.as_deref(), None), ); - let mut affected = self.calculate_exploded_positions_from_level_random(); + // Live randomness is scoped to each blast rather than stored on the world. + let mut random = LegacyRandom::from_seed(rand::random()); + let mut affected = self.calculate_exploded_positions(|| random.next_f32()); self.hurt_entities(); if self.interacts_with_blocks() { - self.interact_with_blocks(&mut affected); + self.interact_with_blocks(&mut affected, &mut random); } if self.fire { - self.create_fire(&affected); + self.create_fire(&affected, &mut random); } affected.len() } diff --git a/steel-core/src/world/explosion/server/block_effects.rs b/steel-core/src/world/explosion/server/block_effects.rs index c7cb9994332..528f9681dd6 100644 --- a/steel-core/src/world/explosion/server/block_effects.rs +++ b/steel-core/src/world/explosion/server/block_effects.rs @@ -17,8 +17,8 @@ const FIRE_CHANCE_DENOMINATOR: i32 = 3; const MAX_DROPS_PER_COMBINED_STACK: i32 = 16; impl ServerExplosion<'_> { - pub(super) fn interact_with_blocks(&self, affected: &mut [BlockPos]) { - self.world.with_random(|random| random.shuffle(affected)); + pub(super) fn interact_with_blocks(&self, affected: &mut [BlockPos], random: &mut impl Random) { + random.shuffle(affected); let mut stacks = Vec::new(); let mut full_chunks = LocalFullChunkHolderCache::new(); @@ -38,10 +38,9 @@ impl ServerExplosion<'_> { } } - pub(super) fn create_fire(&self, affected: &[BlockPos]) { + pub(super) fn create_fire(&self, affected: &[BlockPos], random: &mut impl Random) { self.create_fire_with(affected, || { - self.world - .with_random(|random| random.next_i32_bounded(FIRE_CHANCE_DENOMINATOR)) + random.next_i32_bounded(FIRE_CHANCE_DENOMINATOR) }); } diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs index 0fcf71454db..516833ad701 100644 --- a/steel-core/src/world/explosion/server/block_rays.rs +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -4,7 +4,6 @@ use std::sync::LazyLock; use glam::DVec3; use steel_registry::blocks::block_state_ext::BlockStateExt as _; -use steel_utils::random::Random; use steel_utils::{BlockPos, BlockStateId}; use crate::world::explosion::{ @@ -118,20 +117,6 @@ impl ExplosionWorldBounds { } impl ServerExplosion<'_> { - pub(super) fn calculate_exploded_positions_from_level_random(&self) -> Vec { - let Some(calculator) = self.immutable_calculator_for_rays() else { - return self.calculate_exploded_positions_sequential(|| { - self.world.with_random(Random::next_f32) - }); - }; - - let powers = self - .world - .with_random(|random| self.draw_immutable_ray_powers(|| random.next_f32())); - self.calculate_immutable_ray_powers(&powers, calculator) - } - - #[cfg(test)] pub(super) fn calculate_exploded_positions( &self, mut next_float: impl FnMut() -> f32, diff --git a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs index ff1ee619552..c59822632fd 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use crate::entity::{Entity as _, SharedEntity, entities::PrimedTntEntity, next_entity_id}; use crate::world::{DefaultExplosionDamageCalculator, Explosion, ExplosionDamageCalculator, World}; use steel_registry::vanilla_entities; +use steel_utils::random::legacy_random::LegacyRandom; use steel_utils::{Downcast as _, WorldAabb}; use super::*; @@ -36,7 +37,7 @@ fn format_positions(positions: &[BlockPos]) -> String { } #[test] -fn radius_four_tnt_matches_vanilla_iteration_callbacks_and_fuses() { +fn radius_four_tnt_matches_vanilla_order_and_fuse_bounds() { let expected = include_str!("vanilla-tnt-order.txt"); compare_ray_mode(expected, true); compare_ray_mode(expected, false); @@ -81,8 +82,8 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { .try_with_block_region(bounds, |region| assert!(region.has_complete_data())) .expect("fixture has a resident ray region"); } - world.set_random_seed_for_test(1); - let mut affected = explosion.calculate_exploded_positions_from_level_random(); + let mut random = LegacyRandom::from_seed(1); + let mut affected = explosion.calculate_exploded_positions(|| random.next_f32()); let mut hashes = BTreeSet::new(); for &pos in &affected { assert!( @@ -91,32 +92,8 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { ); } let iteration = format_positions(&affected); - explosion.interact_with_blocks(&mut affected); + explosion.interact_with_blocks(&mut affected, &mut random); let callbacks = format_positions(&affected); - let mut fuses = Vec::new(); - for entity in world.get_entities_in_aabb(&WorldAabb::new(-16.0, 52.0, -16.0, 32.0, 81.0, 32.0)) - { - if let Some(primed) = entity.downcast_ref::() { - let pos = BlockPos::from(primed.position()); - fuses.push(format!( - "{} {} {} {}", - pos.x(), - pos.y(), - pos.z(), - primed.fuse() - )); - } - } - fuses.sort(); - let mut actual = format!( - "iteration\n{iteration}callbacks\n{callbacks}fuses\n{}\nsurviving-tnt\n", - fuses.join("\n") - ); - for &pos in &tnt { - if world.get_block_state(pos).get_block() == &vanilla_blocks::TNT { - actual.push_str(&format_positions(&[pos])); - } - } let (insertion_digest, expected) = expected.split_once('\n').expect("capture insertion digest"); if recorded_rays { let insertions = format_positions(&calculator.0.lock()); @@ -126,7 +103,62 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { } assert_eq!(format!("insertions-sha256 {digest}"), insertion_digest); } - assert_eq!(actual, expected); + let (expected_order, expected) = expected + .split_once("fuses\n") + .expect("capture fuse section"); + assert_eq!( + format!("iteration\n{iteration}callbacks\n{callbacks}"), + expected_order + ); + let (expected_fuses, expected_survivors) = expected + .split_once("surviving-tnt\n") + .expect("capture survivor section"); + assert_tnt_priming(world, &affected, &tnt, expected_fuses); + let survivors = tnt + .into_iter() + .filter(|&pos| world.get_block_state(pos).get_block() == &vanilla_blocks::TNT) + .collect::>(); + assert_eq!(format_positions(&survivors), expected_survivors); +} + +fn assert_tnt_priming( + world: &Arc, + affected: &[BlockPos], + tnt: &[BlockPos], + expected_fuses: &str, +) { + let mut primed_positions = Vec::new(); + for entity in world.get_entities_in_aabb(&WorldAabb::new(-16.0, 52.0, -16.0, 32.0, 81.0, 32.0)) + { + if let Some(primed) = entity.downcast_ref::() { + assert!((10..30).contains(&primed.fuse()), "fuse={}", primed.fuse()); + primed_positions.push((entity.id(), BlockPos::from(primed.position()))); + } + } + primed_positions.sort_unstable_by_key(|&(id, _)| id); + let primed_positions = primed_positions + .into_iter() + .map(|(_, pos)| pos) + .collect::>(); + assert_eq!( + primed_positions, + affected + .iter() + .copied() + .filter(|pos| tnt.contains(pos)) + .collect::>(), + "TNT must be primed in callback order" + ); + let mut actual_positions = format_positions(&primed_positions) + .lines() + .map(str::to_owned) + .collect::>(); + actual_positions.sort(); + let expected_positions = expected_fuses + .lines() + .map(|line| line.rsplit_once(' ').expect("captured fuse").0) + .collect::>(); + assert_eq!(actual_positions, expected_positions); } fn populate_fixture(world: &Arc) -> [BlockPos; 3] { diff --git a/steel-core/src/world/mod.rs b/steel-core/src/world/mod.rs index 5e2772e03ec..3cc9a8a2004 100644 --- a/steel-core/src/world/mod.rs +++ b/steel-core/src/world/mod.rs @@ -265,8 +265,6 @@ pub struct World { pub(crate) game_time: Arc, /// Per-world saved data storage. pub(crate) saved_data: SavedDataManager, - /// Vanilla's live per-level random source. - random: SyncMutex, /// Runtime world border state. world_border: SyncMutex, /// Vanilla sleeping player counts for night-skip checks. @@ -322,16 +320,6 @@ pub struct World { } impl World { - /// Runs an operation against Vanilla's live per-level random source. - pub(crate) fn with_random(&self, operation: impl FnOnce(&mut RandomSource) -> T) -> T { - operation(&mut self.random.lock()) - } - - #[cfg(test)] - pub(crate) fn set_random_seed_for_test(&self, seed: i64) { - *self.random.lock() = RandomSource::Legacy(LegacyRandom::from_seed(seed as u64)); - } - /// Creates a new world with custom configuration. /// /// This allows specifying storage backend (disk or RAM-only) and other options. @@ -452,9 +440,6 @@ impl World { game_time: level_data.game_time_handle(), level_data: SyncRwLock::new(level_data), saved_data, - random: SyncMutex::new(RandomSource::Legacy(LegacyRandom::from_seed( - rand::random::(), - ))), world_border: SyncMutex::new(world_border), sleep_status: SyncMutex::new(sleep_status::SleepStatus::default()), view_distance, From 55f94351ade9ed9e7043191902a9e69a5b78826a Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Thu, 22 Oct 2026 04:03:38 +0200 Subject: [PATCH 07/26] fmt --- .../src/entity/entities/objects/display_ui/item_frame.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs index af2a46259e4..82a58f7bbe6 100644 --- a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs +++ b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs @@ -21,8 +21,8 @@ use crate::entity::{ Entity, EntityBase, EntityBaseLoad, EntityBaseState, EntityMoveError, EntitySyncedData, ItemFrame, SharedEntity, }; -use crate::world::{Explosion, World}; use crate::physics::{MoveResult, MoverType}; +use crate::world::{Explosion, World}; /// Item frame state needed by end-city structure markers. /// @@ -280,7 +280,7 @@ impl Entity for ItemFrameEntity { fn synced_data(&self) -> Option<&dyn EntitySyncedData> { Some(&self.entity_data) } - + fn hurt(&self, world: &World, source: &DamageSource, amount: f32) -> bool { self.hurt_block_attached_entity(world, source, amount) } From a60a499b0c8584e16dbe04e66cfb17cf17d9d95b Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Thu, 22 Oct 2026 04:04:46 +0200 Subject: [PATCH 08/26] fmt --- steel-protocol/src/packets/game/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steel-protocol/src/packets/game/mod.rs b/steel-protocol/src/packets/game/mod.rs index 23d926f79f5..109ee036a55 100644 --- a/steel-protocol/src/packets/game/mod.rs +++ b/steel-protocol/src/packets/game/mod.rs @@ -126,10 +126,10 @@ pub use c_container_set_slot::CContainerSetSlot; pub use c_cooldown::CCooldown; pub use c_damage_event::CDamageEvent; pub use c_entity_event::CEntityEvent; +pub use c_entity_position_sync::{CEntityPositionSync, PositionPath, PositionStep}; pub use c_explode::{ CExplode, ExplosionParticleInfo, ExplosionParticlePalette, ExplosionParticleWeightError, }; -pub use c_entity_position_sync::{CEntityPositionSync, PositionPath, PositionStep}; pub use c_game_event::CGameEvent; pub use c_game_event::GameEventType; pub use c_hurt_animation::CHurtAnimation; From fe93fedf6ed3600df6a41db916e07aefa67ec79a Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:18:27 +0200 Subject: [PATCH 09/26] fixes --- steel-core/src/behavior/blocks/redstone/mod.rs | 1 + .../src/behavior/blocks/redstone/tnt_block.rs | 7 +++++-- steel-core/src/entity/base/mod.rs | 2 +- steel-core/src/entity/block_effects.rs | 7 +------ .../entities/objects/display_ui/item_frame.rs | 4 ---- .../objects/display_ui/leash_fence_knot.rs | 14 +++++++------- steel-core/src/entity/entity/mod.rs | 4 ++++ steel-core/src/entity/manager/tests/ticking.rs | 3 ++- steel-core/src/player/game_mode/adventure_mode.rs | 2 +- steel-core/src/player/game_mode/mod.rs | 1 + steel-core/src/world/explosion/mod.rs | 5 +++++ steel-core/src/worldgen/region.rs | 6 ++++-- steel-protocol/src/packets/game/c_explode.rs | 5 +++++ 13 files changed, 37 insertions(+), 24 deletions(-) diff --git a/steel-core/src/behavior/blocks/redstone/mod.rs b/steel-core/src/behavior/blocks/redstone/mod.rs index 3c4e9d73d64..7fd32c0aaad 100644 --- a/steel-core/src/behavior/blocks/redstone/mod.rs +++ b/steel-core/src/behavior/blocks/redstone/mod.rs @@ -44,6 +44,7 @@ pub use redstone_lamp_block::*; pub use redstone_ore_block::*; pub use redstone_torch_block::*; pub use target_block::*; +pub use tnt_block::*; pub use tripwire::*; pub use weathering_lightning_rod_block::*; pub use wire::*; diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block.rs b/steel-core/src/behavior/blocks/redstone/tnt_block.rs index fe6a80587cc..7d8d774e545 100644 --- a/steel-core/src/behavior/blocks/redstone/tnt_block.rs +++ b/steel-core/src/behavior/blocks/redstone/tnt_block.rs @@ -13,7 +13,7 @@ use steel_registry::vanilla_game_rules::TNT_EXPLODES; use steel_registry::{ sound_events, vanilla_blocks, vanilla_entities, vanilla_game_events, vanilla_items, }; -use steel_utils::types::{InteractionHand, UpdateFlags}; +use steel_utils::types::{GameType, InteractionHand, UpdateFlags}; use steel_utils::{BlockPos, BlockStateId, translations}; use text_components::TextComponent; @@ -23,6 +23,7 @@ use crate::behavior::{ use crate::entity::projectile::Projectile; use crate::entity::{Entity, entities::PrimedTntEntity, next_entity_id}; use crate::player::Player; +use crate::player::game_mode::can_break; use crate::world::game_event::GameEventContext; use crate::world::{ClipHitResult, Explosion, SignalGetter as _, World}; @@ -177,7 +178,9 @@ impl BlockBehavior for TntBlock { return InteractionResult::TryEmptyHandInteraction; } - if !Self::prime(world, pos, Some(player)) { + let can_ignite = player.game_mode() != GameType::Adventure + || inv.with_item(|item| can_break(item, world, pos)); + if !can_ignite || !Self::prime(world, pos, Some(player)) { if !world.get_game_rule(&TNT_EXPLODES) { Self::send_disabled_message(player); return InteractionResult::Pass; diff --git a/steel-core/src/entity/base/mod.rs b/steel-core/src/entity/base/mod.rs index 927933a6141..44ea2d9abba 100644 --- a/steel-core/src/entity/base/mod.rs +++ b/steel-core/src/entity/base/mod.rs @@ -138,7 +138,7 @@ impl EntityMovementTrace { self.final_movements_this_tick.clone() } - fn last_for_block_effects(&self) -> Vec { + fn last_for_block_effects(&self) -> SmallVec<[EntityMovement; 1]> { self.final_movements_this_tick.clone() } } diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index 10d5101fbb4..e018bda3a76 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -246,12 +246,7 @@ fn add_collisions_along_travel( corner_block, opposite_corner, travel, - |pos| { - if visited.insert(pos) && !visitor(pos, iterations) { - return false; - } - true - }, + |pos| !visited.insert(pos) || visitor(pos, iterations), ) { return None; } diff --git a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs index 82a58f7bbe6..c8cb7b2c835 100644 --- a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs +++ b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs @@ -277,10 +277,6 @@ impl Entity for ItemFrameEntity { || !explosion.should_affect_blocklike_entities() } - fn synced_data(&self) -> Option<&dyn EntitySyncedData> { - Some(&self.entity_data) - } - fn hurt(&self, world: &World, source: &DamageSource, amount: f32) -> bool { self.hurt_block_attached_entity(world, source, amount) } diff --git a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs index bda895a48cd..4cee1faa9ef 100644 --- a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs +++ b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs @@ -255,6 +255,13 @@ impl Entity for LeashFenceKnotEntity { self.is_pickable_block_attached_entity() } + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + explosion + .direct_source_entity() + .is_some_and(Entity::is_in_water) + || !explosion.should_affect_blocklike_entities() + } + fn hurt(&self, world: &World, source: &DamageSource, amount: f32) -> bool { self.hurt_block_attached_entity(world, source, amount) } @@ -314,13 +321,6 @@ impl BlockAttachedEntity for LeashFenceKnotEntity { .set_bounding_box(Self::knot_bounding_box(self.entity_type, pos)); Ok(()) } - - fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { - explosion - .direct_source_entity() - .is_some_and(Entity::is_in_water) - || !explosion.should_affect_blocklike_entities() - } } #[cfg(test)] diff --git a/steel-core/src/entity/entity/mod.rs b/steel-core/src/entity/entity/mod.rs index 3172da25b4f..805ffc134ab 100644 --- a/steel-core/src/entity/entity/mod.rs +++ b/steel-core/src/entity/entity/mod.rs @@ -3348,6 +3348,10 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { /// Moves the entity with collision detection. /// /// Updates position, `on_ground`, velocity (on collision), and returns collision info. + #[expect( + clippy::too_many_lines, + reason = "keeps collision, landing, and movement callbacks in execution order" + )] fn move_entity(self: Arc, mover_type: MoverType, delta: DVec3) -> Option { let world = self.level()?; if self.no_physics() { diff --git a/steel-core/src/entity/manager/tests/ticking.rs b/steel-core/src/entity/manager/tests/ticking.rs index 38368b1bb1f..e278320cffe 100644 --- a/steel-core/src/entity/manager/tests/ticking.rs +++ b/steel-core/src/entity/manager/tests/ticking.rs @@ -35,7 +35,8 @@ impl RelationshipTickTestEntity { } fn run_action(&self) { - match mem::take(&mut *self.action.lock()) { + let action = mem::take(&mut *self.action.lock()); + match action { RelationshipTickAction::None => {} RelationshipTickAction::StopRiding => self.stop_riding(), } diff --git a/steel-core/src/player/game_mode/adventure_mode.rs b/steel-core/src/player/game_mode/adventure_mode.rs index 6d0eaa0d057..cc4f4d7fbe0 100644 --- a/steel-core/src/player/game_mode/adventure_mode.rs +++ b/steel-core/src/player/game_mode/adventure_mode.rs @@ -32,7 +32,7 @@ impl Player { /// Is it breakable= #[must_use] -pub(super) fn can_break(item: &ItemStack, world: &World, pos: BlockPos) -> bool { +pub(crate) fn can_break(item: &ItemStack, world: &World, pos: BlockPos) -> bool { item.get(CAN_BREAK) .is_some_and(|predicate| matches(predicate, world, pos)) } diff --git a/steel-core/src/player/game_mode/mod.rs b/steel-core/src/player/game_mode/mod.rs index 69a1bef48db..e58a6d0f0a5 100644 --- a/steel-core/src/player/game_mode/mod.rs +++ b/steel-core/src/player/game_mode/mod.rs @@ -55,6 +55,7 @@ mod player_methods; mod raycast; mod swing; +pub(crate) use adventure_mode::can_break; pub(super) use block_breaking::BlockBreakingManager; pub use item_interaction::{use_item, use_item_on}; diff --git a/steel-core/src/world/explosion/mod.rs b/steel-core/src/world/explosion/mod.rs index a0badbdb735..e17fd0cfc3f 100644 --- a/steel-core/src/world/explosion/mod.rs +++ b/steel-core/src/world/explosion/mod.rs @@ -447,6 +447,10 @@ impl World { } else { options.large_explosion_particle }; + + let play_sound = explosion + .direct_source_entity() + .is_none_or(|source| !source.is_silent()); let packet_block_count = i32::try_from(affected_block_count).unwrap_or(i32::MAX); self.players.iter_players(|_, player| { @@ -459,6 +463,7 @@ impl World { explosion_particle: explosion_particle.clone(), explosion_sound: options.explosion_sound.clone(), block_particles: options.block_particles.clone(), + play_sound, }); } true diff --git a/steel-core/src/worldgen/region.rs b/steel-core/src/worldgen/region.rs index d557d37028e..11881ea0ab0 100644 --- a/steel-core/src/worldgen/region.rs +++ b/steel-core/src/worldgen/region.rs @@ -1060,7 +1060,8 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); + if let Some(state) = replacement(old_state, section_base_y + local_y as i32) { let old_state = Self::set_bulk_block_state( chunk.holder, @@ -1080,7 +1081,8 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); + if let Some(state) = replacement(old_state, section_base_y + local_y as i32) { let old_state = Self::set_bulk_block_state( chunk.holder, diff --git a/steel-protocol/src/packets/game/c_explode.rs b/steel-protocol/src/packets/game/c_explode.rs index 07a1586e8a6..6bdf65a905b 100644 --- a/steel-protocol/src/packets/game/c_explode.rs +++ b/steel-protocol/src/packets/game/c_explode.rs @@ -74,6 +74,7 @@ pub struct CExplode { pub explosion_particle: ParticleData, pub explosion_sound: SoundEventHolder, pub block_particles: ExplosionParticlePalette, + pub play_sound: bool, } #[cfg(test)] @@ -117,6 +118,7 @@ mod tests { 3, ), ]), + play_sound: true, }; let mut encoded = Vec::new(); @@ -148,6 +150,7 @@ mod tests { expected.extend_from_slice(&1.0_f32.to_be_bytes()); expected.extend_from_slice(&0.75_f32.to_be_bytes()); assert!(VarInt(3).write(&mut expected).is_ok()); + expected.push(1); assert_eq!(encoded, expected); } @@ -167,6 +170,7 @@ mod tests { fixed_range: Some(24.0), }, block_particles: palette(Vec::new()), + play_sound: false, }; let mut encoded = Vec::new(); @@ -183,6 +187,7 @@ mod tests { expected.extend_from_slice(&24.0_f32.to_be_bytes()); assert!(VarInt(0).write(&mut expected).is_ok()); + expected.push(0); assert_eq!(encoded, expected); } From 57c24bb0edf7b67151e14c313407f33a1ee9d89a Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Sun, 25 Oct 2026 01:21:30 +0200 Subject: [PATCH 10/26] fmt --- steel-core/src/entity/entity/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steel-core/src/entity/entity/mod.rs b/steel-core/src/entity/entity/mod.rs index cf677da720d..e0c688badc8 100644 --- a/steel-core/src/entity/entity/mod.rs +++ b/steel-core/src/entity/entity/mod.rs @@ -3632,7 +3632,7 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { if on_ground && self.fall_distance() > 0.0 { self.send_landing_particles(world, on_state, pos); } - + if !self.is_in_water() && vertical_movement < 0.0 { self.base().accumulate_fall_distance(vertical_movement); } From 876ed573122c5f83570d8c42e3ba07c4cf3a366d Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Sun, 25 Oct 2026 01:33:44 +0200 Subject: [PATCH 11/26] clippy --- steel-core/src/behavior/blocks/redstone/mod.rs | 1 + steel-core/src/entity/base/mod.rs | 2 +- .../entities/objects/display_ui/item_frame.rs | 4 ---- .../objects/display_ui/leash_fence_knot.rs | 14 +++++++------- steel-core/src/worldgen/region.rs | 4 ++-- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/steel-core/src/behavior/blocks/redstone/mod.rs b/steel-core/src/behavior/blocks/redstone/mod.rs index 3c4e9d73d64..7fd32c0aaad 100644 --- a/steel-core/src/behavior/blocks/redstone/mod.rs +++ b/steel-core/src/behavior/blocks/redstone/mod.rs @@ -44,6 +44,7 @@ pub use redstone_lamp_block::*; pub use redstone_ore_block::*; pub use redstone_torch_block::*; pub use target_block::*; +pub use tnt_block::*; pub use tripwire::*; pub use weathering_lightning_rod_block::*; pub use wire::*; diff --git a/steel-core/src/entity/base/mod.rs b/steel-core/src/entity/base/mod.rs index 927933a6141..44ea2d9abba 100644 --- a/steel-core/src/entity/base/mod.rs +++ b/steel-core/src/entity/base/mod.rs @@ -138,7 +138,7 @@ impl EntityMovementTrace { self.final_movements_this_tick.clone() } - fn last_for_block_effects(&self) -> Vec { + fn last_for_block_effects(&self) -> SmallVec<[EntityMovement; 1]> { self.final_movements_this_tick.clone() } } diff --git a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs index 82a58f7bbe6..c8cb7b2c835 100644 --- a/steel-core/src/entity/entities/objects/display_ui/item_frame.rs +++ b/steel-core/src/entity/entities/objects/display_ui/item_frame.rs @@ -277,10 +277,6 @@ impl Entity for ItemFrameEntity { || !explosion.should_affect_blocklike_entities() } - fn synced_data(&self) -> Option<&dyn EntitySyncedData> { - Some(&self.entity_data) - } - fn hurt(&self, world: &World, source: &DamageSource, amount: f32) -> bool { self.hurt_block_attached_entity(world, source, amount) } diff --git a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs index bda895a48cd..4c1c84feaab 100644 --- a/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs +++ b/steel-core/src/entity/entities/objects/display_ui/leash_fence_knot.rs @@ -278,6 +278,13 @@ impl Entity for LeashFenceKnotEntity { fn try_set_position(&self, pos: DVec3) -> Result<(), EntityMoveError> { self.try_set_position_block_attached_entity(pos) } + + fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { + explosion + .direct_source_entity() + .is_some_and(Entity::is_in_water) + || !explosion.should_affect_blocklike_entities() + } } impl BlockAttachedEntity for LeashFenceKnotEntity { @@ -314,13 +321,6 @@ impl BlockAttachedEntity for LeashFenceKnotEntity { .set_bounding_box(Self::knot_bounding_box(self.entity_type, pos)); Ok(()) } - - fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { - explosion - .direct_source_entity() - .is_some_and(Entity::is_in_water) - || !explosion.should_affect_blocklike_entities() - } } #[cfg(test)] diff --git a/steel-core/src/worldgen/region.rs b/steel-core/src/worldgen/region.rs index d557d37028e..6da802d1037 100644 --- a/steel-core/src/worldgen/region.rs +++ b/steel-core/src/worldgen/region.rs @@ -1060,7 +1060,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); if let Some(state) = replacement(old_state, section_base_y + local_y as i32) { let old_state = Self::set_bulk_block_state( chunk.holder, @@ -1080,7 +1080,7 @@ impl<'region, 'world, 'profile> WorldGenBulkSectionAccess<'region, 'world, 'prof let local_x = usize::from(pos.x()); let local_y = usize::from(pos.y()); let local_z = usize::from(pos.z()); - let old_state = section_guard.states.get(local_x, local_y, local_z); + let old_state = section_guard.states().get(local_x, local_y, local_z); if let Some(state) = replacement(old_state, section_base_y + local_y as i32) { let old_state = Self::set_bulk_block_state( chunk.holder, From 1cf104b8c602a91c552a8a940eedcc6ca21fff16 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Sun, 25 Oct 2026 01:38:34 +0200 Subject: [PATCH 12/26] clippy --- steel-core/src/entity/block_effects.rs | 7 +------ steel-core/src/entity/entity/mod.rs | 4 ++++ steel-core/src/entity/manager/tests/ticking.rs | 3 ++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index 10d5101fbb4..e018bda3a76 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -246,12 +246,7 @@ fn add_collisions_along_travel( corner_block, opposite_corner, travel, - |pos| { - if visited.insert(pos) && !visitor(pos, iterations) { - return false; - } - true - }, + |pos| !visited.insert(pos) || visitor(pos, iterations), ) { return None; } diff --git a/steel-core/src/entity/entity/mod.rs b/steel-core/src/entity/entity/mod.rs index e0c688badc8..5c032f2d721 100644 --- a/steel-core/src/entity/entity/mod.rs +++ b/steel-core/src/entity/entity/mod.rs @@ -3393,6 +3393,10 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { /// Moves the entity with collision detection. /// /// Updates position, `on_ground`, velocity (on collision), and returns collision info. + #[expect( + clippy::too_many_lines, + reason = "keeps vanilla movement phases in order" + )] fn move_entity(self: Arc, mover_type: MoverType, delta: DVec3) -> Option { let world = self.level()?; if self.no_physics() { diff --git a/steel-core/src/entity/manager/tests/ticking.rs b/steel-core/src/entity/manager/tests/ticking.rs index 38368b1bb1f..e278320cffe 100644 --- a/steel-core/src/entity/manager/tests/ticking.rs +++ b/steel-core/src/entity/manager/tests/ticking.rs @@ -35,7 +35,8 @@ impl RelationshipTickTestEntity { } fn run_action(&self) { - match mem::take(&mut *self.action.lock()) { + let action = mem::take(&mut *self.action.lock()); + match action { RelationshipTickAction::None => {} RelationshipTickAction::StopRiding => self.stop_riding(), } From 79e77de2619ed6a0a8b034b30bfbdf8124260c8e Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 26 Oct 2026 00:03:59 +0100 Subject: [PATCH 13/26] remove some magic number and comment --- steel-core/src/entity/block_effects.rs | 25 +++++++++++-------- .../entities/objects/explosives/primed_tnt.rs | 3 ++- steel-core/src/entity/entity/mod.rs | 12 +++------ .../server/block_rays/java_block_pos_set.rs | 1 + .../block_rays/java_block_pos_set/tree.rs | 4 +-- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/steel-core/src/entity/block_effects.rs b/steel-core/src/entity/block_effects.rs index e018bda3a76..cbf83752b20 100644 --- a/steel-core/src/entity/block_effects.rs +++ b/steel-core/src/entity/block_effects.rs @@ -11,7 +11,7 @@ const CLIP_EPSILON: f64 = 1.0e-7; const CORNER_HIT_EPSILON: f64 = 1.0e-5; const ENTITY_INSIDE_SWEEP_INFLATE_EPSILON: f64 = 1.0e-7; -/// Deduplicates up to 16 positions without allocating, then switches to a hash set. +/// Deduplicates small sweeps without allocating, then switches to a hash set. pub(super) enum VisitedBlockPositions { Inline(SmallVec<[BlockPos; 16]>), Hashed(FxHashSet), @@ -518,25 +518,29 @@ mod tests { use super::*; #[test] - fn visited_positions_stay_inline_through_sixteen_and_preserve_duplicates_after_seventeen() { + fn visited_positions_deduplicate_across_inline_capacity_boundary() { let mut visited = VisitedBlockPositions::default(); - for x in 0..16 { + let VisitedBlockPositions::Inline(inline) = &visited else { + panic!("new visited positions must start inline"); + }; + let inline_capacity = inline.inline_size() as i32; + for x in 0..inline_capacity { assert!(visited.insert(BlockPos::new(x, 64, 0))); } - for x in 0..16 { + for x in 0..inline_capacity { assert!(!visited.insert(BlockPos::new(x, 64, 0))); } let VisitedBlockPositions::Inline(inline) = &visited else { - panic!("sixteen unique positions must remain inline"); + panic!("positions up to the inline capacity must remain inline"); }; assert!(!inline.spilled()); - assert!(visited.insert(BlockPos::new(16, 64, 0))); + assert!(visited.insert(BlockPos::new(inline_capacity, 64, 0))); let VisitedBlockPositions::Hashed(hashed) = &visited else { - panic!("the seventeenth unique position must use the hash set"); + panic!("exceeding the inline capacity must use the hash set"); }; - assert_eq!(hashed.len(), 17); - for x in 0..17 { + assert_eq!(hashed.len(), inline_capacity as usize + 1); + for x in 0..=inline_capacity { assert!(!visited.insert(BlockPos::new(x, 64, 0))); } } @@ -641,7 +645,7 @@ mod tests { } #[test] - fn long_trace_uses_spill_path_without_duplicates() { + fn long_trace_visits_each_block_once_in_order() { let positions = visited_positions( DVec3::new(0.5, 64.0, 0.5), DVec3::new(24.5, 64.0, 0.5), @@ -651,7 +655,6 @@ mod tests { .map(|x| BlockPos::new(x, 64, 0)) .collect::>(); - assert!(positions.len() > 16); assert_eq!(positions, expected); } } diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs index c87ced8ce0c..44b0aaa6d86 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -223,6 +223,8 @@ impl PrimedTntEntity { } /// Returns Vanilla's shortened chain-reaction fuse for `fuse`. + /// Adds one eighth of the fuse to a random duration below one quarter, + /// with a minimum random bound of one tick. #[must_use] pub fn get_random_short_fuse(fuse: i32) -> i32 { rand::random_range(0..(fuse / 4).max(1)) + fuse / 8 @@ -300,7 +302,6 @@ impl Entity for PrimedTntEntity { } else { self.refresh_fluid_contact_with_currents(); self.base.reset_fall_distance_in_water(); - // Vanilla's remaining-fuse smoke particle is client-local. } } diff --git a/steel-core/src/entity/entity/mod.rs b/steel-core/src/entity/entity/mod.rs index 5c032f2d721..0e2ba29a19f 100644 --- a/steel-core/src/entity/entity/mod.rs +++ b/steel-core/src/entity/entity/mod.rs @@ -612,15 +612,11 @@ pub trait Entity: EntityEventSource + ErasedType + Send + Sync + 'static { false } - /// Returns whether this entity must participate in conservative hard-collision queries. + /// Whether movement queries must consider this entity for hard collisions. /// - /// Hard collisions are the entity shapes consulted by vanilla movement collision, not - /// ordinary entity pushing. Returning `false` is an immutable capability promise that this - /// entity can never return `true` from [`Self::can_be_collided_with`] for any source and that - /// its [`Self::can_collide_with`] implementation never broadens the default rule as a source. - /// Implementations are conservative by default so plugin-defined collision rules cannot be - /// omitted from the movement broad phase. The world entity manager may cache this capability - /// for the entity's registered lifetime, including chunk-unload retention. + /// Returning `false` promises [`Self::can_be_collided_with`] is always false and + /// [`Self::can_collide_with`] never broadens the default rule. This capability may be + /// cached for the registered lifetime, including chunk-unload retention. fn is_hard_collision_relevant(&self) -> bool { true } diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs index afc8bd88191..9be19112aab 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs @@ -17,6 +17,7 @@ mod tree; const EMPTY: u32 = u32::MAX; const TREEIFY_THRESHOLD: usize = 8; +const UNTREEIFY_THRESHOLD: usize = 6; const MIN_TREEIFY_CAPACITY: usize = 64; #[derive(Default)] diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs index accf5764cf8..4a99cd0ea9b 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs +++ b/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use steel_utils::BlockPos; -use super::{EMPTY, JavaBlockPosSet, spread}; +use super::{EMPTY, JavaBlockPosSet, UNTREEIFY_THRESHOLD, spread}; pub(super) struct Links { pub(super) parent: u32, @@ -104,7 +104,7 @@ impl JavaBlockPosSet { current = next; } for side in 0..2 { - if counts[side] <= 6 { + if counts[side] <= UNTREEIFY_THRESHOLD { // Untreeification preserves the split list's linked order. continue; } From 270ea4b9f8b76d16271c6393a3016917dfff9a6e Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 26 Oct 2026 00:36:11 +0100 Subject: [PATCH 14/26] merge both JavaBlockPosSet --- .../mod.rs} | 106 ++- steel-core/src/java_block_pos_set/removal.rs | 164 ++++ .../java_block_pos_set/tests.rs | 79 +- .../tests/HashSetOracle.java | 0 .../tests/openjdk25-sequential-identity.txt | 0 .../java_block_pos_set/tests/openjdk25.txt | 0 .../java_block_pos_set/tree.rs | 25 +- .../src/java_block_pos_set/worldgen_tests.rs | 145 ++++ steel-core/src/lib.rs | 1 + .../src/world/explosion/server/block_rays.rs | 7 +- .../explosion/server/block_rays/cache.rs | 6 +- .../explosion/server/block_rays/tests.rs | 6 +- .../feature/features/tree/decorators.rs | 2 +- .../worldgen/feature/features/tree/leaves.rs | 2 +- .../src/worldgen/feature/features/tree/mod.rs | 2 +- .../feature/features/vegetation_patch.rs | 2 +- steel-core/src/worldgen/feature/mod.rs | 1 - .../worldgen/feature/vanilla_collections.rs | 778 ------------------ 18 files changed, 514 insertions(+), 812 deletions(-) rename steel-core/src/{world/explosion/server/block_rays/java_block_pos_set.rs => java_block_pos_set/mod.rs} (59%) create mode 100644 steel-core/src/java_block_pos_set/removal.rs rename steel-core/src/{world/explosion/server/block_rays => }/java_block_pos_set/tests.rs (61%) rename steel-core/src/{world/explosion/server/block_rays => }/java_block_pos_set/tests/HashSetOracle.java (100%) rename steel-core/src/{world/explosion/server/block_rays => }/java_block_pos_set/tests/openjdk25-sequential-identity.txt (100%) rename steel-core/src/{world/explosion/server/block_rays => }/java_block_pos_set/tests/openjdk25.txt (100%) rename steel-core/src/{world/explosion/server/block_rays => }/java_block_pos_set/tree.rs (90%) create mode 100644 steel-core/src/java_block_pos_set/worldgen_tests.rs delete mode 100644 steel-core/src/worldgen/feature/vanilla_collections.rs diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs b/steel-core/src/java_block_pos_set/mod.rs similarity index 59% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs rename to steel-core/src/java_block_pos_set/mod.rs index 9be19112aab..4735ecd11f8 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set.rs +++ b/steel-core/src/java_block_pos_set/mod.rs @@ -1,9 +1,9 @@ -//! Explosion-local emulation of Java `HashSet` iteration order. +//! Shared Java `HashSet` bucket and tree-bin ordering. //! //! Distinct hashes follow `OpenJDK` 25's linked tree-bin order. `BlockPos` inherits -//! `Comparable`, so equal-hash ties use JVM identities. Here ties use insertion -//! indices, matching increasing JVM identities for plain `BlockPos` keys. Different -//! identities can also change other entries' order in that bin and its later splits. +//! `Comparable`, so equal-hash ties use JVM identities. Steel preserves its +//! existing deterministic policies: coordinate ordering for worldgen, and insertion +//! indices for explosions (matching increasing JVM identities). use std::mem; use std::vec::IntoIter; @@ -11,9 +11,12 @@ use std::vec::IntoIter; use rustc_hash::FxHashMap; use steel_utils::BlockPos; +mod removal; #[cfg(test)] mod tests; mod tree; +#[cfg(test)] +mod worldgen_tests; const EMPTY: u32 = u32::MAX; const TREEIFY_THRESHOLD: usize = 8; @@ -21,11 +24,21 @@ const UNTREEIFY_THRESHOLD: usize = 6; const MIN_TREEIFY_CAPACITY: usize = 64; #[derive(Default)] -pub(super) struct JavaBlockPosSet { +pub(crate) enum EqualHashOrder { + #[default] + Coordinates, + Insertion, +} + +/// Java bucket ordering with a deterministic policy for equal-hash tree entries. +#[derive(Default)] +pub(crate) struct JavaBlockPosSet { buckets: Vec, entries: Vec, // Allocate tree links only for entries that have actually entered a tree bin. trees: FxHashMap, + len: usize, + equal_hash_order: EqualHashOrder, } #[derive(Clone, Copy)] @@ -52,8 +65,15 @@ struct Entry { } impl JavaBlockPosSet { + pub(crate) fn new(equal_hash_order: EqualHashOrder) -> Self { + Self { + equal_hash_order, + ..Self::default() + } + } + #[inline] - pub(super) fn insert(&mut self, pos: BlockPos) -> bool { + pub(crate) fn insert(&mut self, pos: BlockPos) -> bool { if self.buckets.is_empty() { self.buckets.resize(16, Bucket::EMPTY); self.entries.reserve(16); @@ -63,7 +83,7 @@ impl JavaBlockPosSet { let bucket = self.buckets[index]; let mut bin_len = 0; if bucket.is_tree() { - if self.find_in_tree(bucket.head, hash, pos) { + if self.find_in_tree(self.tree_root(bucket.head), hash, pos) { return false; } } else { @@ -86,6 +106,7 @@ impl JavaBlockPosSet { "JavaBlockPosSet entry arena exhausted its u32 index space" ); self.entries.push(Entry { pos, next: EMPTY }); + self.len += 1; if bucket.is_tree() { self.insert_tree(index, entry_index); } else { @@ -99,14 +120,79 @@ impl JavaBlockPosSet { } } } - if self.entries.len() > self.buckets.len() * 3 / 4 { + if self.len > self.buckets.len() * 3 / 4 { self.resize(); } true } + pub(crate) fn contains(&self, pos: BlockPos) -> bool { + if self.buckets.is_empty() { + return false; + } + let hash = spread(pos); + let bucket = self.buckets[hash as u32 as usize & (self.buckets.len() - 1)]; + if bucket.is_tree() { + return self.find_in_tree(self.tree_root(bucket.head), hash, pos); + } + let mut current = bucket.head; + while current != EMPTY { + let entry = &self.entries[current as usize]; + if entry.pos == pos { + return true; + } + current = entry.next; + } + false + } + + pub(crate) const fn is_empty(&self) -> bool { + self.len == 0 + } + + pub(crate) fn java_order(&self) -> impl Iterator { + self.buckets.iter().flat_map(move |bucket| { + let mut current = bucket.head; + std::iter::from_fn(move || { + if current == EMPTY { + return None; + } + let entry = &self.entries[current as usize]; + current = entry.next; + Some(&entry.pos) + }) + }) + } + + pub(crate) fn java_ordered_positions(&self) -> Vec { + self.java_order().copied().collect() + } + + pub(crate) fn pop_java_ordered_position(&mut self) -> Option { + let index = self + .buckets + .iter() + .position(|bucket| bucket.head != EMPTY)?; + let bucket = self.buckets[index]; + let node = bucket.head; + let entry = &self.entries[node as usize]; + let pos = entry.pos; + self.buckets[index].head = entry.next; + self.len -= 1; + if self.buckets[index].head == EMPTY { + self.buckets[index].tail = 0; + } else if bucket.is_tree() { + let next = self.buckets[index].head; + self.links_mut(next).prev = EMPTY; + // Iterator removal preserves linked order rather than moving the tree root. + self.remove_from_tree(node); + } + self.trees.remove(&node); + Some(pos) + } + #[cfg(test)] - pub(super) const fn bucket_count(&self) -> usize { + pub(crate) const fn bucket_count(&self) -> usize { self.buckets.len() } @@ -151,7 +237,7 @@ impl IntoIterator for JavaBlockPosSet { type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { - let mut ordered = Vec::with_capacity(self.entries.len()); + let mut ordered = Vec::with_capacity(self.len); for bucket in self.buckets { let mut current = bucket.head; while current != EMPTY { diff --git a/steel-core/src/java_block_pos_set/removal.rs b/steel-core/src/java_block_pos_set/removal.rs new file mode 100644 index 00000000000..1850f26d984 --- /dev/null +++ b/steel-core/src/java_block_pos_set/removal.rs @@ -0,0 +1,164 @@ +use super::{EMPTY, JavaBlockPosSet}; + +impl JavaBlockPosSet { + pub(super) fn remove_from_tree(&mut self, node: u32) { + let mut root = self.tree_root(node); + let left = self.links(node).left; + let right = self.links(node).right; + let replacement = if left != EMPTY && right != EMPTY { + let mut successor = right; + while self.links(successor).left != EMPTY { + successor = self.links(successor).left; + } + let successor_was_red = self.links(successor).red; + self.links_mut(successor).red = self.links(node).red; + self.links_mut(node).red = successor_was_red; + let successor_right = self.links(successor).right; + let node_parent = self.links(node).parent; + + if successor == right { + self.links_mut(node).parent = successor; + self.links_mut(successor).right = node; + } else { + let successor_parent = self.links(successor).parent; + self.links_mut(node).parent = successor_parent; + if successor_parent != EMPTY { + if self.links(successor_parent).left == successor { + self.links_mut(successor_parent).left = node; + } else { + self.links_mut(successor_parent).right = node; + } + } + self.links_mut(successor).right = right; + self.links_mut(right).parent = successor; + } + + self.links_mut(node).left = EMPTY; + self.links_mut(node).right = successor_right; + if successor_right != EMPTY { + self.links_mut(successor_right).parent = node; + } + self.links_mut(successor).left = left; + self.links_mut(left).parent = successor; + self.links_mut(successor).parent = node_parent; + if node_parent == EMPTY { + root = successor; + } else if self.links(node_parent).left == node { + self.links_mut(node_parent).left = successor; + } else { + self.links_mut(node_parent).right = successor; + } + + if successor_right == EMPTY { + node + } else { + successor_right + } + } else if left != EMPTY { + left + } else if right != EMPTY { + right + } else { + node + }; + + if replacement != node { + let parent = self.links(node).parent; + self.links_mut(replacement).parent = parent; + if parent == EMPTY { + root = replacement; + self.links_mut(replacement).red = false; + } else if self.links(parent).left == node { + self.links_mut(parent).left = replacement; + } else { + self.links_mut(parent).right = replacement; + } + self.links_mut(node).parent = EMPTY; + self.links_mut(node).left = EMPTY; + self.links_mut(node).right = EMPTY; + } + + if !self.links(node).red { + self.balance_deletion(root, replacement); + } + + let parent = self.links(node).parent; + if replacement == node && parent != EMPTY { + self.links_mut(node).parent = EMPTY; + if self.links(parent).left == node { + self.links_mut(parent).left = EMPTY; + } else if self.links(parent).right == node { + self.links_mut(parent).right = EMPTY; + } + } + } + + fn is_red(&self, node: u32) -> bool { + node != EMPTY && self.links(node).red + } + + fn child(&self, node: u32, left: bool) -> u32 { + if left { + self.links(node).left + } else { + self.links(node).right + } + } + + fn balance_deletion(&mut self, mut root: u32, mut node: u32) { + loop { + if node == root { + return; + } + let mut parent = self.links(node).parent; + if parent == EMPTY { + self.links_mut(node).red = false; + return; + } + if self.links(node).red { + self.links_mut(node).red = false; + return; + } + + let left = self.links(parent).left == node; + let mut sibling = self.child(parent, !left); + if self.is_red(sibling) { + self.links_mut(sibling).red = false; + self.links_mut(parent).red = true; + root = self.rotate(root, parent, left); + parent = self.links(node).parent; + sibling = self.child(parent, !left); + } + if sibling == EMPTY { + node = parent; + continue; + } + let inner = self.child(sibling, left); + let outer = self.child(sibling, !left); + if !self.is_red(inner) && !self.is_red(outer) { + self.links_mut(sibling).red = true; + node = parent; + continue; + } + if !self.is_red(outer) { + if inner != EMPTY { + self.links_mut(inner).red = false; + } + self.links_mut(sibling).red = true; + root = self.rotate(root, sibling, !left); + parent = self.links(node).parent; + sibling = self.child(parent, !left); + } + if sibling != EMPTY { + self.links_mut(sibling).red = self.links(parent).red; + let outer = self.child(sibling, !left); + if outer != EMPTY { + self.links_mut(outer).red = false; + } + } + self.links_mut(parent).red = false; + root = self.rotate(root, parent, left); + node = root; + } + } +} diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs b/steel-core/src/java_block_pos_set/tests.rs similarity index 61% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs rename to steel-core/src/java_block_pos_set/tests.rs index e063cf85f38..825dbd997a4 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests.rs +++ b/steel-core/src/java_block_pos_set/tests.rs @@ -12,13 +12,13 @@ fn linked_order_matches_openjdk_25_after_each_insertion_and_resize() { } fn check_oracle(oracle: &str) { - let mut set = JavaBlockPosSet::default(); + let mut set = JavaBlockPosSet::new(EqualHashOrder::Insertion); let mut case = ""; let mut saw_tree = false; for line in oracle.lines() { if let Some(name) = line.strip_prefix("case ") { case = name; - set = JavaBlockPosSet::default(); + set = JavaBlockPosSet::new(EqualHashOrder::Insertion); continue; } let fields = line.split_whitespace().collect::>(); @@ -94,9 +94,10 @@ fn check_links(set: &JavaBlockPosSet) { if !bucket.is_tree() { continue; } - assert_eq!(set.links(bucket.head).parent, EMPTY, "head is not the root"); + let root = set.tree_root(bucket.head); + assert_eq!(set.links(root).parent, EMPTY, "root has a parent"); let mut tree = BTreeSet::new(); - let mut pending = vec![bucket.head]; + let mut pending = vec![root]; while let Some(node) = pending.pop() { assert!(tree.insert(node), "tree cycle or repeated child"); let links = set.links(node); @@ -112,7 +113,75 @@ fn check_links(set: &JavaBlockPosSet) { "tree and iteration list contain different entries" ); } - assert_eq!(all.len(), set.entries.len(), "unreachable set entry"); + assert_eq!(all.len(), set.len, "unreachable set entry"); +} + +#[test] +fn removing_tree_heads_preserves_order_and_lookup_for_both_policies() { + for policy in [EqualHashOrder::Coordinates, EqualHashOrder::Insertion] { + let mut set = JavaBlockPosSet::new(policy); + for z in (0..64).rev() { + assert!(set.insert(BlockPos::new(-961 * z, 0, z))); + } + let positions = set.java_ordered_positions(); + assert!(set.buckets.iter().any(|bucket| bucket.is_tree())); + for (index, &pos) in positions.iter().enumerate() { + assert_eq!(set.pop_java_ordered_position(), Some(pos)); + assert!(!set.contains(pos)); + assert_eq!(set.java_ordered_positions(), positions[index + 1..]); + for &remaining in &positions[index + 1..] { + assert!(set.contains(remaining)); + } + check_links(&set); + } + assert!(set.is_empty()); + assert_eq!(set.pop_java_ordered_position(), None); + assert!(set.insert(positions[0])); + assert_eq!(set.java_ordered_positions(), [positions[0]]); + check_links(&set); + } +} + +#[test] +fn removed_entries_do_not_count_towards_the_resize_threshold() { + let mut set = JavaBlockPosSet::default(); + for x in 0..12 { + assert!(set.insert(BlockPos::new(x, 0, 0))); + } + let capacity = set.bucket_count(); + for _ in 0..11 { + assert!(set.pop_java_ordered_position().is_some()); + } + for x in 12..23 { + assert!(set.insert(BlockPos::new(x, 0, 0))); + } + assert_eq!(set.bucket_count(), capacity); + check_links(&set); +} + +#[test] +fn resize_untreeifies_small_unsplit_bins_after_iterator_removals() { + for policy in [EqualHashOrder::Coordinates, EqualHashOrder::Insertion] { + let mut set = JavaBlockPosSet::new(policy); + for x in 0..12 { + assert!(set.insert(BlockPos::new(x * 128, 0, 0))); + } + assert!(set.buckets.iter().any(|bucket| bucket.is_tree())); + for _ in 0..6 { + assert!(set.pop_java_ordered_position().is_some()); + } + let positions = set.java_ordered_positions(); + assert!(set.buckets.iter().any(|bucket| bucket.is_tree())); + + set.resize(); + + assert!(!set.buckets.iter().any(|bucket| bucket.is_tree())); + assert_eq!(set.java_ordered_positions(), positions); + for pos in positions { + assert!(set.contains(pos)); + } + check_links(&set); + } } fn tree_shape(set: &JavaBlockPosSet, node: u32, out: &mut String) { diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java b/steel-core/src/java_block_pos_set/tests/HashSetOracle.java similarity index 100% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/HashSetOracle.java rename to steel-core/src/java_block_pos_set/tests/HashSetOracle.java diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt b/steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt similarity index 100% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25-sequential-identity.txt rename to steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt b/steel-core/src/java_block_pos_set/tests/openjdk25.txt similarity index 100% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tests/openjdk25.txt rename to steel-core/src/java_block_pos_set/tests/openjdk25.txt diff --git a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs b/steel-core/src/java_block_pos_set/tree.rs similarity index 90% rename from steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs rename to steel-core/src/java_block_pos_set/tree.rs index 4a99cd0ea9b..fb83cdfcb9c 100644 --- a/steel-core/src/world/explosion/server/block_rays/java_block_pos_set/tree.rs +++ b/steel-core/src/java_block_pos_set/tree.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use steel_utils::BlockPos; -use super::{EMPTY, JavaBlockPosSet, UNTREEIFY_THRESHOLD, spread}; +use super::{EMPTY, EqualHashOrder, JavaBlockPosSet, UNTREEIFY_THRESHOLD, spread}; pub(super) struct Links { pub(super) parent: u32, @@ -22,6 +22,13 @@ impl Links { } impl JavaBlockPosSet { + pub(super) fn tree_root(&self, mut node: u32) -> u32 { + while self.links(node).parent != EMPTY { + node = self.links(node).parent; + } + node + } + pub(super) fn links(&self, node: u32) -> &Links { &self.trees[&node] } @@ -59,7 +66,17 @@ impl JavaBlockPosSet { fn goes_left(&self, node: u32, parent: u32) -> bool { let hash = spread(self.entries[node as usize].pos); let parent_hash = spread(self.entries[parent as usize].pos); - hash < parent_hash || (hash == parent_hash && node < parent) + if hash != parent_hash { + return hash < parent_hash; + } + match self.equal_hash_order { + EqualHashOrder::Insertion => node < parent, + EqualHashOrder::Coordinates => { + let pos = self.entries[node as usize].pos; + let parent_pos = self.entries[parent as usize].pos; + (pos.y(), pos.z(), pos.x()) < (parent_pos.y(), parent_pos.z(), parent_pos.x()) + } + } } fn attach(&mut self, root: u32, node: u32) -> u32 { @@ -139,7 +156,7 @@ impl JavaBlockPosSet { pub(super) fn insert_tree(&mut self, bucket: usize, node: u32) { self.reset_links(node); - let root = self.buckets[bucket].head; + let root = self.tree_root(self.buckets[bucket].head); let parent = self.attach(root, node); let next = self.entries[parent as usize].next; self.entries[parent as usize].next = node; @@ -173,7 +190,7 @@ impl JavaBlockPosSet { } // Both rotations change only the tree; iteration still follows next/prev. - fn rotate(&mut self, mut root: u32, node: u32, left: bool) -> u32 { + pub(super) fn rotate(&mut self, mut root: u32, node: u32, left: bool) -> u32 { let pivot = if left { self.links(node).right } else { diff --git a/steel-core/src/java_block_pos_set/worldgen_tests.rs b/steel-core/src/java_block_pos_set/worldgen_tests.rs new file mode 100644 index 00000000000..5821738b72a --- /dev/null +++ b/steel-core/src/java_block_pos_set/worldgen_tests.rs @@ -0,0 +1,145 @@ +use super::*; + +#[test] +fn duplicate_positions_are_not_inserted_twice() { + let mut set = JavaBlockPosSet::default(); + assert!(set.insert(BlockPos::new(1, 2, 3))); + assert!(!set.insert(BlockPos::new(1, 2, 3))); + assert_eq!(set.java_ordered_positions(), [BlockPos::new(1, 2, 3)]); +} + +#[test] +fn iteration_matches_java_hash_set_after_resize() { + let mut set = JavaBlockPosSet::default(); + for x in 0..20 { + assert!(set.insert(BlockPos::new(x, 64, x % 5))); + } + + assert_eq!( + set.java_ordered_positions(), + [ + (0, 0), + (1, 1), + (2, 2), + (5, 0), + (3, 3), + (6, 1), + (4, 4), + (7, 2), + (10, 0), + (8, 3), + (11, 1), + (9, 4), + (12, 2), + (15, 0), + (13, 3), + (16, 1), + (14, 4), + (17, 2), + (18, 3), + (19, 4), + ] + .map(|(x, z)| BlockPos::new(x, 64, z)) + ); +} + +#[test] +fn collision_chain_triggers_java_hash_map_resize() { + let mut set = JavaBlockPosSet::default(); + for x in (0..=128).step_by(16) { + assert!(set.insert(BlockPos::new(x, 0, 0))); + } + + assert_eq!( + set.java_ordered_positions(), + [0, 32, 64, 96, 128, 16, 48, 80, 112].map(|x| BlockPos::new(x, 0, 0)) + ); +} + +#[test] +fn reinserted_position_returns_to_its_bucket_tail() { + let mut set = JavaBlockPosSet::default(); + let first = BlockPos::new(0, 0, 0); + let second = BlockPos::new(16, 0, 0); + + assert!(set.insert(first)); + assert!(set.insert(second)); + assert_eq!(set.pop_java_ordered_position(), Some(first)); + assert!(set.insert(first)); + + assert_eq!(set.java_ordered_positions(), [second, first]); +} + +#[test] +fn pop_uses_java_bucket_order() { + let mut set = JavaBlockPosSet::default(); + let first = BlockPos::new(15, 0, 0); + let second = BlockPos::new(1, 0, 0); + assert!(set.insert(first)); + assert!(set.insert(second)); + + assert_eq!(set.pop_java_ordered_position(), Some(second)); + assert_eq!(set.pop_java_ordered_position(), Some(first)); + assert_eq!(set.pop_java_ordered_position(), None); +} + +#[test] +fn tree_bin_insertion_and_iterator_removal_match_java_hash_set() { + let mut set = JavaBlockPosSet::default(); + for i in [ + 17, 3, 29, 1, 11, 23, 7, 31, 5, 19, 13, 2, 37, 0, 41, 9, 27, 15, 35, 21, 6, 33, 25, 39, 4, + 43, 8, 45, 10, 47, + ] { + assert!(set.insert(BlockPos::new(i * 64, 0, 0))); + } + + assert_eq!( + set.java_order().map(BlockPos::x).collect::>(), + [ + 1088, 192, 1856, 64, 0, 128, 704, 576, 640, 512, 1472, 1728, 1600, 448, 1984, 2240, + 2112, 2368, 2624, 2752, 2880, 3008, 2496, 320, 256, 384, 1216, 1344, 832, 960, + ] + ); + + for expected in [1088, 192, 1856, 64, 0] { + assert_eq!( + set.pop_java_ordered_position(), + Some(BlockPos::new(expected, 0, 0)) + ); + } + assert!(set.insert(BlockPos::new(1088, 0, 0))); + for i in [49, 51, 53, 55, 57] { + assert!(set.insert(BlockPos::new(i * 64, 0, 0))); + } + + assert_eq!( + set.java_order().map(BlockPos::x).collect::>(), + [ + 1984, 1216, 128, 704, 576, 640, 512, 1472, 1728, 1600, 448, 2240, 2112, 2368, 2624, + 2752, 2880, 3008, 3136, 3264, 3392, 3520, 3648, 2496, 320, 256, 384, 1344, 832, 960, + 1088, + ] + ); +} + +#[test] +fn tree_bin_resize_matches_java_hash_set() { + let mut set = JavaBlockPosSet::default(); + for i in [ + 34, 23, 3, 8, 44, 25, 11, 52, 36, 10, 21, 18, 20, 9, 54, 41, 40, 42, 12, 16, 14, 47, 35, 6, + 46, 31, 53, 5, 13, 7, 51, 29, 33, 0, 24, 32, 22, 1, 27, 37, 30, 50, 48, 43, 4, 15, 26, 2, + 45, 28, 39, 38, 17, 49, 19, 55, + ] { + assert!(set.insert(BlockPos::new(i * 64, 0, 0))); + } + + assert_eq!( + set.java_order().map(BlockPos::x).collect::>(), + [ + 1408, 2176, 128, 256, 0, 384, 512, 2816, 1664, 1536, 1920, 1792, 2048, 3328, 3200, + 3072, 2944, 3456, 2304, 2688, 2560, 2432, 640, 1152, 768, 896, 1024, 1280, 1344, 448, + 1472, 192, 64, 320, 1600, 1728, 1984, 2112, 1856, 704, 3008, 3264, 3136, 2880, 3392, + 3520, 2368, 2240, 2624, 2496, 2752, 576, 960, 1088, 1216, 832, + ] + ); +} diff --git a/steel-core/src/lib.rs b/steel-core/src/lib.rs index 2b8d44973cb..8a68e7d8e44 100644 --- a/steel-core/src/lib.rs +++ b/steel-core/src/lib.rs @@ -20,6 +20,7 @@ pub(crate) mod enchantment_helper; pub mod entity; pub mod fluid; pub mod inventory; +pub(crate) mod java_block_pos_set; pub mod level_data; pub mod permission; pub mod physics; diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs index 516833ad701..b5b2772c9be 100644 --- a/steel-core/src/world/explosion/server/block_rays.rs +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -14,9 +14,8 @@ use crate::world::{BlockRegionBounds, BlockRegionRead, World}; use super::ServerExplosion; mod cache; -mod java_block_pos_set; -use java_block_pos_set::JavaBlockPosSet; +use crate::java_block_pos_set::{EqualHashOrder, JavaBlockPosSet}; const RAY_GRID_SIZE: i32 = 16; const RAY_GRID_LAST_INDEX: i32 = RAY_GRID_SIZE - 1; @@ -171,7 +170,7 @@ impl ServerExplosion<'_> { center: self.center, bounds: ExplosionWorldBounds::from_world(self.world), }; - let mut affected = JavaBlockPosSet::default(); + let mut affected = JavaBlockPosSet::new(EqualHashOrder::Insertion); for (&step, &initial_power) in RAY_STEPS.iter().zip(powers) { visit_immutable_ray_positions( ExplosionRay { @@ -211,7 +210,7 @@ impl ServerExplosion<'_> { &self, mut next_float: impl FnMut() -> f32, ) -> Vec { - let mut affected = JavaBlockPosSet::default(); + let mut affected = JavaBlockPosSet::new(EqualHashOrder::Insertion); let bounds = ExplosionWorldBounds::from_world(self.world); for &step in RAY_STEPS.iter() { diff --git a/steel-core/src/world/explosion/server/block_rays/cache.rs b/steel-core/src/world/explosion/server/block_rays/cache.rs index 42e94e5bef0..f95055dcf96 100644 --- a/steel-core/src/world/explosion/server/block_rays/cache.rs +++ b/steel-core/src/world/explosion/server/block_rays/cache.rs @@ -8,8 +8,8 @@ use crate::world::explosion::{ExplosionBlockReader, ImmutableExplosionBlockCalcu use super::super::ServerExplosion; use super::{ - ExplosionRay, ExplosionRayContext, ExplosionWorldBounds, JavaBlockPosSet, RAY_POWER_DECAY, - RAY_STEPS, ray_power_loss_from_resistance, + EqualHashOrder, ExplosionRay, ExplosionRayContext, ExplosionWorldBounds, JavaBlockPosSet, + RAY_POWER_DECAY, RAY_STEPS, ray_power_loss_from_resistance, }; const BLOCK_CACHE_BITS: u32 = 9; @@ -330,7 +330,7 @@ impl ServerExplosion<'_> { cache_policy: ImmutableRayCachePolicy, mut cache: C, ) -> Option> { - let mut affected = JavaBlockPosSet::default(); + let mut affected = JavaBlockPosSet::new(EqualHashOrder::Insertion); for (&step, &initial_power) in RAY_STEPS.iter().zip(powers) { if !visit_immutable_ray_positions_cached::( ExplosionRay { diff --git a/steel-core/src/world/explosion/server/block_rays/tests.rs b/steel-core/src/world/explosion/server/block_rays/tests.rs index 64ddec5b094..14dcc32e9a5 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests.rs @@ -127,7 +127,7 @@ fn calculate_cached_immutable_rays Vec { - let mut affected = JavaBlockPosSet::default(); + let mut affected = JavaBlockPosSet::new(EqualHashOrder::Insertion); let mut cache = ExplosionBlockCache::default(); let cache_policy = ImmutableRayCachePolicy { resistance: calculator.can_cache_explosion_resistance(), @@ -423,7 +423,7 @@ fn cached_ray_decisions_keep_uncached_order_at_already_affected_positions() { initial_power: 1.0, }); let uncached = RecordingCalculator(SyncMutex::new(Vec::new())); - let mut expected_affected = JavaBlockPosSet::default(); + let mut expected_affected = JavaBlockPosSet::new(EqualHashOrder::Insertion); for ray in rays { visit_immutable_ray_positions(ray, context, world.as_ref(), &uncached, |pos| { expected_affected.insert(pos); @@ -616,7 +616,7 @@ fn bounded_immutable_reader_covers_maximum_power_rays() { #[test] fn java_block_pos_set_resizes_on_a_ninth_collision_before_capacity_sixty_four() { - let mut positions = JavaBlockPosSet::default(); + let mut positions = JavaBlockPosSet::new(EqualHashOrder::Insertion); for x in 1..=13 { assert!(positions.insert(BlockPos::new(x, 0, 0))); } diff --git a/steel-core/src/worldgen/feature/features/tree/decorators.rs b/steel-core/src/worldgen/feature/features/tree/decorators.rs index f635170002d..5cf8e9f139d 100644 --- a/steel-core/src/worldgen/feature/features/tree/decorators.rs +++ b/steel-core/src/worldgen/feature/features/tree/decorators.rs @@ -8,8 +8,8 @@ use steel_utils::Downcast as _; use super::super::super::prelude::*; use super::super::super::runner::FeatureDecorationRunner; -use super::super::super::vanilla_collections::JavaBlockPosSet; use super::{NestedFeaturePlacer, TreePlacement}; +use crate::java_block_pos_set::JavaBlockPosSet; use crate::block_entity::entities::BeehiveBlockEntity; diff --git a/steel-core/src/worldgen/feature/features/tree/leaves.rs b/steel-core/src/worldgen/feature/features/tree/leaves.rs index 28507f85dcc..37ee78bc642 100644 --- a/steel-core/src/worldgen/feature/features/tree/leaves.rs +++ b/steel-core/src/worldgen/feature/features/tree/leaves.rs @@ -2,8 +2,8 @@ use steel_registry::vanilla_block_tags::BlockTag; use super::super::super::prelude::*; use super::super::super::runner::FeatureDecorationRunner; -use super::super::super::vanilla_collections::JavaBlockPosSet; use super::{TreeBounds, TreePlacement}; +use crate::java_block_pos_set::JavaBlockPosSet; const LEAF_DISTANCE_LIMIT: usize = 7; diff --git a/steel-core/src/worldgen/feature/features/tree/mod.rs b/steel-core/src/worldgen/feature/features/tree/mod.rs index fedbc8423bf..45d636472d2 100644 --- a/steel-core/src/worldgen/feature/features/tree/mod.rs +++ b/steel-core/src/worldgen/feature/features/tree/mod.rs @@ -2,7 +2,7 @@ use steel_registry::vanilla_block_tags::BlockTag; use super::super::prelude::*; use super::super::runner::FeatureDecorationRunner; -use super::super::vanilla_collections::JavaBlockPosSet; +use crate::java_block_pos_set::JavaBlockPosSet; use rustc_hash::FxHashMap; use std::cell::{Cell, RefCell}; diff --git a/steel-core/src/worldgen/feature/features/vegetation_patch.rs b/steel-core/src/worldgen/feature/features/vegetation_patch.rs index dd4dbf15aae..9b7b25ad2ff 100644 --- a/steel-core/src/worldgen/feature/features/vegetation_patch.rs +++ b/steel-core/src/worldgen/feature/features/vegetation_patch.rs @@ -1,6 +1,6 @@ use super::super::prelude::*; use super::super::runner::FeatureDecorationRunner; -use super::super::vanilla_collections::JavaBlockPosSet; +use crate::java_block_pos_set::JavaBlockPosSet; impl FeatureDecorationRunner { pub(in crate::worldgen::feature) fn place_vegetation_patch_feature( diff --git a/steel-core/src/worldgen/feature/mod.rs b/steel-core/src/worldgen/feature/mod.rs index df5bf0f4de7..28f6cf3d25e 100644 --- a/steel-core/src/worldgen/feature/mod.rs +++ b/steel-core/src/worldgen/feature/mod.rs @@ -16,7 +16,6 @@ mod prelude; mod runner; mod sorter; mod state; -mod vanilla_collections; mod weather; pub(crate) use features::no_nested_features; diff --git a/steel-core/src/worldgen/feature/vanilla_collections.rs b/steel-core/src/worldgen/feature/vanilla_collections.rs deleted file mode 100644 index 696e7aa5721..00000000000 --- a/steel-core/src/worldgen/feature/vanilla_collections.rs +++ /dev/null @@ -1,778 +0,0 @@ -use std::{cmp::Ordering, mem}; - -use smallvec::SmallVec; - -use super::prelude::*; - -const INLINE_CAPACITY: usize = 32; -const INITIAL_TABLE_CAPACITY: usize = 16; -const LOAD_FACTOR_NUMERATOR: usize = 3; -const LOAD_FACTOR_DENOMINATOR: usize = 4; -const TREEIFY_THRESHOLD: usize = 8; -const UNTREEIFY_THRESHOLD: usize = 6; -const MIN_TREEIFY_CAPACITY: usize = 64; - -/// Block-position set for feature code that vanilla models as `HashSet`. -/// -/// This mirrors the target JDK 25 `HashMap` bucket, resize, and tree-bin behavior. Its iteration -/// order affects feature RNG and leaf-distance propagation. -#[derive(Default)] -pub(super) struct JavaBlockPosSet { - entries: SmallVec<[BlockPos; INLINE_CAPACITY]>, - table_capacity: usize, - tree_bins: Vec, -} - -impl JavaBlockPosSet { - pub(super) fn insert(&mut self, pos: BlockPos) -> bool { - if self.entries.contains(&pos) { - return false; - } - - if self.table_capacity == 0 { - self.table_capacity = INITIAL_TABLE_CAPACITY; - } - - let bucket = Self::bucket(pos, self.table_capacity); - if let Some(tree_bin_index) = self.tree_bins.iter().position(|bin| bin.bucket == bucket) { - let (parent, root) = self.tree_bins[tree_bin_index].insert(pos); - let Some(parent_index) = self.entries.iter().position(|&entry| entry == parent) else { - unreachable!("tree-bin parent must be present in the iteration order"); - }; - self.entries.insert(parent_index + 1, pos); - self.move_position_to_bucket_front(root, bucket); - } else { - let insert_at = self - .entries - .iter() - .position(|&entry| Self::bucket(entry, self.table_capacity) > bucket) - .unwrap_or(self.entries.len()); - self.entries.insert(insert_at, pos); - - let bucket_len = self - .entries - .iter() - .filter(|&&entry| Self::bucket(entry, self.table_capacity) == bucket) - .count(); - if bucket_len > TREEIFY_THRESHOLD { - if self.table_capacity < MIN_TREEIFY_CAPACITY { - self.resize(); - } else { - self.treeify_bin(bucket); - } - } - } - if self.entries.len() - > self.table_capacity * LOAD_FACTOR_NUMERATOR / LOAD_FACTOR_DENOMINATOR - { - self.resize(); - } - true - } - - pub(super) fn contains(&self, pos: BlockPos) -> bool { - self.entries.contains(&pos) - } - - pub(super) fn is_empty(&self) -> bool { - self.entries.is_empty() - } - - pub(super) fn java_order(&self) -> impl Iterator { - self.entries.iter() - } - - pub(super) fn java_ordered_positions(&self) -> Vec { - self.entries.to_vec() - } - - pub(super) fn pop_java_ordered_position(&mut self) -> Option { - if self.entries.is_empty() { - return None; - } - - let pos = self.entries.remove(0); - let bucket = Self::bucket(pos, self.table_capacity); - if let Some(tree_bin_index) = self.tree_bins.iter().position(|bin| bin.bucket == bucket) { - if self - .entries - .iter() - .any(|&entry| Self::bucket(entry, self.table_capacity) == bucket) - { - self.tree_bins[tree_bin_index].remove(pos); - } else { - self.tree_bins.remove(tree_bin_index); - } - } - Some(pos) - } - - const fn bucket(pos: BlockPos, table_capacity: usize) -> usize { - Self::spread_hash(pos) as usize & (table_capacity - 1) - } - - const fn spread_hash(pos: BlockPos) -> u32 { - let hash = pos - .z() - .wrapping_mul(31) - .wrapping_add(pos.y()) - .wrapping_mul(31) - .wrapping_add(pos.x()) as u32; - hash ^ (hash >> 16) - } - - fn resize(&mut self) { - let old_capacity = self.table_capacity; - self.table_capacity *= 2; - let table_capacity = self.table_capacity; - self.entries - .sort_by_key(|&entry| Self::bucket(entry, table_capacity)); - - let old_tree_bins = mem::take(&mut self.tree_bins); - for mut tree_bin in old_tree_bins { - let low = self - .entries - .iter() - .copied() - .filter(|&entry| Self::bucket(entry, table_capacity) == tree_bin.bucket) - .collect::>(); - let high_bucket = tree_bin.bucket + old_capacity; - let high = self - .entries - .iter() - .copied() - .filter(|&entry| Self::bucket(entry, table_capacity) == high_bucket) - .collect::>(); - - if low.is_empty() || high.is_empty() { - tree_bin.bucket = if low.is_empty() { - high_bucket - } else { - tree_bin.bucket - }; - self.tree_bins.push(tree_bin); - continue; - } - - self.retreeify_split_bin(tree_bin.bucket, &low); - self.retreeify_split_bin(high_bucket, &high); - } - } - - fn treeify_bin(&mut self, bucket: usize) { - let positions = self - .entries - .iter() - .copied() - .filter(|&entry| Self::bucket(entry, self.table_capacity) == bucket) - .collect::>(); - let tree_bin = TreeBin::new(bucket, &positions); - let root = tree_bin.root_position(); - self.tree_bins.push(tree_bin); - self.move_position_to_bucket_front(root, bucket); - } - - fn retreeify_split_bin(&mut self, bucket: usize, positions: &[BlockPos]) { - if positions.len() <= UNTREEIFY_THRESHOLD { - return; - } - let tree_bin = TreeBin::new(bucket, positions); - let root = tree_bin.root_position(); - self.tree_bins.push(tree_bin); - self.move_position_to_bucket_front(root, bucket); - } - - fn move_position_to_bucket_front(&mut self, pos: BlockPos, bucket: usize) { - let Some(root_index) = self.entries.iter().position(|&entry| entry == pos) else { - return; - }; - let Some(bucket_start) = self - .entries - .iter() - .position(|&entry| Self::bucket(entry, self.table_capacity) == bucket) - else { - return; - }; - if root_index != bucket_start { - let pos = self.entries.remove(root_index); - self.entries.insert(bucket_start, pos); - } - } -} - -struct TreeBin { - bucket: usize, - nodes: Vec, - root: usize, -} - -impl TreeBin { - fn new(bucket: usize, positions: &[BlockPos]) -> Self { - let mut tree = Self { - bucket, - nodes: Vec::new(), - root: 0, - }; - let Some((&first, remaining)) = positions.split_first() else { - return tree; - }; - tree.nodes.push(TreeNode::new(first)); - for &pos in remaining { - tree.insert(pos); - } - tree - } - - fn root_position(&self) -> BlockPos { - self.nodes[self.root].pos - } - - fn insert(&mut self, pos: BlockPos) -> (BlockPos, BlockPos) { - let mut parent = self.root; - loop { - let ordering = tree_compare(pos, self.nodes[parent].pos); - let next = if ordering.is_lt() { - self.nodes[parent].left - } else { - self.nodes[parent].right - }; - let Some(next) = next else { - let index = self.nodes.len(); - self.nodes.push(TreeNode { - pos, - parent: Some(parent), - left: None, - right: None, - red: true, - }); - if ordering.is_lt() { - self.nodes[parent].left = Some(index); - } else { - self.nodes[parent].right = Some(index); - } - self.root = balance_insertion(&mut self.nodes, self.root, index); - return (self.nodes[parent].pos, self.root_position()); - }; - parent = next; - } - } - - fn remove(&mut self, pos: BlockPos) { - let Some(node) = self.find(pos) else { - return; - }; - let mut root = self.root; - let left = self.nodes[node].left; - let right = self.nodes[node].right; - - let replacement = if let (Some(left), Some(right)) = (left, right) { - let mut successor = right; - while let Some(next) = self.nodes[successor].left { - successor = next; - } - let successor_was_red = self.nodes[successor].red; - self.nodes[successor].red = self.nodes[node].red; - self.nodes[node].red = successor_was_red; - let successor_right = self.nodes[successor].right; - let node_parent = self.nodes[node].parent; - - if successor == right { - self.nodes[node].parent = Some(successor); - self.nodes[successor].right = Some(node); - } else { - let successor_parent = self.nodes[successor].parent; - self.nodes[node].parent = successor_parent; - if let Some(successor_parent) = successor_parent { - if self.nodes[successor_parent].left == Some(successor) { - self.nodes[successor_parent].left = Some(node); - } else { - self.nodes[successor_parent].right = Some(node); - } - } - self.nodes[successor].right = Some(right); - self.nodes[right].parent = Some(successor); - } - - self.nodes[node].left = None; - self.nodes[node].right = successor_right; - if let Some(successor_right) = successor_right { - self.nodes[successor_right].parent = Some(node); - } - self.nodes[successor].left = Some(left); - self.nodes[left].parent = Some(successor); - self.nodes[successor].parent = node_parent; - if let Some(node_parent) = node_parent { - if self.nodes[node_parent].left == Some(node) { - self.nodes[node_parent].left = Some(successor); - } else { - self.nodes[node_parent].right = Some(successor); - } - } else { - root = successor; - } - - successor_right.unwrap_or(node) - } else { - left.or(right).unwrap_or(node) - }; - - if replacement != node { - let node_parent = self.nodes[node].parent; - self.nodes[replacement].parent = node_parent; - if let Some(node_parent) = node_parent { - if self.nodes[node_parent].left == Some(node) { - self.nodes[node_parent].left = Some(replacement); - } else { - self.nodes[node_parent].right = Some(replacement); - } - } else { - root = replacement; - self.nodes[replacement].red = false; - } - self.nodes[node].parent = None; - self.nodes[node].left = None; - self.nodes[node].right = None; - } - - root = if self.nodes[node].red { - root - } else { - balance_deletion(&mut self.nodes, root, replacement) - }; - - if replacement == node - && let Some(parent) = self.nodes[node].parent - { - self.nodes[node].parent = None; - if self.nodes[parent].left == Some(node) { - self.nodes[parent].left = None; - } else if self.nodes[parent].right == Some(node) { - self.nodes[parent].right = None; - } - } - self.root = root; - } - - fn find(&self, pos: BlockPos) -> Option { - let mut node = Some(self.root); - while let Some(index) = node { - if self.nodes[index].pos == pos { - return Some(index); - } - node = if tree_compare(pos, self.nodes[index].pos).is_lt() { - self.nodes[index].left - } else { - self.nodes[index].right - }; - } - None - } -} - -#[derive(Clone, Copy)] -struct TreeNode { - pos: BlockPos, - parent: Option, - left: Option, - right: Option, - red: bool, -} - -impl TreeNode { - const fn new(pos: BlockPos) -> Self { - Self { - pos, - parent: None, - left: None, - right: None, - red: false, - } - } -} - -fn tree_compare(left: BlockPos, right: BlockPos) -> Ordering { - let left_hash = JavaBlockPosSet::spread_hash(left) as i32; - let right_hash = JavaBlockPosSet::spread_hash(right) as i32; - left_hash - .cmp(&right_hash) - .then_with(|| left.y().cmp(&right.y())) - .then_with(|| left.z().cmp(&right.z())) - .then_with(|| left.x().cmp(&right.x())) -} - -fn balance_insertion(nodes: &mut [TreeNode], mut root: usize, mut node: usize) -> usize { - loop { - let Some(parent) = nodes[node].parent else { - nodes[node].red = false; - return node; - }; - if !nodes[parent].red { - return root; - } - let Some(grandparent) = nodes[parent].parent else { - return root; - }; - - if nodes[grandparent].left == Some(parent) { - let uncle = nodes[grandparent].right; - if let Some(uncle) = uncle.filter(|&uncle| nodes[uncle].red) { - nodes[uncle].red = false; - nodes[parent].red = false; - nodes[grandparent].red = true; - node = grandparent; - continue; - } - if nodes[parent].right == Some(node) { - root = rotate_left(nodes, root, parent); - node = parent; - } - let Some(parent) = nodes[node].parent else { - return root; - }; - let Some(grandparent) = nodes[parent].parent else { - return root; - }; - nodes[parent].red = false; - nodes[grandparent].red = true; - return rotate_right(nodes, root, grandparent); - } - - let uncle = nodes[grandparent].left; - if let Some(uncle) = uncle.filter(|&uncle| nodes[uncle].red) { - nodes[uncle].red = false; - nodes[parent].red = false; - nodes[grandparent].red = true; - node = grandparent; - continue; - } - if nodes[parent].left == Some(node) { - root = rotate_right(nodes, root, parent); - node = parent; - } - let Some(parent) = nodes[node].parent else { - return root; - }; - let Some(grandparent) = nodes[parent].parent else { - return root; - }; - nodes[parent].red = false; - nodes[grandparent].red = true; - return rotate_left(nodes, root, grandparent); - } -} - -#[expect( - clippy::too_many_lines, - reason = "keeping both symmetric branches together makes the OpenJDK red-black deletion port auditable" -)] -fn balance_deletion(nodes: &mut [TreeNode], mut root: usize, mut node: usize) -> usize { - loop { - if node == root { - return root; - } - let Some(mut parent) = nodes[node].parent else { - nodes[node].red = false; - return node; - }; - if nodes[node].red { - nodes[node].red = false; - return root; - } - - if nodes[parent].left == Some(node) { - let mut sibling = nodes[parent].right; - if is_red(nodes, sibling) { - let sibling_index = sibling.unwrap_or(parent); - nodes[sibling_index].red = false; - nodes[parent].red = true; - root = rotate_left(nodes, root, parent); - let Some(new_parent) = nodes[node].parent else { - return root; - }; - parent = new_parent; - sibling = nodes[parent].right; - } - let Some(sibling_index) = sibling else { - node = parent; - continue; - }; - let sibling_left = nodes[sibling_index].left; - let mut sibling_right = nodes[sibling_index].right; - if !is_red(nodes, sibling_right) && !is_red(nodes, sibling_left) { - nodes[sibling_index].red = true; - node = parent; - continue; - } - if !is_red(nodes, sibling_right) { - if let Some(sibling_left) = sibling_left { - nodes[sibling_left].red = false; - } - nodes[sibling_index].red = true; - root = rotate_right(nodes, root, sibling_index); - let Some(new_parent) = nodes[node].parent else { - return root; - }; - parent = new_parent; - sibling = nodes[parent].right; - } - if let Some(sibling) = sibling { - nodes[sibling].red = nodes[parent].red; - sibling_right = nodes[sibling].right; - if let Some(sibling_right) = sibling_right { - nodes[sibling_right].red = false; - } - } - nodes[parent].red = false; - root = rotate_left(nodes, root, parent); - node = root; - continue; - } - - let mut sibling = nodes[parent].left; - if is_red(nodes, sibling) { - let sibling_index = sibling.unwrap_or(parent); - nodes[sibling_index].red = false; - nodes[parent].red = true; - root = rotate_right(nodes, root, parent); - let Some(new_parent) = nodes[node].parent else { - return root; - }; - parent = new_parent; - sibling = nodes[parent].left; - } - let Some(sibling_index) = sibling else { - node = parent; - continue; - }; - let mut sibling_left = nodes[sibling_index].left; - let sibling_right = nodes[sibling_index].right; - if !is_red(nodes, sibling_left) && !is_red(nodes, sibling_right) { - nodes[sibling_index].red = true; - node = parent; - continue; - } - if !is_red(nodes, sibling_left) { - if let Some(sibling_right) = sibling_right { - nodes[sibling_right].red = false; - } - nodes[sibling_index].red = true; - root = rotate_left(nodes, root, sibling_index); - let Some(new_parent) = nodes[node].parent else { - return root; - }; - parent = new_parent; - sibling = nodes[parent].left; - } - if let Some(sibling) = sibling { - nodes[sibling].red = nodes[parent].red; - sibling_left = nodes[sibling].left; - if let Some(sibling_left) = sibling_left { - nodes[sibling_left].red = false; - } - } - nodes[parent].red = false; - root = rotate_right(nodes, root, parent); - node = root; - } -} - -fn is_red(nodes: &[TreeNode], node: Option) -> bool { - node.is_some_and(|node| nodes[node].red) -} - -fn rotate_left(nodes: &mut [TreeNode], mut root: usize, node: usize) -> usize { - let Some(right) = nodes[node].right else { - return root; - }; - nodes[node].right = nodes[right].left; - if let Some(right_left) = nodes[right].left { - nodes[right_left].parent = Some(node); - } - nodes[right].parent = nodes[node].parent; - if let Some(parent) = nodes[node].parent { - if nodes[parent].left == Some(node) { - nodes[parent].left = Some(right); - } else { - nodes[parent].right = Some(right); - } - } else { - root = right; - nodes[root].red = false; - } - nodes[right].left = Some(node); - nodes[node].parent = Some(right); - root -} - -fn rotate_right(nodes: &mut [TreeNode], mut root: usize, node: usize) -> usize { - let Some(left) = nodes[node].left else { - return root; - }; - nodes[node].left = nodes[left].right; - if let Some(left_right) = nodes[left].right { - nodes[left_right].parent = Some(node); - } - nodes[left].parent = nodes[node].parent; - if let Some(parent) = nodes[node].parent { - if nodes[parent].right == Some(node) { - nodes[parent].right = Some(left); - } else { - nodes[parent].left = Some(left); - } - } else { - root = left; - nodes[root].red = false; - } - nodes[left].right = Some(node); - nodes[node].parent = Some(left); - root -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn duplicate_positions_are_not_inserted_twice() { - let mut set = JavaBlockPosSet::default(); - assert!(set.insert(BlockPos::new(1, 2, 3))); - assert!(!set.insert(BlockPos::new(1, 2, 3))); - assert_eq!(set.java_ordered_positions(), [BlockPos::new(1, 2, 3)]); - } - - #[test] - fn iteration_matches_java_hash_set_after_resize() { - let mut set = JavaBlockPosSet::default(); - for x in 0..20 { - assert!(set.insert(BlockPos::new(x, 64, x % 5))); - } - - assert_eq!( - set.java_ordered_positions(), - [ - (0, 0), - (1, 1), - (2, 2), - (5, 0), - (3, 3), - (6, 1), - (4, 4), - (7, 2), - (10, 0), - (8, 3), - (11, 1), - (9, 4), - (12, 2), - (15, 0), - (13, 3), - (16, 1), - (14, 4), - (17, 2), - (18, 3), - (19, 4), - ] - .map(|(x, z)| BlockPos::new(x, 64, z)) - ); - } - - #[test] - fn collision_chain_triggers_java_hash_map_resize() { - let mut set = JavaBlockPosSet::default(); - for x in (0..=128).step_by(16) { - assert!(set.insert(BlockPos::new(x, 0, 0))); - } - - assert_eq!( - set.java_ordered_positions(), - [0, 32, 64, 96, 128, 16, 48, 80, 112].map(|x| BlockPos::new(x, 0, 0)) - ); - } - - #[test] - fn reinserted_position_returns_to_its_bucket_tail() { - let mut set = JavaBlockPosSet::default(); - let first = BlockPos::new(0, 0, 0); - let second = BlockPos::new(16, 0, 0); - - assert!(set.insert(first)); - assert!(set.insert(second)); - assert_eq!(set.pop_java_ordered_position(), Some(first)); - assert!(set.insert(first)); - - assert_eq!(set.java_ordered_positions(), [second, first]); - } - - #[test] - fn pop_uses_java_bucket_order() { - let mut set = JavaBlockPosSet::default(); - let first = BlockPos::new(15, 0, 0); - let second = BlockPos::new(1, 0, 0); - assert!(set.insert(first)); - assert!(set.insert(second)); - - assert_eq!(set.pop_java_ordered_position(), Some(second)); - assert_eq!(set.pop_java_ordered_position(), Some(first)); - assert_eq!(set.pop_java_ordered_position(), None); - } - - #[test] - fn tree_bin_insertion_and_iterator_removal_match_java_hash_set() { - let mut set = JavaBlockPosSet::default(); - for i in [ - 17, 3, 29, 1, 11, 23, 7, 31, 5, 19, 13, 2, 37, 0, 41, 9, 27, 15, 35, 21, 6, 33, 25, 39, - 4, 43, 8, 45, 10, 47, - ] { - assert!(set.insert(BlockPos::new(i * 64, 0, 0))); - } - - assert_eq!( - set.java_order().map(BlockPos::x).collect::>(), - [ - 1088, 192, 1856, 64, 0, 128, 704, 576, 640, 512, 1472, 1728, 1600, 448, 1984, 2240, - 2112, 2368, 2624, 2752, 2880, 3008, 2496, 320, 256, 384, 1216, 1344, 832, 960, - ] - ); - - for expected in [1088, 192, 1856, 64, 0] { - assert_eq!( - set.pop_java_ordered_position(), - Some(BlockPos::new(expected, 0, 0)) - ); - } - assert!(set.insert(BlockPos::new(1088, 0, 0))); - for i in [49, 51, 53, 55, 57] { - assert!(set.insert(BlockPos::new(i * 64, 0, 0))); - } - - assert_eq!( - set.java_order().map(BlockPos::x).collect::>(), - [ - 1984, 1216, 128, 704, 576, 640, 512, 1472, 1728, 1600, 448, 2240, 2112, 2368, 2624, - 2752, 2880, 3008, 3136, 3264, 3392, 3520, 3648, 2496, 320, 256, 384, 1344, 832, - 960, 1088, - ] - ); - } - - #[test] - fn tree_bin_resize_matches_java_hash_set() { - let mut set = JavaBlockPosSet::default(); - for i in [ - 34, 23, 3, 8, 44, 25, 11, 52, 36, 10, 21, 18, 20, 9, 54, 41, 40, 42, 12, 16, 14, 47, - 35, 6, 46, 31, 53, 5, 13, 7, 51, 29, 33, 0, 24, 32, 22, 1, 27, 37, 30, 50, 48, 43, 4, - 15, 26, 2, 45, 28, 39, 38, 17, 49, 19, 55, - ] { - assert!(set.insert(BlockPos::new(i * 64, 0, 0))); - } - - assert_eq!( - set.java_order().map(BlockPos::x).collect::>(), - [ - 1408, 2176, 128, 256, 0, 384, 512, 2816, 1664, 1536, 1920, 1792, 2048, 3328, 3200, - 3072, 2944, 3456, 2304, 2688, 2560, 2432, 640, 1152, 768, 896, 1024, 1280, 1344, - 448, 1472, 192, 64, 320, 1600, 1728, 1984, 2112, 1856, 704, 3008, 3264, 3136, 2880, - 3392, 3520, 2368, 2240, 2624, 2496, 2752, 576, 960, 1088, 1216, 832, - ] - ); - } -} From 3ed863ba70ea227e59038e4b065d0b87d0f8380d Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:41:16 +0100 Subject: [PATCH 15/26] replace with json --- steel-core/src/java_block_pos_set/tests.rs | 77 +- .../tests/HashSetOracle.java | 82 - .../tests/openjdk25-sequential-identity.txt | 184 - .../java_block_pos_set/tests/openjdk25.txt | 735 -- .../test_assets/java_block_pos_sets.json | 10959 ++++++++++++++++ 5 files changed, 11006 insertions(+), 1031 deletions(-) delete mode 100644 steel-core/src/java_block_pos_set/tests/HashSetOracle.java delete mode 100644 steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt delete mode 100644 steel-core/src/java_block_pos_set/tests/openjdk25.txt create mode 100644 steel-core/test_assets/java_block_pos_sets.json diff --git a/steel-core/src/java_block_pos_set/tests.rs b/steel-core/src/java_block_pos_set/tests.rs index 825dbd997a4..a25802b52ee 100644 --- a/steel-core/src/java_block_pos_set/tests.rs +++ b/steel-core/src/java_block_pos_set/tests.rs @@ -5,40 +5,54 @@ use sha2::{Digest, Sha256}; use super::*; +#[derive(serde::Deserialize)] +struct CollectionFixtures { + minecraft_version: String, + java_feature_version: u32, + cases: Vec, +} + +#[derive(serde::Deserialize)] +struct CollectionCase { + name: String, + insertions: Vec, +} + +#[derive(serde::Deserialize)] +struct CollectionInsertion { + position: [i32; 3], + inserted: bool, + capacity: usize, + tree_bins: usize, + order_sha256: String, + tree_shape_sha256: String, +} + #[test] fn linked_order_matches_openjdk_25_after_each_insertion_and_resize() { - check_oracle(include_str!("tests/openjdk25.txt")); - check_oracle(include_str!("tests/openjdk25-sequential-identity.txt")); + let fixtures: CollectionFixtures = + serde_json::from_str(include_str!("../../test_assets/java_block_pos_sets.json")) + .expect("SteelExtractor collection fixtures must be valid JSON"); + assert_eq!(fixtures.minecraft_version, steel_utils::MC_VERSION); + assert_eq!(fixtures.java_feature_version, 25); + let mut saw_tree = false; + for case in fixtures.cases { + saw_tree |= check_case(&case); + } + assert!(saw_tree, "the fixtures must exercise actual tree bins"); } -fn check_oracle(oracle: &str) { +fn check_case(case: &CollectionCase) -> bool { let mut set = JavaBlockPosSet::new(EqualHashOrder::Insertion); - let mut case = ""; let mut saw_tree = false; - for line in oracle.lines() { - if let Some(name) = line.strip_prefix("case ") { - case = name; - set = JavaBlockPosSet::new(EqualHashOrder::Insertion); - continue; - } - let fields = line.split_whitespace().collect::>(); - let pos = BlockPos::new( - fields[0].parse().expect("oracle coordinate"), - fields[1].parse().expect("oracle coordinate"), - fields[2].parse().expect("oracle coordinate"), - ); - assert_eq!(set.insert(pos), fields[3] == "true", "{case}: {pos:?}"); - assert_eq!( - set.bucket_count(), - fields[4].parse::().expect("oracle count"), - "{case}: {pos:?}" - ); + for (index, insertion) in case.insertions.iter().enumerate() { + let [x, y, z] = insertion.position; + let pos = BlockPos::new(x, y, z); + let context = format!("{}: insertion {index} {pos:?}", case.name); + assert_eq!(set.insert(pos), insertion.inserted, "{context}"); + assert_eq!(set.bucket_count(), insertion.capacity, "{context}"); let trees = set.buckets.iter().filter(|bucket| bucket.is_tree()).count(); - assert_eq!( - trees, - fields[5].parse::().expect("oracle count"), - "{case}: {pos:?}" - ); + assert_eq!(trees, insertion.tree_bins, "{context}"); saw_tree |= trees != 0; check_links(&set); let mut digest = Sha256::new(); @@ -59,7 +73,7 @@ fn check_oracle(oracle: &str) { for byte in digest.finalize() { write!(actual, "{byte:02x}").expect("string write"); } - assert_eq!(actual, fields[6], "{case}: {pos:?}"); + assert_eq!(actual, insertion.order_sha256, "{context}"); let mut shape = String::new(); for (index, bucket) in set.buckets.iter().enumerate() { if bucket.is_tree() { @@ -71,9 +85,12 @@ fn check_oracle(oracle: &str) { for byte in Sha256::digest(shape.as_bytes()) { write!(actual_shape, "{byte:02x}").expect("string write"); } - assert_eq!(actual_shape, fields[7], "{case}: {pos:?} tree shape"); + assert_eq!( + actual_shape, insertion.tree_shape_sha256, + "{context} tree shape" + ); } - assert!(saw_tree, "the oracle must exercise actual tree bins"); + saw_tree } fn check_links(set: &JavaBlockPosSet) { diff --git a/steel-core/src/java_block_pos_set/tests/HashSetOracle.java b/steel-core/src/java_block_pos_set/tests/HashSetOracle.java deleted file mode 100644 index 48ba1cab42b..00000000000 --- a/steel-core/src/java_block_pos_set/tests/HashSetOracle.java +++ /dev/null @@ -1,82 +0,0 @@ -// Run with Minecraft 26.2's server/library classpath, OpenJDK 25 and -// --add-opens java.base/java.util=ALL-UNNAMED. Collection oracle, not gameplay. -// The --sequential-identities mode also requires -// -XX:+UnlockExperimentalVMOptions -XX:hashCode=3. -import java.lang.reflect.*; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.*; -import net.minecraft.core.BlockPos; - -public class HashSetOracle { - static Field map, table, key, left, right, red; - static boolean sequentialIdentities; - static int previousIdentity; - static HashSet set; - static void start(String name) {System.out.println("case " + name);set=new HashSet<>();} - static void add(int spread) throws Exception { - // Invert HashMap.hash to choose a signed spread hash, using real BlockPos. - add(new BlockPos(spread^(spread>>>16), 0, 0)); - } - static void add(BlockPos pos) throws Exception { - if(sequentialIdentities) { - int identity=System.identityHashCode(pos); - if(identity<=previousIdentity)throw new IllegalStateException("sequential identity hashes required: use -XX:hashCode=3"); - previousIdentity=identity; - } - boolean added=set.add(pos); - Object[] bins=(Object[])table.get(map.get(set)); - long trees=Arrays.stream(bins).filter(b->b!=null&&b.getClass().getSimpleName().equals("TreeNode")).count(); - StringBuilder order=new StringBuilder(); - for(BlockPos p:set) order.append(p.getX()).append(' ').append(p.getY()).append(' ').append(p.getZ()).append('\n'); - String digest=HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(order.toString().getBytes(StandardCharsets.UTF_8))); - StringBuilder shape=new StringBuilder(); - for(int i=0;i0&&!sequentialIdentities)throw new IllegalArgumentException("expected --sequential-identities"); - map=HashSet.class.getDeclaredField("map");map.setAccessible(true); - key=Class.forName("java.util.HashMap$Node").getDeclaredField("key");key.setAccessible(true); - Class tree=Class.forName("java.util.HashMap$TreeNode"); - left=tree.getDeclaredField("left");left.setAccessible(true);right=tree.getDeclaredField("right");right.setAccessible(true);red=tree.getDeclaredField("red");red.setAccessible(true); - table=HashMap.class.getDeclaredField("table");table.setAccessible(true); - if(sequentialIdentities) { - start("equal-hash-sequential-identities"); - for(int z=0;z<40;z++)add(new BlockPos(-961*z,0,z)); - for(int z=0;z<40;z++)add(new BlockPos(-961*z,0,z)); - start("equal-hash-rebuild-after-split");warmup(); - for(int z=0;z<12;z++)add(new BlockPos(-961*z,0,z)); - for(int i=0;i<8;i++)add(64+i*128); - fillUntil(49);add(new BlockPos(-961*12,0,12)); - fillUntil(97);add(new BlockPos(-961*13,0,13)); - return; - } - start("control-initial-sixteen");for(int h=0;h<12;h++)add(h);add(3); - start("control-resize-without-trees");for(int h=0;h<130;h++)add(h); - start("collision-resizes-before-treeification");for(int h=0;h<15;h++)add(h*64);add(128); - start("split-to-lists-and-treeify-again");warmup();for(int h=0;h<9;h++)add(h*64);fillUntil(49);for(int h=9;h<21;h++)add(h*64);add(640); - start("split-tree-and-list");warmup();for(int h=0;h<13;h++)add(h*128);for(int h=0;h<3;h++)add(h*128+64);fillUntil(49);add(13*128);add(64); - start("split-two-trees-then-untreeify");warmup();for(int h=0;h<16;h++)add(h*64);fillUntil(49);add(1024);add(1088);fillUntil(97);add(1152); - start("unsplit-tree-retains-shape");warmup();for(int h=0;h<14;h++)add(h*256);fillUntil(49);add(14*256);fillUntil(97);add(15*256);fillUntil(193);add(16*256); - start("overflowing-three-dimensional-hashes");warmup(); - Random coords=new Random(428); - for(int i=0;i<25;i++) { - int spread=(i%2==0?Integer.MIN_VALUE:0)+(i/2)*64; - int raw=spread^(spread>>>16), y=coords.nextInt(), z=coords.nextInt(); - add(new BlockPos(raw-(y+z*31)*31,y,z)); - } - start("signed-hash-comparison");warmup();for(int h:new int[]{0, Integer.MIN_VALUE, 64, Integer.MIN_VALUE+64, -64, 128, -128, Integer.MIN_VALUE+128, 192, -192, Integer.MAX_VALUE-63})add(h);add(Integer.MIN_VALUE); - - } -} diff --git a/steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt b/steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt deleted file mode 100644 index adee8c963cf..00000000000 --- a/steel-core/src/java_block_pos_set/tests/openjdk25-sequential-identity.txt +++ /dev/null @@ -1,184 +0,0 @@ -case equal-hash-sequential-identities -0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --961 0 1 true 16 0 d6a6531cdfb49fca2419e941ccc457424f5732758f4e70c91ee08c20d235fa23 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --1922 0 2 true 16 0 6cb4ed34dc91bbaa0d8a0266f624f550fd7371740184bcaab75a2e62ab5fd520 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --2883 0 3 true 16 0 f670f1e579f1606ea556d019d70c1c70ca36a132f0fe2fb711f36b051a80d67c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --3844 0 4 true 16 0 0782b23169ff830a7e8df9b4c4336e9908a5b618936d718a617dbc0866f93965 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --4805 0 5 true 16 0 92c131f85ba57172f6ef57267c35e1857786f591a11e244deb47985910a1f099 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --5766 0 6 true 16 0 abf7f527a15b390fe5733abc7acdb925b079c2ff885322aa8f5589ee1e6cac92 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --6727 0 7 true 16 0 6c796ec96389ca6cfca7e5efaaf471480bc843642007be3e69e7f470e19f782f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --7688 0 8 true 32 0 2c40726d335d09f9ac592403037ea85f4119aaa0e5a37d30ccceffea1061d57f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --8649 0 9 true 64 0 43ae91711870b9b55a68184065080060c7cab20d8bf9eef81b8808e02cadf6b7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --9610 0 10 true 64 1 daf20b594761396b22bc4010a41ff55595f32082779bb688b3f8d894dcd0edf2 84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca --10571 0 11 true 64 1 4a8338e6843a7d60b19524ded28b0dc059f803630db8d3f276194062cf77e27d b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39 --11532 0 12 true 64 1 d44f108d60f8482a660065e505d27ef2e46073232086a3ed64cfe4f4866321c8 b78ebc662f60e63bfb405ba6bc7f6c08e2182547bce1400cedbdf1bcc398fd89 --12493 0 13 true 64 1 f34b88eb8bc718fbd41c430fa915c46b61203e273da761402e5b00e960251b71 570275ca3b9b4944601bafb2d7a82937862fce41b785acaa499138d4ba2320f4 --13454 0 14 true 64 1 069f90a71a6d817b2db6575425e67facc5916d60929c412cf4e85bd94d7462b9 70d228f8cb5adcbc87aaa1a2a6858a0795cac2b2e805fef7095de635172b41c7 --14415 0 15 true 64 1 db6992057b9ba2d862ec1c3130aea7b5c06413f7d2b35b08d07737a3617a663d ef887de5be1e543580d1ba3c9c864467f7845bc53f347cc530d56c96d494c412 --15376 0 16 true 64 1 76db8af81c29dfb29154fd8c3e55db095fc74edd591367b659dbde03d59882b8 db5d2d544ca06fd3ddb3438a7d449478cac9608c4ce874bb793220ce13ba1462 --16337 0 17 true 64 1 7b3edda6525338f416c78b70d5775addd8db22dd8628d93de36cd485004c2fc7 9fdaedecf5af2930aee1ab8bbc30d795d16e3510042da2b149d1d1172990b240 --17298 0 18 true 64 1 81112a160eb871be093acd07fe44b24a14f90bf293503f64381ccb659f9897b8 0a7d05282271ab28563c6255dc126e00e8c990f3e09497b5a0c52ee332c7599e --18259 0 19 true 64 1 cdb89cacc4756f858e17bd59989f30432fd1a6e4912b82d415233d96b70bb043 908e37aa30b2690bb95e1ef971016b7716a101ca6b69599392aa5e1eb1c5f537 --19220 0 20 true 64 1 8b4c98a175713a69e7a39c5c1ccbf6f2a89bcae693ca24c31b19698609237dd0 563d08119299c94b013c9ddd150ad54c90de5f29b4b87c54144155ec0171864e --20181 0 21 true 64 1 6683fdb4d582ee74dda1b74f682a1d32ef54580ccf6f69eec82b8439dd29c2bb 0d388687314a5852a16f7b40468c194ba94bb756e7699f377c86e2d310c7b2a2 --21142 0 22 true 64 1 c7de33105dd48cea06df66b17a3a38f724dec841e9a0e298e1f44dd86afa20be cfcad2fc0e3ba75e18fd14d22133409234b75f996b22cc44d7d2bb9c2e7ed89d --22103 0 23 true 64 1 c653fcc4eb5db571f293ca8b7b228d0544cee709378f5750f6d1816af09a02ad b8bf175f1204adb56a775b5c643313a3f7f642e6f74c321789c6f366d3e43a22 --23064 0 24 true 64 1 1b4c6fcde97e76a295facb7dec9f1d550baf95fc2d7cb63a38d4b1c49c5895dc 727bf5a5e698bb7bfc66022f7eac0ec6dc6bef46e90750ee86501b71f7d99963 --24025 0 25 true 64 1 993c50fd0595f1f8f396f78222f73058256037c0701006b9604834d87dc205e5 1449819139f42b851bdded67a306bbc38f991c7db4740b10e2d9016b0211349d --24986 0 26 true 64 1 8f84cce6f4b53cf2a394541a25085a70a93d4a46271d8005191cdced8ca15f06 8fbc8fb6069cc09077fc762fda3845bceeb1e2b64751f9dde3a7a1bb5a2fa26e --25947 0 27 true 64 1 b3d3123e481e7e62376a0df3259d20f56e27f9208f58ea6f7e02ba9cdf14e6e5 378bb234dc80cfa68fbb9e19b5f5d320758235454f00a68a76d473c120b20880 --26908 0 28 true 64 1 113b77d0076533415ae3cc64e0e484dbf19f978dd283cd791d09676d976a2114 9d9db3b20eb9232d566bdb8a887f6d834957f9530d865a5ecb2df5343f0efa98 --27869 0 29 true 64 1 8b85c249b492245ed7f7556656b545b72970dd3e0adb0554c6a0d698a842d152 2d4cea9148bc69fb9c7da49f0425ecab77f8c31a7595539fe0f5635670476079 --28830 0 30 true 64 1 9d79923f219c004b189fa8b9aeb0e61299290454e28099798918f0d7ebf3834b d9d3c6b81d39eb30686d491a2124feff758bfa4224b6fb0aac6724f73278a95c --29791 0 31 true 64 1 48cc02b217db0ce88a9d7f2962528e89513862cbd9a8bd0561fb4719c871c325 672d63946b4c94c0658a67235d236b3dde70feac49efffba4468616fdbcc9411 --30752 0 32 true 64 1 c323a6afe71851d3fdc10a55414ba5cab722e81a3b3c8d5a70835cedff1eae83 bfd6951f83c11e3caf9cec5f9d2cdb1ea44be9caeaa25c64d4248881698d25d2 --31713 0 33 true 64 1 5b2a9db04b6b27f6010eaf03d05af6b6be95abb8455e282a863a61a80e396a10 74bae8284c0972d5bc5735b9716375fa1110ef05ce6a2ba8832b95b87673068a --32674 0 34 true 64 1 f2a68e6ed1be399c7e05aea8d1773e4b0a6ce8cbc680ac0e4e0e37e324c880af a1f35a34df27609ef104f0123902335709e78afaa40591e8a8d9fa845d903107 --33635 0 35 true 64 1 0df0d72efdd77cdb5cd5eb8de80f5b71d968b54855bc3f8ee97d7ef4c0faf956 3b1d10d5264b5e2c3206a7d338b4b5e09f873969b893f4bbefef59c775029adc --34596 0 36 true 64 1 3724862237ec4000879297e6b56eae5dec4ef78b76df8d29a14f2855584955af 16f188b175398518f638953266664293ca12507574a37cd297e8eabf4e2cf1d7 --35557 0 37 true 64 1 ebfb5279a8b869a7e8e3f2de372b76b9ada20f90e0d6ca14f0ad6795c61fdc5e 126026f7603a9412df5f1eb0275d8dfa91e2579ce716dd47a4797fbee05fc645 --36518 0 38 true 64 1 7984f3a1f54ded99dfb88142c1a205bb3b5daf29fb805cc15f42efc32f9c5cf3 cd4ab893ff909a144c37f7805d81e488bc32427febb63104a8a7c2bad4df7a53 --37479 0 39 true 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e -0 0 0 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --961 0 1 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --1922 0 2 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --2883 0 3 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --3844 0 4 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --4805 0 5 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --5766 0 6 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --6727 0 7 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --7688 0 8 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --8649 0 9 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --9610 0 10 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --10571 0 11 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --11532 0 12 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --12493 0 13 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --13454 0 14 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --14415 0 15 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --15376 0 16 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --16337 0 17 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --17298 0 18 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --18259 0 19 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --19220 0 20 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --20181 0 21 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --21142 0 22 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --22103 0 23 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --23064 0 24 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --24025 0 25 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --24986 0 26 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --25947 0 27 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --26908 0 28 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --27869 0 29 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --28830 0 30 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --29791 0 31 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --30752 0 32 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --31713 0 33 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --32674 0 34 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --33635 0 35 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --34596 0 36 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --35557 0 37 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --36518 0 38 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e --37479 0 39 false 64 1 e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6 8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e -case equal-hash-rebuild-after-split -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --961 0 1 true 64 0 28f2321938e91464724d62b132c2f9cf7fb0a2baaa6befc9687934c4368a2e02 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --1922 0 2 true 64 0 ed07bc1e4a9c7734c2036f91f06d491a989db5bed82a77e1caa0a191a399bb2b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --2883 0 3 true 64 0 a0605ca52e48537e4e4549ce64bae2dec257487e72aea756bdaa4bbf7d83a4e2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --3844 0 4 true 64 0 672ba16f45b976346fc4ccb484100bf9c5f1170432fecb4770129511ea9a41f8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --4805 0 5 true 64 0 899284429a4dfb8f5be1bb62bc8c04e5cb2626e81ca1816ae48b555de321898f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --5766 0 6 true 64 0 b86f2ae672461be3dbc4fb1be156e114c773542663767aea7dff21d418d90d20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --6727 0 7 true 64 0 031f818ccaf1a0f6f3ac2695a5f9396ead2b0cdbea6a9691eff0e89ec30b4756 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --7688 0 8 true 64 1 dcdb4d5436aa1cdc03b47f05ff26b33571707ca1d57032698d5a3cced8eb81cf 75328e67ca3609924e3438d04b5afba893309762643ec69321741c1a0f64e55b --8649 0 9 true 64 1 6ef7a69d8b4474527f202668739628a9e84e8aee57babb31cc6de596950dfe4d 7f2c5e0a7aded0b2c93c77aa27b6df7976285865977c3f6a03eec123c1968e8f --9610 0 10 true 64 1 5ddf4fb23faa9c9cadd10a231a4b59c868dc1e721218bdd16643d9030d5c70c6 84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca --10571 0 11 true 64 1 b2b41c4135ab2534db7afdc937e1be1291e068da834de83130d3bf65a51d0006 b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39 -64 0 0 true 64 1 d44012d5a8ed3653116e274ef413b876f79946247a682bdd9b2fc3db8b269a3b c6af71d6450f9fa424506e20f0685616c89fe043a5dda19fbde83cc7dfc1207e -192 0 0 true 64 1 7ab4bea925c1e1571e245aca6a4ab0e2406373715e21cc746a3513a038add9cc 62c009a4a11fb122e75e62b432c5ad7d1bc1bb82b6d72008535176714378714b -320 0 0 true 64 1 6fc46d6c4985c56e9c8917daafc02e6a131de058c1ed9a59a710d4140b80053c 38ec63211d138852829eb8487aa68cb4c20c3f632e5c81d25948c35befe41639 -448 0 0 true 64 1 bb752e67c8ffb88c8015fe8bf231dc3f83d6ea7c05be9bd1012115434b400749 767e9ce7a87d89a4846460dc74a883f62cd2b6ab50136a2486e49867684d78d4 -576 0 0 true 64 1 5fa455a6d315c342715490a477a70c30006a6659614879cae764e898d4c83ae4 77840e858d833d2c07b8a8268c5e1536a41aacca07f44680da897966b85d9170 -704 0 0 true 64 1 a3613e36165f18d9fe8e1046bb04664aad6109a65317e1f3cf1e9a3c288af2b0 3afd26cc69fbddc34af7c0339a91bda92ddfe07e617c0eb9f665a632d683a0ad -832 0 0 true 64 1 a3f108c97653742c1916cf89e79cbcc90d0779310f3741ed84b0086ee0ff596c 3a63beba0d2b43f8d749d16ee4ce63604715f7d4895adf69dbe8e2dcff3ea330 -960 0 0 true 64 1 9b27867c5693c0750508e589991ffbea799567656d25b2247648f7616c80a725 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b -33 0 0 true 64 1 34cd9f42fdb5ec5552e05d587bd65ef4d9bb2910ccc8f4d9d7e2f5f1558a6679 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b -34 0 0 true 64 1 479869cf52945b2811ea6e932b61f5f85e417555d76dc7da0040428aec50d1c4 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b -35 0 0 true 64 1 1232cf3630377c25240f08168a7f0fb24fef6820ab40fe2d895d26bae9acc7f7 60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b -36 0 0 true 128 2 58bddd158d069b4a2f196e37235229d9a81ef08a1a1a4b818b9352483b0a1ff5 3b685fe49ef22d30c6786b08b8c27bf481e5f4d00dd9bc0c343ab50f1bfe629d --11532 0 12 true 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -33 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -34 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -35 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -36 0 0 false 128 2 26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -37 0 0 true 128 2 81ca95157f58410f9a0cfbee34db2fc8cf5d7c38c6be4b58de764090fbbe00a1 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -38 0 0 true 128 2 7e416ded09240ca979d531b444983b40ccd3a9ded76428f8bc0c9ffc61243bd0 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -39 0 0 true 128 2 426e9bbfe7eb695d7196e2684a0c1d84c5dd7f0e070dc339d436c28206de96a4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -40 0 0 true 128 2 627e964d4df803a029a8368d3e922d9ee0e7dd08d149cb9e01d181349afbda08 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -41 0 0 true 128 2 1cd561a978f7853d42e13064290aa124467be6e898c04aa85fd7f9c5081693e5 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -42 0 0 true 128 2 5f24daead54530e127923a342164f3ace1a5877836675aa47196616fdd7f9574 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -43 0 0 true 128 2 d82898824c3b6ef18d8ae955a6cfa5f4d587096d79f03abeb2753ba05ea39027 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -44 0 0 true 128 2 43cb7761d8fd4aedf0c779c029a6e0a3d0ff424d6fa4a81128a840fa97427356 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -45 0 0 true 128 2 036a22cfeecfe626de7af464c0f5d714e5820122b7dcff8da90d3be1baf45230 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -46 0 0 true 128 2 f55359b81f8e2d499fb44f7b66f35cf58c369126552decc9b6bfc9f1a7a798f5 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -47 0 0 true 128 2 b886e62743d53a30d851716697c8f1a3d9f739fa931807578b0fae5663251fa7 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -48 0 0 true 128 2 de325163135313f0d3a0ac02bad4df4a1ebcd7b4002a0b715920a0eac07181e6 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -49 0 0 true 128 2 3d3034b9091cf442697b49a8ceeb38a88647a79637c1a86a836c70c818b2f9e9 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -50 0 0 true 128 2 8944b73b66cb104bd6d4fe191d8b492cb94d0d014e39948da1e4f7cf9dbf77bb 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -51 0 0 true 128 2 97796bac3f0defd81b0107eca78b172089e2fa94de73966b96894d0e865c8708 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -52 0 0 true 128 2 4ad7ef6ff5268c8cfb1bf07d45b437987189dac882481f4236cf7f3962be748d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -53 0 0 true 128 2 e15512c30cdd2b2fefbccfb28d948a73bf8e270659854090ebfe974f8014b866 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -54 0 0 true 128 2 4bbc0655dca0176a06116313a8250df5b3abc8806391864dab4638e12660c590 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -55 0 0 true 128 2 2920265c3294bfaafc6a0fe861dc3286e0a2e58fba57e655498830dcbbb4e91f 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -56 0 0 true 128 2 0b4723197fb30f51f8324a9345329eb26537384ad7a279cedc9a49517ede1c21 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -57 0 0 true 128 2 967d783bee8347d24535c9e2109b6b96f19e978800880db01c895e9bba76d849 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -58 0 0 true 128 2 a2b1aa1ce8bf0b09ae9184a24762e9117f3b8b24aca9eace218a3f402a42eb98 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -59 0 0 true 128 2 53e998b5c0fc11cb84735c34b0a4f7bf75875b43753113c9384fc5cd33b40f7b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -60 0 0 true 128 2 c6b4566964cee98292c5da1eb27a83984e2d0dad8260ee2738934e1c96775a27 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -61 0 0 true 128 2 d0f468d617714b8cc0816e9e0774bfc562ce77bfe25dd46b439b6787ee46b9c8 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -62 0 0 true 128 2 3a6cffbd7773e3dbd5df13f8302ea4aadaca610a0274b5ed3885e3ea5a9e9cd1 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -63 0 0 true 128 2 7b12fc9cfc3682a72333b8684bf24d7db1d3265806cbbc0110e7f79f6884c611 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -65 0 0 true 128 2 7f91bef82718e5772ac27a284d115fa788fdeaab6d54d31da9f40048f6e328d4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -66 0 0 true 128 2 38f3868ea0556fef5f51da993555c8d60b2b5a5b9206647ba044c82d90a4d7ee 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -67 0 0 true 128 2 4aa7087bbe3e3e010226b048d350122b6442e3dcf8dffb13966aa4f4a178c206 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -68 0 0 true 128 2 a3f6a41680a6a130f8f9dc079f63b400d66ab382f8453c28c55a4c3efd3547bd 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -69 0 0 true 128 2 0a31f19dc364caba577b9687297c236e90d2fee166c0aae401a6d34e785daa9d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -70 0 0 true 128 2 b16289a9b4473eb6db7ff1107a2cced757ef974256861d6607c902b07cdbd8a4 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -71 0 0 true 128 2 70037c27de33d04d6b52f8f31bb00eb894ae841f3ce7a78c3af6cd375a4b6522 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -72 0 0 true 128 2 7c6c6950efcc128dc0059fbe5d51ac1d500bbfb00fd4946df471881448b3b71f 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -73 0 0 true 128 2 f6ff623325609ec3585f8bf254f11fd246aee17af0dbacbb35ba963504737d81 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -74 0 0 true 128 2 74521421e0e3195e4d8d02fb69ddf11e5cd404a5d1aa01fa17f6a48c1f22eee2 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -75 0 0 true 128 2 b902ee1a8d822bf990e9c974bdac88847d0b95faa21f6a28ec30a4eeeb49f68b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -76 0 0 true 128 2 61f36b928ac7bc49bda4515600637a032af4c0c1320cd6d9416b1f7d5df58a75 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -77 0 0 true 128 2 615e2921695e0af402e876698e65e229ecee3e579f05e24a0bb171dd964fc0db 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -78 0 0 true 128 2 f03979dc4e93ad8ec89d042f145edd27a9c2b61b74d3b418828fdfe7384a9f0d 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -79 0 0 true 128 2 ee281edd421da3ea1f270f5929090d14e93092f0c0068ca395d765a89a031f16 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -80 0 0 true 128 2 d47edbde3765827b9c958382f3d9d34f65e3d52e33d98a229928e9346b61afc3 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -81 0 0 true 128 2 44732290e14ba7102004e7f517662afc8e219147138b5a61b5c82d43d4dc3270 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -82 0 0 true 128 2 4a1c410eac5a048f3a3718609fb373422acdc616ce953e5f3205822730b24f26 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -83 0 0 true 128 2 8fc19a64f73c7ea721400c7cbc19137769e89d34cfeefa59633c82a213bab96b 9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356 -84 0 0 true 256 1 483b278321ce9f366efb1240e44251f0af4ab866ea680315ef96a5cee3d59cdb 94ca30e3be6601240004b38ab375c316c5d4af052423ba6058019e1e22d96734 --12493 0 13 true 256 1 32d900d9e0043e975a69fdc9345b35c3b293088edce67f9e69166e6df0cf3a20 ae31d94b5ba98a6be4ac62effefc8e2eb9732294235edc0d1bff4d1721ad025e diff --git a/steel-core/src/java_block_pos_set/tests/openjdk25.txt b/steel-core/src/java_block_pos_set/tests/openjdk25.txt deleted file mode 100644 index 12281d7a8bf..00000000000 --- a/steel-core/src/java_block_pos_set/tests/openjdk25.txt +++ /dev/null @@ -1,735 +0,0 @@ -case control-initial-sixteen -0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1 0 0 true 16 0 b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 false 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -case control-resize-without-trees -0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1 0 0 true 16 0 b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 32 0 bd040b4c699899cc871720d10ff6db9a3473c3a00843ef2a47650c18e8b5ce60 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 e8fd3997b27b020552b8ffc75253014ecb6d32c75e3c2b7a5e3a7077169d31b2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 e614c27b7c3f68966686beddaec8c9d0e6a736782f9815188d772810ffa5cfe7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 456da144547ca79d2d0f452a3b5d5d0de0717c3f7ea912439a55ec5283415b47 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 e9a3c80dcdc7aecf6f6e464e680caf8442928ff16a2df19a376ebfe2e814824f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 dda2124909b295d83849102f952fb56f3d5ae665feb68c5f13701956dc50b543 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 05bc57e705e3083c3bbb0542020d54766e80a6936947e91bb237399bdc57a249 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 ce507f4bd955c21e907c5ab512821ba76a5333a33301ddce8a54f9d68e6065c3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 61395173eb8ba1c3af19e055472188ffe3cdd8f12104fb6676d338da1116b1ab e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 920d45a8f6fe792d01dba2210d9dcda47ce18b954d797581f7a304925d2c241e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 39f97f2383870373d5cdcb4e2840e9a0d6340bff18155556e9ad5784f9321f26 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 798dc0d94f1d34fbf73e61d8b09f13e88ac60584ddfef416f6cc0a5054ca7de1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 64 0 b428c9d223ead8e831dd55743387d221e5dd15fb794af5fb0c0cdb1eaff6febc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -26 0 0 true 64 0 08dec168fa3ec57ac20bba12f7159978cf61de9faea52788cdf8da8f9d1d14c6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -27 0 0 true 64 0 f71328d5f28cdca59ad849d78a7eeb96a1b8b89eae244866170a81ab9d9a5fc9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -28 0 0 true 64 0 f74d25c360000cacfa1c1001299c45ad36bd88186d727adff1dcd662bdc16934 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -29 0 0 true 64 0 8622356616071041f8c5afde5c6d1905ae0cfdf8a9c083eeb3d6cb1f0294eca3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -30 0 0 true 64 0 30b48c5be69741a7f01f49a47af0225b82b1aee003bcbcc9a455dd4976cb179b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -31 0 0 true 64 0 55e060353390792958f43a710622ca91ba9c55c7c9f1bbb2421db3bb4506ae1e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -32 0 0 true 64 0 634185dc55a65cdc9e2ce81f552963760813490f09d480c4f8b9c52ac05cc5fd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -33 0 0 true 64 0 29b324e71f63025a0aefc7faca6bb67a8c0bf74059e11240f0ba93604608ce08 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -34 0 0 true 64 0 20fc00b062269e4d829083458a351d2b2a7fac66b50e8860de26bd08e43d3c9a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -35 0 0 true 64 0 4882fca6660f8723ff9660129e9b9116c0b0c947adb18d2aae739e2002cb3641 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -36 0 0 true 64 0 b817dd1b94144abbe80eab5a45836ae1083205235b68a0863da3908d9d9b1873 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -37 0 0 true 64 0 bad150910d3afd1ec912a23c44385132d2303453fb029c3abbe84cdaa75c7a56 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -38 0 0 true 64 0 92ff6b629797572226ac15a37f8e8ecd1a941efbd5fe4849d73ad45f566cea5c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -39 0 0 true 64 0 09901528278808d533e2ae273af84c89b2c6d5c3992308fd62acf1eb1d145c12 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -40 0 0 true 64 0 39e27cccf7838fe719334a10b3fc5341fba46a7e9f2d973f88806540fcef7cba e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -41 0 0 true 64 0 20788ef1868b6713a20d53b6e25b547b4dbc18296ea03df1f4d2cf4fd55ac29e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -42 0 0 true 64 0 a1ec77e660556e004b53d06b113717214f66d8b76856eb9813b3b2c4ec4dc8f6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -43 0 0 true 64 0 4e1b07aba6a9e734991a9d4105cb99e08bc9e6c87279ac1090430d4fd58ec04d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -44 0 0 true 64 0 e8c411018c27499d45a145ce6b173494f72c1fadd40cfe0fc353148256bf99cd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -45 0 0 true 64 0 ad87f8e1a54a9e1405a9d9254124b7bf04723c9b92709d480e3d86e207e2cd1f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -46 0 0 true 64 0 513ecbdb7767f25f1f0ff18efc9cf0ed8305564d0be57ef8b8d394a35d4312ec e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -47 0 0 true 64 0 e3e2f84b65499960efc2a4ae5fe314acd42cf2c9035ea6ebdcdf15b494a5ff20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -48 0 0 true 128 0 e1a3fa4d7da04b00751ce2ebf940a4f97f47cde2cb00550a0698bad63dbc5ca5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -49 0 0 true 128 0 241d95c35b8f1597b9024ca4414fce30ccfa52a2a0c91eb66ccb5f1ab2cc7c17 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -50 0 0 true 128 0 739bbe489385ee2319b7fe133afde50a1c624d26c910aec2908ffc7b516bd17a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -51 0 0 true 128 0 baed0246834058891cfcd43100a4bd2fb563179f828cb4d2906c5cdee7f5fc80 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -52 0 0 true 128 0 17d3c5e449d13c652e85f5db23adfd32dbc9d2a7f51f039383b5910ad24ea1b6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -53 0 0 true 128 0 c508dd36bf642807d70eaf8737273ee27d74f80f5af3634c90af88e2a83146f7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -54 0 0 true 128 0 5b7d012494eb8d3f4834039c9bfa0973519cd732a62ee6f075884bad439f3c06 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -55 0 0 true 128 0 9531b3ffab274a103b63a76a681a74b77be854a3778e7d5845dc4bd71819d37e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -56 0 0 true 128 0 235f2987e6a26e48dc5cc15893cca6f0bf9d5d64df7511885ec56f7a21a00036 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -57 0 0 true 128 0 0bc5e3dafa121abf81047ed6763f3129345adb1e73d55e77b204e2d8aced8caf e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -58 0 0 true 128 0 604117d2ce40121669a191c7997f24db09443046d775333bd8e057f12f508611 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -59 0 0 true 128 0 4db2023164e1b306db0eb21902211461b1fede3880bbb861c9ec0bd0f33439cb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -60 0 0 true 128 0 eecf48926325970f68ce3a4df2bd8822370fd58f86bd8e2b0c2cc9f2e3f4441c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -61 0 0 true 128 0 63b132c71f0b41db8e688770d39bc576af5f6a5285a08454247c160fd5b38ba3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -62 0 0 true 128 0 b71e96c6f415261133b2d1d09fb919cfe63855c32cff69bcd2d18603c34531bc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -63 0 0 true 128 0 2f000e4f88896c94219c4588ce6c31b6a0ac7bf2d3a1f3a10bcf950a75b47846 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -64 0 0 true 128 0 c6e3b4a5ea6f274c0fe46e3141558c67adc2a3fb5c3bec3f7f422aef3d9e9b36 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -65 0 0 true 128 0 3d335a80226152e8c234acd6267059fda97f351ff033f2e58a842872d5868c13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -66 0 0 true 128 0 07a3b99f7d8e337e9c9562e4010f87bda53ef618dbddc1d3090df2e1e9d488b8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -67 0 0 true 128 0 282b76144b6a8359d248e2147e9b32b28ddb3ab1fb2c69c066870fc4ed72173f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -68 0 0 true 128 0 f2f7211d8122e766c086ca2262fdbe6522d46687e7e7053d696ce61e80b9eacc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -69 0 0 true 128 0 b03d7171a648580a66f5d0b6166179aa504fcd9726a1da1231bdb084a948efac e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -70 0 0 true 128 0 bc870029844cdf8f4e14aac43b43f514beedeb007925887663ffd109a9a37dc6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -71 0 0 true 128 0 cfe90a75af10d3a2b92db2cfe066834063c4b75cb2626755856dcf99d0273ff0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -72 0 0 true 128 0 d19f8ed1973f3faa5f2c5c4c0aca0f6b786b758b2decfd320e99ce630d189ff0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -73 0 0 true 128 0 03b32c3dc357dbe05d06d858e7e3246cffd8455b38fb876b2d421a3d63db4845 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -74 0 0 true 128 0 2ae522d7b8fc3c9fcf8bd792b92a6860d23c430c4ad8d6dc73adca392062f4f5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -75 0 0 true 128 0 7c4082746cd62e0fcebb1f5a8104ac2db71d21ee3bf97e69be29df28426b0c54 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -76 0 0 true 128 0 ce8e41d72a42178b79a717627c6ed733e656015fa29954840565784ac58b4593 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -77 0 0 true 128 0 ae5cbf8b0b9b6a77c47a1ac0e2325c1fe326dd62af5632c7220238f34bce2919 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -78 0 0 true 128 0 38cd3cad6abf6c2da81a00bcd8d1ca34f23ca39726835553f366b3727dbac255 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -79 0 0 true 128 0 653848f003a2c2760585b8828b4958f6541aca7b61950ca13628d56464bf841d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -80 0 0 true 128 0 d49d77865b84c3a7cdf783f5a84b7be5ceb30cb8c45a6f71682cf3b984548ab2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -81 0 0 true 128 0 8700cd3d5c90ae04ec8ba338e1c66e1094588a416ccc7561ca6f717650ea3e3b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -82 0 0 true 128 0 c3dfafd3708e5242c6004585e5ddf55492c99e3caa32565a1faa450123e1dfad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -83 0 0 true 128 0 8d27190f8054c80b12304093acad95a123a8d3e227fb254d3f599a7b772a4483 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -84 0 0 true 128 0 75967a043d2511ceb414346b3e2c5885c875eb97359774e320800f82aaabc431 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -85 0 0 true 128 0 1491a4e3171186aaa83e999bd234f6ffaf0b9fa8db971032bb324c950edf0d5b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -86 0 0 true 128 0 fd3860fbf2180351752501c55a676fd5d65889a3f3f2b0e6ac811de4c616477b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -87 0 0 true 128 0 f74d27c2225be37a38e3bb76ba968a8947c5da8b1d1f4cc632edecc88358dd45 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -88 0 0 true 128 0 4e1f6526ad508fd29133bd6b213caaa655eccb95a71e30df37af901501a7f118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -89 0 0 true 128 0 86dbe26695073d54a83dcccb4109904826a96e00e15f7c67606fdc9be6a33c0b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -90 0 0 true 128 0 e1fcfe54589354cce2553be418a61379cdce338c7497bc0e02fccc2091c85bad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -91 0 0 true 128 0 6b214a974c04569e9ea89d056b1b281a03bc7cdbd6ccc6e91fa3b1eda791ad46 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -92 0 0 true 128 0 fd3e63f5a2416029c93e6b81a73e086a0e144fd167a72959a1bffbe9a15a14ae e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -93 0 0 true 128 0 5763138049348184e0e2c757a43cf516de46153a1be6934ad9ca41fac42a2d08 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -94 0 0 true 128 0 9647f46a2f9a9c473fb2006a1586af3ccc8606d7f1c4254fd29c37c96ed69b09 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -95 0 0 true 128 0 90db01392bdfd1eb23992f04686fe3fa9da451d17ed09fd476d76e8baebd1990 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -96 0 0 true 256 0 973e269d23020bace974571580315565a5a81abe32bb575b0e58dcf0693b4a89 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -97 0 0 true 256 0 243263b7abc6d8b410e59b09ed52866e539afe8a11d330cffc720d192f849f6e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -98 0 0 true 256 0 b6b6309d0d53df6e2e3453e14f290ff6f94d75dedbe1fc59c5e085e3793b6444 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -99 0 0 true 256 0 e67528897a8de849b61f7a2e18a48582e0c7175ca50c333f7982adc36f4a94a1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -100 0 0 true 256 0 8760f83e86bb18235e32053c069ab88084bb643139bfc2ad230346618164b239 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -101 0 0 true 256 0 fc0c9fb527ede41e3f8e0ad6b3e99d4599d330653f172e440c96182412632460 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -102 0 0 true 256 0 6f72a6b227e3326d1a75c9809879df0df3c4427545e69c8a4d1ec78d78946faa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -103 0 0 true 256 0 178a387c6d2a59ec501e4bd80212a5a1215afe78ec450631b47c21da228786ef e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -104 0 0 true 256 0 96dd19472f1465731ec3667b323269e6c71d66c26bbfa4c35208ea57d8b176ee e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -105 0 0 true 256 0 9e2e3103b64f55e4ae990bec98fdefe7d709f8fd818ffd38717d5c0435ab6825 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -106 0 0 true 256 0 af55410aea8d74ec0eaebcd8b151687ce65451d987bfb68f964d83fad52d60f6 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -107 0 0 true 256 0 b48fbe3dd6b6cebe136e59edef4e66fc277edca38bc70fa71b3e12b0ec508196 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -108 0 0 true 256 0 b548022db689363aa78379d652ea691e94e58c34961f4bdd2aa3c9c506883bb1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -109 0 0 true 256 0 c03e6719a25498430226cb264f084e2bc04583b9a8015f33a1e202754e1150f7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -110 0 0 true 256 0 1da7ea240c05ee4619dde9497fc4ae26fc664ba55d776b38d1547d9def199167 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -111 0 0 true 256 0 752375aace4815277db268064d07730a6d260d24d19a03bfdea2f87c1ba28b04 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -112 0 0 true 256 0 156bb9688142e7ce8d3a2871a329ae5e49653235b519d91ebe36262f4bedc2f9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -113 0 0 true 256 0 e2c467c4a663d0f6feb6fcf5a31fb6fbffcf54ba515135585398dc9f33a59d88 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -114 0 0 true 256 0 060b007f65cca7b15678d4ec780fcef696e07b6898cb1d0d1d705f6378298a84 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -115 0 0 true 256 0 9c23608a13c11cfad5119f30c45903a0024445fea15f3699bab2305b572c695a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -116 0 0 true 256 0 304810dd4aa63d6bc21a83b762481cefaed49a7d26f53b0f8b0eec317b38c9cb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -117 0 0 true 256 0 c0bc7beae38699a230200b45d4de8f8a62606d92c97dff6f677c1c71bdc81823 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -118 0 0 true 256 0 6e127416ba68530fa1488758e66c6480a080d87284537e1ac3769d13dda730f9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -119 0 0 true 256 0 95bc47f6b9fa225965cc572dd394f707d4bdb3fa047b4a87a7e429723a2616d7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -120 0 0 true 256 0 bfa96a0805705a10e7c2252d42d79b7f00a70e7064a2200bcb14ecea88c6bfec e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -121 0 0 true 256 0 53128be53877cc0951bdf564f12878479eaa29d394e2746e3890f3c57e53e3bd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -122 0 0 true 256 0 32c500e507f4fcea1bd712f53ccaafaf643041e52c7662c0b10aac19304fed15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -123 0 0 true 256 0 d8e0afdb2b1e2d4b098919875e5067de17579855795f493b3615bcc60a246f0c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -124 0 0 true 256 0 ac3d3a7277c728ea6c160adb6b031b9617a7e0bda2713d36402e8dd342dde3cd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -125 0 0 true 256 0 857f7644fb1a082214fbc7ab1b85969c34ad8259e39b3147a68326efbdfa7bc9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -126 0 0 true 256 0 fd5ee8335ed8f13c52b49c655c720655150ff1e8893a5ebf7829c27dfd854d9a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -127 0 0 true 256 0 f8350e97d9a0a5382bfb52804a3b301276d0e16123b246eeb4a368fc72fe0860 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 256 0 ada636bb30d32d830579f51f599209312fa776402f03a9606d2d40d496b47dae e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -129 0 0 true 256 0 953e7c5fe433b7bae8340fae9bdc17fd2898774ab2b19db0e22b8657fd5c13a1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -case collision-resizes-before-treeification -0 0 0 true 16 0 a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -64 0 0 true 16 0 15b8e296e318d4f07cf46716611157ecbe102168ce40f12eb058c1bc8cc52af2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 16 0 16882ce8b10032f0603149e29b8bd8462526eb9b3f39c99e37f1333bb4760249 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -192 0 0 true 16 0 ce2c86195416b6060c1cd874f8a0962f61a0b668cefb7d6312616cf39a637b36 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -256 0 0 true 16 0 78641378f6554873fb864e4194ecdff433bca53f22f635149cca0e9ed4479dba e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -320 0 0 true 16 0 05c2f50ece14c333bb6163ab3b2b0ff43980714bdda65ff6f4f889610be5b807 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -384 0 0 true 16 0 c34af995a927ca99217a14c182d2baa27fd75546825061fb940c7a7bc8357420 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -448 0 0 true 16 0 b3a7ffb37c93d5fdda74a0bf76eaf23333ab22a40df9026739c188931ee7d1eb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -512 0 0 true 32 0 e08198bfb8e435d23b22d5a64bdbc76f96adb84e78e2ce0d7908f6b1725f0010 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -576 0 0 true 64 0 98a698a0285bfc6c9a77daf1804ec4d6c5776fa6db65f22c371f6d1321d384d5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -640 0 0 true 64 1 d79759b6efc7cca5abd3d8b93fd7dcff096ae7cd2d031b10d0c581b7caf000ee 0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa -704 0 0 true 64 1 210c2d82d8f23dfc6e324c7ffe628d7ff639c1cdba5781d02df105b787c25907 33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e -768 0 0 true 64 1 680b2385ed8075123be0949fff2d9a64ee2a83f429c97ff41d9cbced4b00561c 103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c -832 0 0 true 64 1 5d7e02e447fbde77c3091a3d7198f49b664fe9633863d883bc5c8333d2aee470 19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816 -896 0 0 true 64 1 8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 -128 0 0 false 64 1 8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 -case split-to-lists-and-treeify-again -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -64 0 0 true 64 0 9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 64 0 c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -192 0 0 true 64 0 f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -256 0 0 true 64 0 dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -320 0 0 true 64 0 a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -384 0 0 true 64 0 3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -448 0 0 true 64 0 99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -512 0 0 true 64 1 10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -33 0 0 true 64 1 74fa7598312e57412585f54360b45ca74a053a3371424dbaa5a674e337fe9ad3 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -34 0 0 true 64 1 337b8e3ac9094219ff1424939e3cfe69005ce4d38c7507b9e0a2ca33f6cde97e fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -35 0 0 true 64 1 261518dc617c1bce90ee63f07f7bab37b5b56e2221edb4d754ee83fe20749448 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -36 0 0 true 64 1 9fc8912560ebd5d2af71a7e1896ba340acf1fbc767f937f723cf35a6292fd7b1 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -37 0 0 true 64 1 06c22a7f6ac63d7d0277e56d88e4fbda21daeb73f376503dfe842c3fbbfbf3de fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -38 0 0 true 64 1 cfb15c9b2407fa96fd86d628331d26813df93e1ba671c47adc4b3475ec9eba00 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -39 0 0 true 64 1 8bca0a05bb458e402384f6175068addb9e802546929a8355e674456face0dd22 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -40 0 0 true 64 1 7f33eb30aaff727789a679b7db28280861b53ebc2e6efadadd34d815d878d791 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -41 0 0 true 64 1 c88eca1372e2dda34ddf5e9c55f2cdb30546d034632fa437473bfd16532e5c00 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -42 0 0 true 64 1 005f07817a86ab001b8535904ab805f6aecac9aed4372d5995c80d9e3567453c fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -43 0 0 true 64 1 df490232b0b5cec29aa17820236ae5dfaa07309b013b1414fa7a9f535fb3e9aa fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -44 0 0 true 64 1 2d8eac5fefe6230281d4df059e95e82033115386bcc9a5927a52ffb76b8971ea fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -45 0 0 true 64 1 20e13a2bc6eb489d32421a142f21b84258ae56eec4ba603f787e3d5d702aecee fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -46 0 0 true 64 1 cf74506bfc094a888feee4d6f771dd0f0161966ce450e0f36e57ec2150b1ab96 fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -47 0 0 true 128 0 86e4e51ab3794cc9f5373645cfd3ef563b6408c05835aab1fc36577cb7e3dd17 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -576 0 0 true 128 0 4c841a696aa02076087987f2bae9c03155ee8e4b121e1cb1b94c579e1bb45741 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -640 0 0 true 128 0 383f67a53d42dde1712fd6afaa8a295c654a16ab289359b4914ae9ec0e93b70c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -704 0 0 true 128 0 f09a85cdc693ba9327699fe40502c073c252eda3cdba8e65cbac29bd780a51c7 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -768 0 0 true 128 0 d715149336c4b95abbdb0ccbdd89d2bf6a7fa2a4301ceb0b0e073332a2b687aa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -832 0 0 true 128 0 ed0572bf2caaec4063823accb4ceec8877e4c202ac0a261d14246cd88df681d0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -896 0 0 true 128 0 5a60e660ce5fb979be0030dee87182a6ec6d95cf5ded5ba572ec679cfdc297df e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -960 0 0 true 128 0 309a76c5f12235c69b0beac9c9ff154c8d61cfa7accdc5fc3b03744e4437c8c2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1024 0 0 true 128 1 a3d2858b7913d65d466ae360eed2c15ca9df2da07d5daa493a341e00164b47fd 4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf -1088 0 0 true 128 2 db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -1152 0 0 true 128 2 c0e6a3558abde3b100ce9072bc9ab6dfc0d11556303b22091a0980554266b65c f465512a719c69f51b0cc0168b19fa3fdac821f073298e0803e20da82b3bdbbb -1216 0 0 true 128 2 4bc3faa5dd64eefc41d86431c22a2851a773c6ebd51ba4d8814965e2fa17a9d9 635ce30db786756077e2f13fd7e8f44b887ffdb4117bf2243cf3fd1a67627cd8 -1280 0 0 true 128 2 87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e 81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf -640 0 0 false 128 2 87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e 81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf -case split-tree-and-list -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 64 0 b78199ef26bb1ca970f06edf1868b0453038474c0189dd304bd19fb8d7e3a754 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -256 0 0 true 64 0 65cda444c639049cc359d59bca2ee1291271ba98772fb4efbe1803847f7d76cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -384 0 0 true 64 0 f38d2f439f143859b95f909ec22b042b5744f19b85f04b60f23a58b4621867a2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -512 0 0 true 64 0 0ff6f0c7ab6c88cdda2238366c6d113609156cf1a596c11fd22aec4a395363be e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -640 0 0 true 64 0 2f69abc26249c64e02c9bf93f9cf9780020c652761a6d93b898c87dca8432ac2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -768 0 0 true 64 0 ce430551672bc0736fa61699e30b1e4251a52c0edac72ed0894f5fe45934d03f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -896 0 0 true 64 0 4a8b68223bb91e4d7ea1e8ead25729b8d8b15585b947aaf8fa65762cd6494b06 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1024 0 0 true 64 1 52198185bd42421b5f42472e09cf81dd752429ca68128f8d77577672d8cdfbf2 4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf -1152 0 0 true 64 1 29269f28279478f49442ddeb316511669ea0b05ba2afd64748504e17d8915eb0 9d11d8c45174163e45cd775852192b5f3c601d4cc3e00b5c4062d3cd9ffb1151 -1280 0 0 true 64 1 2058098fb8a951f6331a42b03fb1f4fffd935d51faf16524308334bdcd5bb9b7 4ae62b44b3f7d02d9a036edbbed4351bdf1e88471cacf4c3649e17fae9be621f -1408 0 0 true 64 1 a93b02668b935b7e1036ed720e59038fca6b18fb51af6ced1972d4fd956d7f93 44bf06d402ac3bf46208d23e997eac9d85a2a74695adc4a45820e29e6e524d07 -1536 0 0 true 64 1 226a007cbc1be62b0da3ce1d43179cc487c23e5b576e0b933ae5afc74408256f f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b -64 0 0 true 64 1 2ddb2670968f5e0ba92fb4d456359da168b80e1f79527cf1086de8f0a92756fc bba9b12b91ec2d8cbf357b01939459a5e81d7ee0d52f9df2be33d60fa5879b7b -192 0 0 true 64 1 4d8fcf95bba4233a8357c4e8889eedf311e229cbc93d72479982a45ea7d22391 d48080bbb1d91cf68559ab959da3a42256b6871f2073e7a16632d484ac95bd6b -320 0 0 true 64 1 ce3ad225e4b7cd750ed69cc7b26b46d7b43e75eab2421eb467972487eb239c78 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -33 0 0 true 64 1 859a413b7e585ee358afa8bc17fd673df50b2293b36ceb8620d64cddcb683676 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -34 0 0 true 64 1 ed90392a38baed8d084b6052b00442b4745459c2a6db575e311e8eaab0cfdbe7 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -35 0 0 true 64 1 99ebebb66472b46e141ee4c4530b473bb17c176c689caa155dbc63c3c870bb7b ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -36 0 0 true 64 1 0f967074d74586d94e777fba69c7738144a292257927c7e349542284e3414e96 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -37 0 0 true 64 1 9037845ee6c7370506b982d54c2f3f844929450099b7317ff600df834d51dc10 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -38 0 0 true 64 1 6c0d3b26a0d013f0620b8ff2fcaa73793643fb3b8938155230e649e2a7558bef ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -39 0 0 true 64 1 e3e535af2e3f5c5674d36e9649293d2e23017787146d5dd1d1b12e749cdb5619 ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962 -40 0 0 true 128 1 d05a3191e19d867d8cabf405353ca2cd40724ad4e939164ea36492bf05254aea f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b -1664 0 0 true 128 1 7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2 5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799 -64 0 0 false 128 1 7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2 5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799 -case split-two-trees-then-untreeify -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -64 0 0 true 64 0 9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 64 0 c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -192 0 0 true 64 0 f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -256 0 0 true 64 0 dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -320 0 0 true 64 0 a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -384 0 0 true 64 0 3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -448 0 0 true 64 0 99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -512 0 0 true 64 1 10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9 -576 0 0 true 64 1 b01ee63d0ca97c7ff5fc33f25054ce5bbc0f3ae5fd96d16a35a852f55dc1ecbf 531a39d6203888b7cf2233cfc01fa5c27000c558083f7e191d0e21308cb75a28 -640 0 0 true 64 1 d31317af96cbe10e265788dfff1235854fa02d489be1e276904684e0724a31b3 0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa -704 0 0 true 64 1 3f06308ddd03feb8b194ab029a8d3c268a6ff5380a2b48f667edc3ccc415b8c2 33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e -768 0 0 true 64 1 6e3db8553740d08187c9f968350f06db1944c44255eeb185aa2cb59310c36bcc 103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c -832 0 0 true 64 1 8a70c584875601d81e01d80db8e9a082bd24db9dcce3643e0ec2a7a68deb5711 19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816 -896 0 0 true 64 1 eb6bfff5a1121e1b2593933b788bb2c6483da0db030729523e405f4089714740 d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2 -960 0 0 true 64 1 4d4c7c82dbd48903ca8209bb3525470e1fdda1be4ec2da4d5d2b4571569596f2 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -33 0 0 true 64 1 61eff08a3baa241f13634e9406aa5da53488ddd0ab15ec5d17854f91cb929ffd b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -34 0 0 true 64 1 4599dc62b328bd6f29a70cb79ecb99413bf63b4a198836e64c7c0f4317bddb92 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -35 0 0 true 64 1 2276239a003d02c9c01c481170cd4edb890ce20f5a5e68fc776061cb0478dd50 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -36 0 0 true 64 1 7108f5a2322417122f7886202203533ef612e7ec7126196dd7b3baea473a9ad5 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -37 0 0 true 64 1 046dc2e6a810d1b60c61771dc28f2e689545c336492f478eb47b504c2526a456 b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -38 0 0 true 64 1 70fe5fe50418532d35f6c936d41b310d566708a0c6af122996e990c36c7a9e1b b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -39 0 0 true 64 1 39e2c133c42b885d3973c311d0fd2ae80a6557aafde2e34ef22cb58b21bb761a b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10 -40 0 0 true 128 2 a888f38ad0588622f4ed4f46e54f2546c79fb15017a15322e422de99fb7060dc bed83c0b1895a8b232cd3048a42d2a3baeacbe447d1163c29f27a793bc0add6c -1024 0 0 true 128 2 cdc50dfb2dc31318f379eb0c0b0afa74256f8e994d08440cc9087cd9d9fea924 740624f785d60814ad1b10d053b6fb7a3592c2f4fe18b4212b60bfd51c980083 -1088 0 0 true 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -33 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -34 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -35 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -36 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -37 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -38 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -39 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -40 0 0 false 128 2 33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -41 0 0 true 128 2 3ccc01cd8cfdc61feb802550718e0f787c06757f50dd3ab1fca746b621229b96 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -42 0 0 true 128 2 2e0e9648fb1191ee4592bd70092117e6e96207b6c3641bfc33d0a0317c348d7b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -43 0 0 true 128 2 3b91c16ee7900ea3b5939439afd261470c3b37fe4576615349b48cc5083e2e9b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -44 0 0 true 128 2 bd93d34e20882961a53e95a7d150021d9253e5206f366350fa6436f3dbbf9e89 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -45 0 0 true 128 2 2b9533a1d216ecd266502a7ee464d8d3b4e8a634ed17766ad916d65f5e850d75 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -46 0 0 true 128 2 017b31c64164e6b89d3495ab25e2cf93137b1ca69f5ac00234eae7a24c224a40 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -47 0 0 true 128 2 db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -48 0 0 true 128 2 0d466999062222d7479af2d7a1de605b199c98b0e5e131b042a3c59e226abdcf 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -49 0 0 true 128 2 02f6a23605a0437ab0d1002609e5749ee373e90c1c33f3998df2402dba6057a1 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -50 0 0 true 128 2 ef8af77355ac51a6ea98bc146e8ddd265139b9fd2b9f3e64e788785164fe2ca8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -51 0 0 true 128 2 3361a7a7b92eb51bc4a8bbad523c8cd9431f0bae52b4f53214b4ebbd457883a6 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -52 0 0 true 128 2 c53ae02483e6cb5e6d4280dd18019a86bc7983431bbbec13d4b782c4b87b500e 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -53 0 0 true 128 2 e8b94e181889ccd9bdb6df9fb992e2e2ac5efa429020d8b53d778665090ffffc 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -54 0 0 true 128 2 6455ece4299525a8b03c6fd853afdbeb505840c4911025a08d3fc2e1ba85304b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -55 0 0 true 128 2 bdf1ddd938f479cea520582174d02d6b91d852a8b9ce3451a8e2446642f0fded 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -56 0 0 true 128 2 9c4f30c20cf9b4ec359941df1e5fafad5299fadf5efc9867e59db746d5e9e923 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -57 0 0 true 128 2 fc487be5b93639d516233e31321d930d9c1c71491a7f2adc7cc35d7e83074d38 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -58 0 0 true 128 2 6108479245c62c06aacecbc33f0907de16c86ba8e8147308aba22f915186a76c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -59 0 0 true 128 2 d0da7a405abe930d11afba76ca5e6ccd49d8c53c418a4aaae372b4aa076bd0eb 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -60 0 0 true 128 2 80619a5df249a65b309f15f60748ad3c9eccbd5298faa0739fb3c3c0e9e969c7 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -61 0 0 true 128 2 234566a1f45eec8b55f23a3d6d5024f28a42c71316218968d65f3c825b881dbb 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -62 0 0 true 128 2 40e2b139cc37e572578e992102ba110f29c193a7b8dcc4a824858530c0b1c566 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -63 0 0 true 128 2 f1198b28030d058975c4ea8ddfde0d9ed397c0627e7739667512dd1f30b7136b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -65 0 0 true 128 2 844ed0cca40294719622f7a5244e9eaa2119526d5525ba13ae3b3a9e9bf602e3 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -66 0 0 true 128 2 737586e22d8404af99c9c863cef2b3ca281da7c92f52c9433ad100ffcb0258a4 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -67 0 0 true 128 2 ec35df7827b3da2d29c8bf31805c7aff1c954e96a9b8a2f97c399dcd0f77a75d 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -68 0 0 true 128 2 be0b1d5e39585b54703154e3b6d51967cbb4d50aee663267814f691b5c33a2c7 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -69 0 0 true 128 2 a1533222b0b5385d9665b41036d48d7270efd6c4780e37adbb9fe1f73459fc8a 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -70 0 0 true 128 2 0cf65aa96a45774b0180079805fbba112a366531cf113000514a4510f6d25559 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -71 0 0 true 128 2 cdb78ee58b1f1bad0c5be4baaab0ec79471c99bd07466752ca8d5e018558f6f5 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -72 0 0 true 128 2 078a594a77f0d723933f0241fc239192a0a11dc1101b75545a74dc65d9c15bb6 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -73 0 0 true 128 2 046d4555fa313408d626a7b11cb3f25f8accc65508e8dc7477f16a9b65046af0 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -74 0 0 true 128 2 ca6ab9ad4f63add965e0d0e0e40ff4d8223d01c0b82082342668d6fa94e04156 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -75 0 0 true 128 2 a8cab7f7238e1247180ae1e087c8b5cedfd72d602ded67b81080c6de3ddf91a8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -76 0 0 true 128 2 a98c9cdbe06f49e10afbd317b80cdb1edcd7fc929e77e115f2e5223c0419545c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -77 0 0 true 128 2 8780d4356c825eb702ad3bfb8c3d0de9da3f28d200910c2a081957f08218a6b0 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -78 0 0 true 128 2 83e3a86c2d7b4c08b62bbd22473e7052e8e13e20257b3875e4b10198a70922f8 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -79 0 0 true 128 2 1c4e24a41cb5d433503faf3636aafed824727cb4994a1fb78d451c5e3cafa236 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -80 0 0 true 128 2 33a5b55dd13a08d785e2d263381abca5ca387daeabea6933ab92fbce5d48f51b 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -81 0 0 true 128 2 6e0036dcb731edee32cc9bf35f2c4a64f3e28539dd28ef6c8c1eb4df6e8c4f77 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -82 0 0 true 128 2 b092156fefefdbe327098323d224454ec04c3ff21d98ea8e768825df19466aff 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -83 0 0 true 128 2 e26fe3f36f09b2d1114dd217dea7a59669c6f29ab1aa19d9d3399c236261260c 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -84 0 0 true 128 2 37ac1020f6f194e18a2927533db22e8211a08823dc209576ba5cf14214066fa3 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -85 0 0 true 128 2 0c6bb724d200edbc95d472b9476ae9728d5616a654083c959158b24ed4f3705a 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -86 0 0 true 128 2 0269da117e5f662d0b995302da84ab315c447ca8d9871c42fa34705d846f09b2 5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87 -87 0 0 true 256 0 8c0d641bad96e785ed5351338f6671479a5d92abbe0608b5726b8f3a21972c88 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1152 0 0 true 256 0 bf8f9f14aa073901ccf2866514f64ac72d6841463cbbffba9ca5997cc6ff12eb e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -case unsplit-tree-retains-shape -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -256 0 0 true 64 0 a478bb8c30b66f77108ab0b45caa8ebcecce9482d6bec317fdeb24c06814f77f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -512 0 0 true 64 0 ae8ad6418957aea5000c47bfe689bccd3e85fa75f8503fe13d8f150fc96a2dfc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -768 0 0 true 64 0 4bec31b6a9b3669f759dd56296b90f10c05c5ebddd383c4e886a909c7f5e141a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1024 0 0 true 64 0 a387ac29ebd18c21d385b3752135e91f16010abb678861cd63ee1d014a7e5a3d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1280 0 0 true 64 0 93dabf0ca5af28828eaeeba9bdb489ae70b8ab9836ac0344bb264d25ab052cf1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1536 0 0 true 64 0 5f9678bcfec30ac864303c89c527d643ebc39e8e3959db661091aff5414f3748 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1792 0 0 true 64 0 58fc68308a10925f00838fd5925f71c3e1fcd82c63fe134ae255c3c2ac31c3dd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2048 0 0 true 64 1 4730fb1093e41dcb9b8ec61247362d5e576f81182123be69be18fe6845978728 ddc69b8f7753ebfcc5bdff53e39dff4832e4aaa1082b7e906966c42316ca0976 -2304 0 0 true 64 1 7a424d1fc373be358d3d9ec245478c13042e2abc1dfa58b6232c51d255aa9a92 54e4f282ac513b8ca619d1f6867d93b186e85215a0c3c2f874786c825f27cc1f -2560 0 0 true 64 1 d8b526cbd01254dc95b910884f55bc7ab1156e042f2a7e4c30b9b60efa2ca7a2 d46f38deef2ff7fc9b0570c21887556c09e8428a158a71fdc52110869f1c141b -2816 0 0 true 64 1 5f6c46ce2262b67bbdf1909d35b2187d102a2f629b92d26434f215edccdf8596 528573c7a4f186bc1a5a7f9e99130009d87f0e3b4ef217e2a357043ab8914461 -3072 0 0 true 64 1 8a97bbc7db7da74c60a9bbb2fed3974adb5b763a0b007654b9fe1a7bbaf39e06 c1cae48efac87ce79879b940782d22de6eefee3f9f5c07befb9045827129e9ac -3328 0 0 true 64 1 40719c2f20379c09c4d1612db07c3656fb7d2dc341a3ff4b507ae85e77cd8817 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -33 0 0 true 64 1 950ded5f9c39bfeccf2e3ba6c6dda9c63dd331399df581d1c7c36cb1fe2c1a2d d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -34 0 0 true 64 1 f36254f2a6ad9eda41d53f4df2d3d4944931a1581a74d734f99295a383aab8fb d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -35 0 0 true 64 1 dd2db5efeb1a7535e5f91ee73dc2ae1d2fe65074256d9a8d3b6f090d30118675 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -36 0 0 true 64 1 516d4748202f94c3ea65ef8ac3835e3cf9c701bfe205d103af883174b565c571 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -37 0 0 true 64 1 6c6e6d68ab58db6f3f1feb1291abe7ed56cb98e5c1c38494e6bc5cf516d619f6 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -38 0 0 true 64 1 7fcc8e29f197ffef1448b5adbc6920b4006485fb6cedf903b863913ca3cd030b d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -39 0 0 true 64 1 61ef7f6ab2adad7c5db6e3fa529251e61541dd733614e61350b64f59f41ac760 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -40 0 0 true 64 1 5c0b1c0dd5d698f20aaf04f6500a11c88783d597932a590441c29a2aee810b31 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -41 0 0 true 64 1 755567bd07c916a5fb611659a81e6619bccc698c1c7c7f2de24eaf34dc184886 d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -42 0 0 true 128 1 b58897eec4519e77d9c74dc6427b79b1bc81f1c8ef3ea257a0ed4a07b05533bc d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5 -3584 0 0 true 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -33 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -34 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -35 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -36 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -37 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -38 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -39 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -40 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -41 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -42 0 0 false 128 1 941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -43 0 0 true 128 1 05237242ffe4362e8011982fa80d1f8f95fdae194807c896f3564c03d10ba72c 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -44 0 0 true 128 1 75346660f88c9a02e09d0fcfcae4774e455ca28b451dfc79b4ae29a10b4055b2 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -45 0 0 true 128 1 0e948d6c42cedd8d5dcd2a99d3fad83f8eb61bf956c3cdc256994e2131359673 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -46 0 0 true 128 1 6f752248a0e2e17d495dbef0d98f9a9c0ec0d545f627e1b01a36b4fc34ae0184 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -47 0 0 true 128 1 e07388a68cbc03672fc61020a251dd573fd74d6b9eef010d3c6871ff986475eb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -48 0 0 true 128 1 c25e02dc14660701db6adcd3f3eb11d2eda38c86b1433fd9cdd59355024576ae 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -49 0 0 true 128 1 c8e55a8de5a307bb1eefe72bd969039f54f9fa6ac42e68948f17ce20cf57da98 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -50 0 0 true 128 1 c0be431ec284f336df9e679c4ba2175f8cf399c3b553ee33928bdd550879e0ae 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -51 0 0 true 128 1 818ddb15573df1045986d47a0cae81ae0fc5e5ab965e00373131cbbb8de0131b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -52 0 0 true 128 1 19c246794cdb9af948af72c8b16ecedb9f13cd066be144524ff603d5ee55b428 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -53 0 0 true 128 1 b38d7c9cc8af5ba04ac5f699b19e92a4262ff0a1f610dc322e774b61c811d57c 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -54 0 0 true 128 1 1bb72c8d78d723512da752689c2908cb5e8c308ff8807d57406a2a79637ecf5a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -55 0 0 true 128 1 0b756cf2684e72ad89dc068f07498aa16fc95a72da830b6eda7ae7dcb915f4c4 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -56 0 0 true 128 1 b8d890029b2f86ea9a91bc04c9ef868326cf18d76a1429836eaf98083a8dd37e 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -57 0 0 true 128 1 e6c79b8be77b814f361b79243228d8caa359c9a6bba68c318356e4fdfacae6fb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -58 0 0 true 128 1 3503667ec4bcfeacad1214fde9331f761ae03e458b91a3bf70aed8f9efdff2ed 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -59 0 0 true 128 1 f5738f36ddaea8dbf34bb82a9ad83a4144b52da4a20fd4f6ac68baf6601d0954 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -60 0 0 true 128 1 bee7b3649a30fd73d0636927c087053db95fbb6779a6879b8fd9d7ba8e5ee87f 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -61 0 0 true 128 1 70cf274a0c0867a30c743863cd561f6c21beb002159269aaffccf8aabd19b361 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -62 0 0 true 128 1 50acf44d23a23d495b15ac94fa178c54870ca45eaf1d3d0c5735e1950b2f9c49 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -63 0 0 true 128 1 d9f3456da6660ee5d8c690d8ff4a3741e9cae3ba6ae9e01570d2531a039b9ffc 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -65 0 0 true 128 1 4932aadcc37f950630bd3368c9f9a2c9f2cf3bbef0a70b62188e92f80a4579ac 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -66 0 0 true 128 1 fa31f8d1b22cd611123e0a6594a2cf073595988c51e4a40322bc1891b24b8ea9 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -67 0 0 true 128 1 0cee3f43430d82a7fdf3c47e43be594e5f2630609be499f0da0f916cdc629ff0 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -68 0 0 true 128 1 53b937950557ae59ea765a77ddf768d205ade40b9d533536d7e5963db82bae94 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -69 0 0 true 128 1 277eabe39f3c041c26be53843649a753d69971a66706439cfb7d10e09d47f833 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -70 0 0 true 128 1 db7ba54337cbf904b29da1e7ff8c4012fde7fdf1a5aa8f7f88c3faebff67a792 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -71 0 0 true 128 1 cd6d98ceb4f45721aeaf001bd84038d952635874c745122c037edbc1b311ee11 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -72 0 0 true 128 1 4d0be0aeb34dc6e937badbb4a0621dd18630b2f5f944d33c9db9ecd5c9261a02 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -73 0 0 true 128 1 7bcee9e1df91c562cfe5035feb0b1960a5e2d42b7b1eb91b9efd31f6bede8fc6 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -74 0 0 true 128 1 3494ccbb48891a4cdf29d7f3726586f4ab49b2d01850314385153d933130f98d 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -75 0 0 true 128 1 a003d215d6b2c61333b842cab1b725bd0209023452adcbc053a7e630e6f99f89 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -76 0 0 true 128 1 adc3aa0dde25a793f7205d2106526025851dd4bf53681552a52b73de20e3f70b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -77 0 0 true 128 1 dc08d76987887151aa558f779c9880a2881eba5b46b3a97de775a14fffa17953 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -78 0 0 true 128 1 c95ec6970dacecb4a40127afc892f071248e025681fe702c580d2fa22dbe3879 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -79 0 0 true 128 1 0c440e2813b082d24833f5310cc548bde774bd847cb5f36d1cb56a13e51dcf70 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -80 0 0 true 128 1 60d1ca1c586c0d63c9e3cb1c904072e32f1c6716c223f717225df5e38fa337d4 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -81 0 0 true 128 1 d46cb69be72232d7aca2933707ec894e2a50d611354355ab55cf528ef254ac3b 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -82 0 0 true 128 1 712033931e1e416382f7214cbf099ec5ff3522c395c6231e7597d6bba71706a7 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -83 0 0 true 128 1 b12864d4a177cff6a56ac5badc42f7600e7c0cdaf6ddc9340968f35827194e08 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -84 0 0 true 128 1 e5a1c471a0ef01b70abd17582e76ee575cf9859907e91db97b5315be8bae4edb 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -85 0 0 true 128 1 038e7505d20626165995cab81af14db2848c3185238801a78599e7229d7e6221 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -86 0 0 true 128 1 da18d23bdb40e1bd83a49fcbc824338677e45412646c0499f4f96d8116d1294a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -87 0 0 true 128 1 d05b8d164ceed11ea78cd93ecd2ff4b79bcf0d1f6d9b70b640b82d2b8d8ef03a 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -88 0 0 true 128 1 ed3cf90b95e4421e68cffb3b35e3b52d4067a9b7084979fd6bd4e9fdfb195833 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -89 0 0 true 128 1 ffa244203bbcca255d8e1f8b2335c00bdce22331032ec3c0179ffe9da89e3c36 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -90 0 0 true 256 1 a5c8ec8eb239c8c8f7ee915ff42cf67e4d137c62c6e60141dfbd72850e6ca716 96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb -3840 0 0 true 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -33 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -34 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -35 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -36 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -37 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -38 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -39 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -40 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -41 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -42 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -43 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -44 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -45 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -46 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -47 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -48 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -49 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -50 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -51 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -52 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -53 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -54 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -55 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -56 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -57 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -58 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -59 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -60 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -61 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -62 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -63 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -65 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -66 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -67 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -68 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -69 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -70 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -71 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -72 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -73 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -74 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -75 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -76 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -77 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -78 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -79 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -80 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -81 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -82 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -83 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -84 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -85 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -86 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -87 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -88 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -89 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -90 0 0 false 256 1 624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -91 0 0 true 256 1 0e6a4ee3fc5e7a2003cb96e18d55d2e36710424dac4a92f2e2dec60bd3b19440 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -92 0 0 true 256 1 f3a5279412d0a65f69a3180f5c25132744008d5714e45c9a6db3b448efd1bff8 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -93 0 0 true 256 1 4684ad4f099310999d7099aa479026648b410a616c265ea57cce5be04d9d5a5d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -94 0 0 true 256 1 c1e0e14e2235b956f587c326ecabd6e645fca49ca9d4ccfc3b54c051846b29b4 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -95 0 0 true 256 1 8ba5eece1ed93890b1a5eb2908488d464910bbb6dfdea4ff64afe1026f018429 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -96 0 0 true 256 1 893bd3363de2b52c2f4395e4f55f462d550b8381a3c1a97a633cc4ee5a67d58b aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -97 0 0 true 256 1 4c1f51aecedb5d9d3c041da5cadd58cacd04c72c075c816bbb07c27849b7f761 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -98 0 0 true 256 1 1b9dd7ef0c41e339b900365da773032a1d01ecfbf86b6ef024696ea2e8636048 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -99 0 0 true 256 1 9927fd71ba266b888006436764be66e248389b8478739f89c4517e7d68c73e72 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -100 0 0 true 256 1 ff6d815ea258e27bfe4079f1df0e5dc85c7d4848a05a117b5100e9bae1ca77d2 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -101 0 0 true 256 1 099d0613c49b6e813e0ecbfe5e68724560bfb349f5eee0be8188f5c3e05d9094 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -102 0 0 true 256 1 397fb0004634916f2fe0851a6b4216af7502d421ca45c92178bbd810529bf1c8 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -103 0 0 true 256 1 7a7ec537ba825db12a3981142ec3d4f8f026d85af8569c8ea29f3ed8817bb23a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -104 0 0 true 256 1 3d856afe77c075d20fda66e9769d07022b2e87f0e5287d3ffc8f61bfa4362113 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -105 0 0 true 256 1 e308e38825e9564bb72cfc394073016057b7ef7e0c5d574f39f82bcb6b666acf aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -106 0 0 true 256 1 19d2909aecef708e46b8975fa3a5faaa4273ac945cfc44d9fc6ef67b2ad69b03 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -107 0 0 true 256 1 ecda6d348c58adb60992ef830e83fd1e283c16a09e5578ca255fa11d64d4e6ab aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -108 0 0 true 256 1 0ea2f08f790389bf4d87f6b56f37f3bed71e64e9c69dedb7d0a06a0fdc18a97b aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -109 0 0 true 256 1 c175f80948434a0b37a3527c57ee103b746d0f000382a4cb7310e74b01287876 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -110 0 0 true 256 1 3ccf3c5fa2832b9545e4a7855ce59b60ec29d158cc650497c87a441e42b527ad aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -111 0 0 true 256 1 2ee4fb24802bb16366ba51118415a11033ad7532db7ccd68bbdf172cd1c1a3f3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -112 0 0 true 256 1 5adcea262dff9be57f812d3a617d93974d6f6e829d1f1377a9b055702dbb43a3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -113 0 0 true 256 1 0b09852fc4c1cf81d3a5e37655d623ae115a2f5a4f9e0f0158c038745a23d322 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -114 0 0 true 256 1 cd1bdca638603605e1e4edd4abb532a262b36c09c63a50cdb77042b73749e0d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -115 0 0 true 256 1 fd5cc68e9f6e732355c1686dfc47044604ed5bab4ac1627dfe8d4af5aed6845c aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -116 0 0 true 256 1 696257e05791a737a20d6d7ec2792c5b87e8222661e9a3ade7520a82fd475b78 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -117 0 0 true 256 1 48cbf09c346d1b0f32ee9f045ecbda4f19ba05b67345585d0b58138108bb3c31 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -118 0 0 true 256 1 265fcd80d136a232bee56a98091ab02ec1fe490bbbae55a5fdca6bf565071417 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -119 0 0 true 256 1 f096c4b39abdd517267b23138b2a9e51675b43dcb379a5986024ccdc925672f9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -120 0 0 true 256 1 6438e5a65e31ed24581e55d4cd491dc47e60dc0c177d1b301900410309c903be aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -121 0 0 true 256 1 b7557914763965b1a7baf856ef6df5761f882dfaa3ed96b680b91f298b6bd6be aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -122 0 0 true 256 1 2b6662b2f50b627465531f437676b8e7b622d82356a7bd6239987f31e59efe67 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -123 0 0 true 256 1 85460cca22cba37f07442aa71d04ca6b7c479ed2a6cc459eb900f070d0e1cc42 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -124 0 0 true 256 1 c9a72134eef697282ce8c872cf9a6c104164ccd51d81dd4248cac7266f049169 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -125 0 0 true 256 1 d567e6dfcddf3dfe00958852e0f1ff440d655410a87897b4bb1195c927633f79 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -126 0 0 true 256 1 bf2019864bf008b6dfc7f6d5288b84b16057eaa4350de51ffb4a9fb294cab65f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -127 0 0 true 256 1 4328e641b307b021434c094958541372b3cfa57860295781966af1327c42346d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -129 0 0 true 256 1 d2f17af24526ec97892d1c519bd850a1e2bee6ef0411b91898051e127a355d7f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -130 0 0 true 256 1 bc0d1762db14673d87bc8d4c93fe83d79bc6c737a1f8e5dd01a01236fbf12909 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -131 0 0 true 256 1 61d3d2c70b2a9827a5c326afa74ab1f1f5ff5822705a7744e472d2303ee87ebc aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -132 0 0 true 256 1 4f2540b180916ee40c87f9c918fb47987a4fd28a554cf61300e7b7c13c306f58 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -133 0 0 true 256 1 dda9353ea35cf39bb9f07345247eee41a5ead2b5adb0fb21281afb389f7e1426 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -134 0 0 true 256 1 64f2c490cd93446beb312ea6587515f365c6df9f59186c72be42e3243a744b1c aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -135 0 0 true 256 1 2250b7b97ca5f791d5ca4b4c482b4f4c04e257e11936d4741571c0a690ef06fb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -136 0 0 true 256 1 ead2765d770d911c7b57760a6ff3d08881dfad51be47e56fa055d178868868ff aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -137 0 0 true 256 1 9b3e1fcb10a80f63d0dff6db9d40e51b29435ad2c49cbadf91b57395c84d1acd aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -138 0 0 true 256 1 80e8368549f275e07711435fb0241144889e5bee84b49579d4cad8e2ee9a3e93 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -139 0 0 true 256 1 245b80b65f88cd54b883a55048b784192556627c45f7317004ca8008041de78a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -140 0 0 true 256 1 d27efea039e72cb592256844594d7ff87c809c9c2050f172f3aebb0680c3c482 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -141 0 0 true 256 1 94fd5ddf81a1d84bda2ea772971332910511dd3cf1792dbce3a21c94a3d54f6e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -142 0 0 true 256 1 5d4867a53133e60c913511c356f6301f1efc625b69d9581fbc1293d637c167c5 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -143 0 0 true 256 1 ab1b9047f7d6ba974d897651bae052e78d02ed1be5fbc8eb3326eeac97f40bfb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -144 0 0 true 256 1 1b5a754c8aec53a0531827d5c0af4d4fc7851dee38da4624e8926cd51df59903 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -145 0 0 true 256 1 5891da9146f0e437275701316bece9912d7e5825e4c78bd7f87e11cbc920873e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -146 0 0 true 256 1 2f026211607597bb95411e9479fa9edf727c666afe1879ef2b7c0c2fc323a8d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -147 0 0 true 256 1 5399860da986247586e4c3145f23ca4e1dca5e1aa9e639469dcfc1e1ba5468d9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -148 0 0 true 256 1 74b626d241a25760a59c9348cd793638d416b3e2c36f4473328402770180b484 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -149 0 0 true 256 1 b224be6b0a90307ba2370ed6bd416684dea417585ca82e2e5e18f9e172f1f0bc aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -150 0 0 true 256 1 74e21e7e01b7f4416db5d8cbb9160fde4ca40ed0a3675233391b15d3de98db2a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -151 0 0 true 256 1 197ba5383f67f081652f2d39eaab2fe75ea6195cb987451354c53764ba14daa0 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -152 0 0 true 256 1 282ffd4270c1c3d130bc4d4c14cb2a382f15e95dc39c4877ff45b18f974acb7d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -153 0 0 true 256 1 0e3c752eda114643f5370d321cd26bb79acbc3a0aa56290ee1761dca9b046e65 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -154 0 0 true 256 1 29fb5f3cf6fce8d521899a60087ea62cf917a38600506eb1ee2023c77cf11518 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -155 0 0 true 256 1 a1bea921ae252bacd324d54cca91ae71eac170304a3e0e6b2509d395047b2dce aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -156 0 0 true 256 1 adcbdccc905b4ea49eae5c3e0f2d2c9cb5446f4e29a54bf04b085ac589adc311 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -157 0 0 true 256 1 68d0fd95a0994bd95504d3e8169b7a375c72b0204d0f79529be23b5a2b0d33ac aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -158 0 0 true 256 1 8b5cc8e091ca0ccbe2a84cf298a9827fd43592da46f413cf9db812a94c70e238 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -159 0 0 true 256 1 c2e6ab45d650a0c1baa9759f9df96643e4f3ed5b2ec147d29e2aa1767eaa2a39 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -160 0 0 true 256 1 ea05d214aa6d13343e18baec3cebee6cda7f8b5acf98a8381436d8072dd084e6 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -161 0 0 true 256 1 6e3da0f456c4c6d8aeab0aa9494c695ae76d98c70051177eecf7f5c7e6f5f6ed aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -162 0 0 true 256 1 a5c5930e2347b05b8983feab6de33d49f6210adbabe391d827a88844fc6c986e aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -163 0 0 true 256 1 04f6100f84b2808e0eff54079adec3c82b91b5c9b2f622c2a949c79675147f2a aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -164 0 0 true 256 1 8adbe144d476795d6fa952a70eac2adf2b01b696088a50e9972c9c7766299465 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -165 0 0 true 256 1 3dad537864a673f61bba48ef4bbeebd98daa7c55c1de5b2a52ff07062b33ab81 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -166 0 0 true 256 1 2c30d66e2bf9edca12839cc4324d54780c437e676bb7b6fc36b4d0edafaa48d3 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -167 0 0 true 256 1 10246f2abc45b2d877be7921328395f023ed8eabde0edc3f1913c5244e874e2f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -168 0 0 true 256 1 0f176be4d9d01de2dd31d0f07a35f5880d3de4128ee8e47d26c01da12a5b38a9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -169 0 0 true 256 1 db5f194c063882417eff0507ee0ba0ce439b79638787d2c34d0a40dbf72361d9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -170 0 0 true 256 1 8f8ce4bdd579e04fc09878efe34078290135d4b60a5f735df06a222cdd2fe58d aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -171 0 0 true 256 1 824fe8ec4e23947b0c0d82c275e16e57c0a3bd59eb50011c9f228bb039bc97fb aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -172 0 0 true 256 1 eb266c7accaf765dc5ad36ed5e580ccb0b0957cc7228c36bf250c155c4f8a356 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -173 0 0 true 256 1 068756ea014db3ece578f2e0ca680e86f68a532813a44c9b8f49fc60e5e5c008 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -174 0 0 true 256 1 3cff71d77364b057013d57a0ee56a5b5833302cbd8636530c002585a6e9cd043 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -175 0 0 true 256 1 647e552a9233e84c6ab5a921aaffc6dc41252fc5962673ebd6e2df7f180802a9 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -176 0 0 true 256 1 cae4386e60929dd5d6fc6b65a4817bc7a4634d628d20f5455a2d12cc3f002ec2 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -177 0 0 true 256 1 da5e2c96d595707df915847731a455041edc911424fe134c37d7f35cefdaf314 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -178 0 0 true 256 1 54b041288ef61baa9e060fbace387490fc3852f2c63f25a9772780d3c9213d03 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -179 0 0 true 256 1 c60291a5bce8d065eb3a7fbc74eb72b0825f6c0d7a03201c2c101fea52ed5d85 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -180 0 0 true 256 1 fcc7fdd6d74c6252d0903d013b41e123f2e469346fb6b098947021d8c448e757 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -181 0 0 true 256 1 0def948b618ab0c7dcd64707633bb896bfa25b31a4df5e8d1495bb25defbba72 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -182 0 0 true 256 1 e7ea19ab133930e634de9bb30f9bf030e2b612c1cbc2f9fead992143e88cedc0 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -183 0 0 true 256 1 869a9e09c2d4ae81267e1cab66db3d689814dc353ada065e906165b0c3c5bb69 aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -184 0 0 true 256 1 0f7ab8fdfc4fbea1f04e2bf9a84accbc0d870b9d3ceae002e8b0429f3477a41f aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -185 0 0 true 256 1 6a27d86da48f827487a83e3c7f6d7961591c6971a99739ee1bd53153330713ca aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af -186 0 0 true 512 2 a7fd0bb9a5eba8b7a9186aa1ebb695cd0d3fe7e5f1377de75a891ee869951c52 96034df7569f4241ab9a2cfb404d1c74fa2815aafbe4b40febf72f1a2cf33121 -4096 0 0 true 512 2 e23c259850469e325ebf6505decf0948a2bfedfbb8e28e66970d6e1675984bcc fd1da2b8dde7bb78f48c63f31348720e258ef6d9d7197b6c3323b44375f75e40 -case overflowing-three-dimensional-hashes -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2009112444 -1024669950 1548280006 true 64 0 8fddd50a279f63427e3898a1ac0522bb30021771558eed1e9bfbd18247c29f1f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2091821115 -1737564729 1005828012 true 64 0 9258bf83ea976eb12b8b0340e1269a0db68a368d89686836609a2175aae95b65 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -187793916 -1132608087 284384781 true 64 0 393db7bf3f2748e1c051bd4e89f56cbe1a3ae8bf9610d37dc04238f33c000f10 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1148401140 -1824399378 -496532742 true 64 0 0314d12be9c0fc442688138b9b19b73b0a59307b69ddf90259f59a81933a0f89 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -1643660664 -1177107225 -636364081 true 64 0 95b87cf89b6dae9ea03f2913b914491c9051966fe6fdafb8226b0c7ef4b7abf8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -193402067 -1942909294 1144036287 true 64 0 f0109644179b144bcd744a4b1e9e1836abec926cd137146526fcd15f1309046d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -931923172 -1331968731 -907722591 true 64 0 9af26c7b21fab8d24fe8dedef97b2011c15bc5d86b13a89043c94e05b1c2372b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -302026495 1188548138 -1044240021 true 64 0 352ae0f383bd929c4c6bd4fb447b80fc61b49efe6b446856e4a9291c49c48b19 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -488910413 -1052404978 634556481 true 64 1 9b3097f5b5d68cf3705c31d01b268d952721e29c4656cee0b8b518cee71ab13c eb292e8c50c7d2b2e51e17929372bcf54a056308520d9c5e3a111eba8855b2cb -239249819 1537778272 1711037189 true 64 1 7614ec20b4f198897e0d201b9ce2480a7dfad9ab9774edfd473d2fcae1c675d2 6df274ff57948bfdfbcb812244f7fcc4cc9fe18c75d110ba14fabd01c828f0dd --96289821 -1202460919 -155523898 true 64 1 ec0c61cd2dcc24daf0a3836c9c1604e95c8df6134e432883dbfef89f2c3bbe83 49de073cca5ebc4007db6fbbb8f9b361f76f93c9693a20c425ccd58bbcb7335d --650120678 -868465859 1306902403 true 64 1 8c60fc6a5bb0724f8ca084a877ca41fd8051b76f01897e2014ee441e39536a31 f04c2c3f6ade5f146d3e8efa8a37361bdd32e0d8828c9e079d49923015e02b10 --1899624886 50559235 15988249 true 64 1 75a996b11c09b62b0099d57f55a7c4d09d68f40256d9d96f8c918b0754dbc4c4 11b976b9a2021971a6d4be38e0b8487451d1d482c052afd73decb691aafe55eb --389288696 -1203576701 -1909371045 true 64 1 c70f75a2407b3eeebcdec1d4a2d9732330c50142f6ac0fe40b6568c275862cf4 5bf2675fdea343b501d31ce69e5ecbc7ef88c3bcf14cb21a913e38c6e11ae1db --366916050 40569969 -870199645 true 64 1 21429b7b9d47d35d2ed63300ef72f22ec5d3995c99949356ea4f2d37605f51a5 46cf5233f90e57be26599ef7e313f14ca3a66f61a690bfbc04fb419f845c9169 --1203878688 -395921575 -1608320167 true 64 1 fc0c8e99b42871436d9d0e6daa6ca71e32abcb74c2afc47be9ba1d3d48d62dcc b28662001689a519e3143d4ba39e514a9c17d9cdc90fe21aa20ada3ee9b2a1a7 --1267239570 1150204053 -636901305 true 64 1 38b894868221b7433d462e8102e2fe389cdda5278ebf100ccd4347ba5c3ac40f 0745ea2a5c9e650543ec78ab63a18bef9e4212faab5ffd9bb266150e1832a03e --24107366 -511222227 -2043816781 true 64 1 e43f9f2842ffc55ffac3f082f805bb60945a467fb8ec62d1b56acf45a2d766b9 f31156e75660f87be9a815a48b0dae676f35f9cf9b97766e929b1bdf2e01d500 -1681984333 2116612351 -259971950 true 64 1 040bac3faeaa3b84552bf5d4eb35a08660196e0a65c7767e547bab8a04cab09a 583187a1129041282654e5ad45335292b67711da9195d2a74ebeabc90a12ed25 -961617563 872639189 449061466 true 64 1 b1b53058d20d3446e5f3562775ca9bd80a1f074a708889f6f3356bf612965b25 bfa52e1da75df8295e4ff41b39c80e40e4ef9bdc28ce1b34833c3fc6cff48014 --1964818907 -1914891690 -1659087823 true 64 1 351ca3b82f75cdd12561bb9ff5695cdb8ca8266111e24468781d25166008a1c1 6ffb230eceb2cb733353322b4a0dfbf541e9ca386d4a0bbaafafd38af629ae6f --363487355 1584649975 981661586 true 64 1 46b79011d5a9a6bc6dad2765bbfe5ffe33e6abd4fee5eb973e25015e06ffd834 acdcf0e8e805d1a9ce33fbf58f6cfa743cc2ad87c794bc1a73b2cbec2b63c2fb --1859474891 -1031833768 -1133493853 true 64 1 e5fcf8aa73744a7024193dd41b09b14aeba84b3b3290d5ad59f6bc8f1e2cd76d e2d86564526cc1862b8b91e8020f6b6e851ce2b7101de636e7703359ae8c90a7 -2144777543 596559531 1801985988 true 128 2 1f7ac6d0570e8faceed114720136f2d22c2dfa1ebf80553a94fd3a3df9617a2c e5293472aff59bde4e53c0ebfc8b6888499381dd2db8b5d4c337df3b084af0e4 --1493583440 -1101309410 -1444482258 true 128 2 ac8c7b4db72b3ea3f35d50f67dd5253c1b50b01f46efd8a392fdae7a3b6c9541 ac6043f322977bc08fd11e9afc574252b21d4f56477d9afeee9e9e95c4553c1e -case signed-hash-comparison -1 0 0 true 16 0 32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -2 0 0 true 16 0 f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -3 0 0 true 16 0 1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -4 0 0 true 16 0 e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -5 0 0 true 16 0 301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -6 0 0 true 16 0 7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -7 0 0 true 16 0 b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -8 0 0 true 16 0 eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -9 0 0 true 16 0 0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -10 0 0 true 16 0 4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -11 0 0 true 16 0 c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -12 0 0 true 16 0 0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -13 0 0 true 32 0 85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -14 0 0 true 32 0 6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -15 0 0 true 32 0 cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -16 0 0 true 32 0 6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -17 0 0 true 32 0 f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -18 0 0 true 32 0 e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -19 0 0 true 32 0 cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -20 0 0 true 32 0 86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -21 0 0 true 32 0 30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -22 0 0 true 32 0 e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -23 0 0 true 32 0 49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -24 0 0 true 32 0 8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -25 0 0 true 64 0 0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -0 0 0 true 64 0 e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --2147450880 0 0 true 64 0 9e7d99338fb137098f555e67dfb1fbd488b38468eb22be493dbe6d5b5fec532c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -64 0 0 true 64 0 d7b2d45a6f8b1ce418f4a94c2a5fb9b98fdd4aac4741d9f89dc167e5df7d063a e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --2147450816 0 0 true 64 0 1957748b3f0333a633f003fc37395245a5fb6f79aeb8428a8ee254497541d914 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --65473 0 0 true 64 0 92f43cd2f8389db92fdfb3f2cab8ce75766ec92ed02256dc0f90d15f32279968 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -128 0 0 true 64 0 83cf2e30bb618fe6e4107b3db2e1f7dd614e7fd3634f70d7ec2cf2776ffa264b e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --65409 0 0 true 64 0 f5e8d264f33b2e3b68606f6c7fb0a40b02bf2948b2226eed26a82cd71a93e73c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 --2147450752 0 0 true 64 0 ea945e1f0ca5e3ba362ad4e81097ccaaa0470c3371f1e893a017e4342bbca9df e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -192 0 0 true 64 1 00abc47ace9e53ba1cd9d9863b8fd772b1703186765b56fa9c884cb419db8959 e633ea12ff3a67be1378ebc0da2ef85ca2c8b4bc8ae7b2909b8c3804cd24ecdd --65345 0 0 true 64 1 579350650332cce744176247a448e8c80452f59732a7cbcb0b5c607c3dffc921 717656903d2345cbfceeb394711d4e8292d1a3127bab1d804b8867fe445610f0 -2147450943 0 0 true 64 1 83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832 fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b --2147450880 0 0 false 64 1 83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832 fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b diff --git a/steel-core/test_assets/java_block_pos_sets.json b/steel-core/test_assets/java_block_pos_sets.json new file mode 100644 index 00000000000..5897546847f --- /dev/null +++ b/steel-core/test_assets/java_block_pos_sets.json @@ -0,0 +1,10959 @@ +{ + "minecraft_version": "26.3", + "java_feature_version": 25, + "java_runtime_version": "25.0.4.1+1", + "identity_hash_code_mode": "sequential", + "cases": [ + { + "name": "control-initial-sixteen", + "insertions": [ + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": false, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ] + }, + { + "name": "control-resize-without-trees", + "insertions": [ + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b32278b71f9cf0fb13ebc862504465ddfea39cff6ec5fe0387defa93c29887f0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e7c2d1b2db5403bedce3a17f93bda1b353d6b223d0880968f5eb925d1cda40ff", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "fe2d5879313dd8df4f48c044d10352b362a2b1465b95acb58f89ce1fc8d4dc9c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "272786546899dec0fc8550b167c3f5ae284430a20a80dc5d699fd68bec0c01cf", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "2139db916c4bacc9aebec1be8dedc45fe1636cfb46044aa16a7f653404eb8627", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f6100d0c579ccf33ac1fe652e47d3b65d56e895830e62968ac3a7ed563966e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "8bc54f8c7c2f156abb29298b393b998f617856bc135fa24f70a8ede99a02970e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1b99372eec8bfa0b6f3d3d25bc032322e81d3253a8815f9920fd883c05ecbc9d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7d6e08867be12f61ccb63a6755de0b0f8f128d1f90277fbfa8ded3129cda34d6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0b00954d8963c227fd742ce54902492adc24509096357a2c5f56f16a34b84975", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "31185cd477fb1af3878da7f1a60f11baa184ec9b7e27773558ae3f17df7ec611", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "bd040b4c699899cc871720d10ff6db9a3473c3a00843ef2a47650c18e8b5ce60", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e8fd3997b27b020552b8ffc75253014ecb6d32c75e3c2b7a5e3a7077169d31b2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e614c27b7c3f68966686beddaec8c9d0e6a736782f9815188d772810ffa5cfe7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "456da144547ca79d2d0f452a3b5d5d0de0717c3f7ea912439a55ec5283415b47", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e9a3c80dcdc7aecf6f6e464e680caf8442928ff16a2df19a376ebfe2e814824f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "dda2124909b295d83849102f952fb56f3d5ae665feb68c5f13701956dc50b543", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "05bc57e705e3083c3bbb0542020d54766e80a6936947e91bb237399bdc57a249", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "ce507f4bd955c21e907c5ab512821ba76a5333a33301ddce8a54f9d68e6065c3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "61395173eb8ba1c3af19e055472188ffe3cdd8f12104fb6676d338da1116b1ab", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "920d45a8f6fe792d01dba2210d9dcda47ce18b954d797581f7a304925d2c241e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "39f97f2383870373d5cdcb4e2840e9a0d6340bff18155556e9ad5784f9321f26", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "798dc0d94f1d34fbf73e61d8b09f13e88ac60584ddfef416f6cc0a5054ca7de1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "b428c9d223ead8e831dd55743387d221e5dd15fb794af5fb0c0cdb1eaff6febc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 26, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "08dec168fa3ec57ac20bba12f7159978cf61de9faea52788cdf8da8f9d1d14c6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 27, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f71328d5f28cdca59ad849d78a7eeb96a1b8b89eae244866170a81ab9d9a5fc9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 28, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f74d25c360000cacfa1c1001299c45ad36bd88186d727adff1dcd662bdc16934", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 29, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "8622356616071041f8c5afde5c6d1905ae0cfdf8a9c083eeb3d6cb1f0294eca3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 30, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "30b48c5be69741a7f01f49a47af0225b82b1aee003bcbcc9a455dd4976cb179b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 31, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "55e060353390792958f43a710622ca91ba9c55c7c9f1bbb2421db3bb4506ae1e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 32, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "634185dc55a65cdc9e2ce81f552963760813490f09d480c4f8b9c52ac05cc5fd", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "29b324e71f63025a0aefc7faca6bb67a8c0bf74059e11240f0ba93604608ce08", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "20fc00b062269e4d829083458a351d2b2a7fac66b50e8860de26bd08e43d3c9a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "4882fca6660f8723ff9660129e9b9116c0b0c947adb18d2aae739e2002cb3641", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "b817dd1b94144abbe80eab5a45836ae1083205235b68a0863da3908d9d9b1873", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "bad150910d3afd1ec912a23c44385132d2303453fb029c3abbe84cdaa75c7a56", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "92ff6b629797572226ac15a37f8e8ecd1a941efbd5fe4849d73ad45f566cea5c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "09901528278808d533e2ae273af84c89b2c6d5c3992308fd62acf1eb1d145c12", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "39e27cccf7838fe719334a10b3fc5341fba46a7e9f2d973f88806540fcef7cba", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "20788ef1868b6713a20d53b6e25b547b4dbc18296ea03df1f4d2cf4fd55ac29e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a1ec77e660556e004b53d06b113717214f66d8b76856eb9813b3b2c4ec4dc8f6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "4e1b07aba6a9e734991a9d4105cb99e08bc9e6c87279ac1090430d4fd58ec04d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e8c411018c27499d45a145ce6b173494f72c1fadd40cfe0fc353148256bf99cd", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "ad87f8e1a54a9e1405a9d9254124b7bf04723c9b92709d480e3d86e207e2cd1f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "513ecbdb7767f25f1f0ff18efc9cf0ed8305564d0be57ef8b8d394a35d4312ec", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e3e2f84b65499960efc2a4ae5fe314acd42cf2c9035ea6ebdcdf15b494a5ff20", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 48, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "e1a3fa4d7da04b00751ce2ebf940a4f97f47cde2cb00550a0698bad63dbc5ca5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 49, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "241d95c35b8f1597b9024ca4414fce30ccfa52a2a0c91eb66ccb5f1ab2cc7c17", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 50, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "739bbe489385ee2319b7fe133afde50a1c624d26c910aec2908ffc7b516bd17a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 51, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "baed0246834058891cfcd43100a4bd2fb563179f828cb4d2906c5cdee7f5fc80", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 52, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "17d3c5e449d13c652e85f5db23adfd32dbc9d2a7f51f039383b5910ad24ea1b6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 53, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "c508dd36bf642807d70eaf8737273ee27d74f80f5af3634c90af88e2a83146f7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 54, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "5b7d012494eb8d3f4834039c9bfa0973519cd732a62ee6f075884bad439f3c06", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 55, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "9531b3ffab274a103b63a76a681a74b77be854a3778e7d5845dc4bd71819d37e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 56, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "235f2987e6a26e48dc5cc15893cca6f0bf9d5d64df7511885ec56f7a21a00036", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 57, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "0bc5e3dafa121abf81047ed6763f3129345adb1e73d55e77b204e2d8aced8caf", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 58, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "604117d2ce40121669a191c7997f24db09443046d775333bd8e057f12f508611", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 59, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "4db2023164e1b306db0eb21902211461b1fede3880bbb861c9ec0bd0f33439cb", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 60, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "eecf48926325970f68ce3a4df2bd8822370fd58f86bd8e2b0c2cc9f2e3f4441c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 61, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "63b132c71f0b41db8e688770d39bc576af5f6a5285a08454247c160fd5b38ba3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 62, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "b71e96c6f415261133b2d1d09fb919cfe63855c32cff69bcd2d18603c34531bc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 63, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "2f000e4f88896c94219c4588ce6c31b6a0ac7bf2d3a1f3a10bcf950a75b47846", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "c6e3b4a5ea6f274c0fe46e3141558c67adc2a3fb5c3bec3f7f422aef3d9e9b36", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 65, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "3d335a80226152e8c234acd6267059fda97f351ff033f2e58a842872d5868c13", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 66, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "07a3b99f7d8e337e9c9562e4010f87bda53ef618dbddc1d3090df2e1e9d488b8", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 67, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "282b76144b6a8359d248e2147e9b32b28ddb3ab1fb2c69c066870fc4ed72173f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 68, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "f2f7211d8122e766c086ca2262fdbe6522d46687e7e7053d696ce61e80b9eacc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 69, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "b03d7171a648580a66f5d0b6166179aa504fcd9726a1da1231bdb084a948efac", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 70, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "bc870029844cdf8f4e14aac43b43f514beedeb007925887663ffd109a9a37dc6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 71, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "cfe90a75af10d3a2b92db2cfe066834063c4b75cb2626755856dcf99d0273ff0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 72, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "d19f8ed1973f3faa5f2c5c4c0aca0f6b786b758b2decfd320e99ce630d189ff0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 73, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "03b32c3dc357dbe05d06d858e7e3246cffd8455b38fb876b2d421a3d63db4845", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 74, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "2ae522d7b8fc3c9fcf8bd792b92a6860d23c430c4ad8d6dc73adca392062f4f5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 75, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "7c4082746cd62e0fcebb1f5a8104ac2db71d21ee3bf97e69be29df28426b0c54", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 76, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "ce8e41d72a42178b79a717627c6ed733e656015fa29954840565784ac58b4593", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 77, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "ae5cbf8b0b9b6a77c47a1ac0e2325c1fe326dd62af5632c7220238f34bce2919", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 78, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "38cd3cad6abf6c2da81a00bcd8d1ca34f23ca39726835553f366b3727dbac255", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 79, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "653848f003a2c2760585b8828b4958f6541aca7b61950ca13628d56464bf841d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 80, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "d49d77865b84c3a7cdf783f5a84b7be5ceb30cb8c45a6f71682cf3b984548ab2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 81, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "8700cd3d5c90ae04ec8ba338e1c66e1094588a416ccc7561ca6f717650ea3e3b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 82, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "c3dfafd3708e5242c6004585e5ddf55492c99e3caa32565a1faa450123e1dfad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 83, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "8d27190f8054c80b12304093acad95a123a8d3e227fb254d3f599a7b772a4483", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 84, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "75967a043d2511ceb414346b3e2c5885c875eb97359774e320800f82aaabc431", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 85, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "1491a4e3171186aaa83e999bd234f6ffaf0b9fa8db971032bb324c950edf0d5b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 86, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "fd3860fbf2180351752501c55a676fd5d65889a3f3f2b0e6ac811de4c616477b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 87, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "f74d27c2225be37a38e3bb76ba968a8947c5da8b1d1f4cc632edecc88358dd45", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 88, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "4e1f6526ad508fd29133bd6b213caaa655eccb95a71e30df37af901501a7f118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 89, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "86dbe26695073d54a83dcccb4109904826a96e00e15f7c67606fdc9be6a33c0b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 90, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "e1fcfe54589354cce2553be418a61379cdce338c7497bc0e02fccc2091c85bad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 91, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "6b214a974c04569e9ea89d056b1b281a03bc7cdbd6ccc6e91fa3b1eda791ad46", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 92, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "fd3e63f5a2416029c93e6b81a73e086a0e144fd167a72959a1bffbe9a15a14ae", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 93, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "5763138049348184e0e2c757a43cf516de46153a1be6934ad9ca41fac42a2d08", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 94, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "9647f46a2f9a9c473fb2006a1586af3ccc8606d7f1c4254fd29c37c96ed69b09", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 95, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "90db01392bdfd1eb23992f04686fe3fa9da451d17ed09fd476d76e8baebd1990", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 96, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "973e269d23020bace974571580315565a5a81abe32bb575b0e58dcf0693b4a89", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 97, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "243263b7abc6d8b410e59b09ed52866e539afe8a11d330cffc720d192f849f6e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 98, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "b6b6309d0d53df6e2e3453e14f290ff6f94d75dedbe1fc59c5e085e3793b6444", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 99, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "e67528897a8de849b61f7a2e18a48582e0c7175ca50c333f7982adc36f4a94a1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 100, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "8760f83e86bb18235e32053c069ab88084bb643139bfc2ad230346618164b239", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 101, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "fc0c9fb527ede41e3f8e0ad6b3e99d4599d330653f172e440c96182412632460", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 102, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "6f72a6b227e3326d1a75c9809879df0df3c4427545e69c8a4d1ec78d78946faa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 103, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "178a387c6d2a59ec501e4bd80212a5a1215afe78ec450631b47c21da228786ef", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 104, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "96dd19472f1465731ec3667b323269e6c71d66c26bbfa4c35208ea57d8b176ee", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 105, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "9e2e3103b64f55e4ae990bec98fdefe7d709f8fd818ffd38717d5c0435ab6825", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 106, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "af55410aea8d74ec0eaebcd8b151687ce65451d987bfb68f964d83fad52d60f6", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 107, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "b48fbe3dd6b6cebe136e59edef4e66fc277edca38bc70fa71b3e12b0ec508196", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 108, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "b548022db689363aa78379d652ea691e94e58c34961f4bdd2aa3c9c506883bb1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 109, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "c03e6719a25498430226cb264f084e2bc04583b9a8015f33a1e202754e1150f7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 110, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "1da7ea240c05ee4619dde9497fc4ae26fc664ba55d776b38d1547d9def199167", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 111, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "752375aace4815277db268064d07730a6d260d24d19a03bfdea2f87c1ba28b04", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 112, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "156bb9688142e7ce8d3a2871a329ae5e49653235b519d91ebe36262f4bedc2f9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 113, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "e2c467c4a663d0f6feb6fcf5a31fb6fbffcf54ba515135585398dc9f33a59d88", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 114, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "060b007f65cca7b15678d4ec780fcef696e07b6898cb1d0d1d705f6378298a84", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 115, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "9c23608a13c11cfad5119f30c45903a0024445fea15f3699bab2305b572c695a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 116, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "304810dd4aa63d6bc21a83b762481cefaed49a7d26f53b0f8b0eec317b38c9cb", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 117, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "c0bc7beae38699a230200b45d4de8f8a62606d92c97dff6f677c1c71bdc81823", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 118, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "6e127416ba68530fa1488758e66c6480a080d87284537e1ac3769d13dda730f9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 119, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "95bc47f6b9fa225965cc572dd394f707d4bdb3fa047b4a87a7e429723a2616d7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 120, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "bfa96a0805705a10e7c2252d42d79b7f00a70e7064a2200bcb14ecea88c6bfec", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 121, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "53128be53877cc0951bdf564f12878479eaa29d394e2746e3890f3c57e53e3bd", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 122, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "32c500e507f4fcea1bd712f53ccaafaf643041e52c7662c0b10aac19304fed15", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 123, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "d8e0afdb2b1e2d4b098919875e5067de17579855795f493b3615bcc60a246f0c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 124, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "ac3d3a7277c728ea6c160adb6b031b9617a7e0bda2713d36402e8dd342dde3cd", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 125, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "857f7644fb1a082214fbc7ab1b85969c34ad8259e39b3147a68326efbdfa7bc9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 126, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "fd5ee8335ed8f13c52b49c655c720655150ff1e8893a5ebf7829c27dfd854d9a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 127, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "f8350e97d9a0a5382bfb52804a3b301276d0e16123b246eeb4a368fc72fe0860", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "ada636bb30d32d830579f51f599209312fa776402f03a9606d2d40d496b47dae", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 129, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "953e7c5fe433b7bae8340fae9bdc17fd2898774ab2b19db0e22b8657fd5c13a1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ] + }, + { + "name": "collision-resizes-before-treeification", + "insertions": [ + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "15b8e296e318d4f07cf46716611157ecbe102168ce40f12eb058c1bc8cc52af2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "16882ce8b10032f0603149e29b8bd8462526eb9b3f39c99e37f1333bb4760249", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "ce2c86195416b6060c1cd874f8a0962f61a0b668cefb7d6312616cf39a637b36", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 256, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "78641378f6554873fb864e4194ecdff433bca53f22f635149cca0e9ed4479dba", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 320, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "05c2f50ece14c333bb6163ab3b2b0ff43980714bdda65ff6f4f889610be5b807", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 384, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c34af995a927ca99217a14c182d2baa27fd75546825061fb940c7a7bc8357420", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 448, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b3a7ffb37c93d5fdda74a0bf76eaf23333ab22a40df9026739c188931ee7d1eb", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 512, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e08198bfb8e435d23b22d5a64bdbc76f96adb84e78e2ce0d7908f6b1725f0010", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 576, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "98a698a0285bfc6c9a77daf1804ec4d6c5776fa6db65f22c371f6d1321d384d5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 640, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "d79759b6efc7cca5abd3d8b93fd7dcff096ae7cd2d031b10d0c581b7caf000ee", + "tree_shape_sha256": "0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa" + }, + { + "position": [ + 704, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "210c2d82d8f23dfc6e324c7ffe628d7ff639c1cdba5781d02df105b787c25907", + "tree_shape_sha256": "33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e" + }, + { + "position": [ + 768, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "680b2385ed8075123be0949fff2d9a64ee2a83f429c97ff41d9cbced4b00561c", + "tree_shape_sha256": "103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c" + }, + { + "position": [ + 832, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5d7e02e447fbde77c3091a3d7198f49b664fe9633863d883bc5c8333d2aee470", + "tree_shape_sha256": "19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816" + }, + { + "position": [ + 896, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7", + "tree_shape_sha256": "d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8ddec0a17f1d8943bb00bcc18dd9fe4d8b8623b2dcda848ff0c68c2e6fd8bbb7", + "tree_shape_sha256": "d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2" + } + ] + }, + { + "name": "split-to-lists-and-treeify-again", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 256, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 320, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 384, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 448, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 512, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "74fa7598312e57412585f54360b45ca74a053a3371424dbaa5a674e337fe9ad3", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "337b8e3ac9094219ff1424939e3cfe69005ce4d38c7507b9e0a2ca33f6cde97e", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "261518dc617c1bce90ee63f07f7bab37b5b56e2221edb4d754ee83fe20749448", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "9fc8912560ebd5d2af71a7e1896ba340acf1fbc767f937f723cf35a6292fd7b1", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "06c22a7f6ac63d7d0277e56d88e4fbda21daeb73f376503dfe842c3fbbfbf3de", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "cfb15c9b2407fa96fd86d628331d26813df93e1ba671c47adc4b3475ec9eba00", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8bca0a05bb458e402384f6175068addb9e802546929a8355e674456face0dd22", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7f33eb30aaff727789a679b7db28280861b53ebc2e6efadadd34d815d878d791", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "c88eca1372e2dda34ddf5e9c55f2cdb30546d034632fa437473bfd16532e5c00", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "005f07817a86ab001b8535904ab805f6aecac9aed4372d5995c80d9e3567453c", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "df490232b0b5cec29aa17820236ae5dfaa07309b013b1414fa7a9f535fb3e9aa", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "2d8eac5fefe6230281d4df059e95e82033115386bcc9a5927a52ffb76b8971ea", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "20e13a2bc6eb489d32421a142f21b84258ae56eec4ba603f787e3d5d702aecee", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "cf74506bfc094a888feee4d6f771dd0f0161966ce450e0f36e57ec2150b1ab96", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "86e4e51ab3794cc9f5373645cfd3ef563b6408c05835aab1fc36577cb7e3dd17", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 576, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "4c841a696aa02076087987f2bae9c03155ee8e4b121e1cb1b94c579e1bb45741", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 640, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "383f67a53d42dde1712fd6afaa8a295c654a16ab289359b4914ae9ec0e93b70c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 704, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "f09a85cdc693ba9327699fe40502c073c252eda3cdba8e65cbac29bd780a51c7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 768, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "d715149336c4b95abbdb0ccbdd89d2bf6a7fa2a4301ceb0b0e073332a2b687aa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 832, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "ed0572bf2caaec4063823accb4ceec8877e4c202ac0a261d14246cd88df681d0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 896, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "5a60e660ce5fb979be0030dee87182a6ec6d95cf5ded5ba572ec679cfdc297df", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 960, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 0, + "order_sha256": "309a76c5f12235c69b0beac9c9ff154c8d61cfa7accdc5fc3b03744e4437c8c2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1024, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "a3d2858b7913d65d466ae360eed2c15ca9df2da07d5daa493a341e00164b47fd", + "tree_shape_sha256": "4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf" + }, + { + "position": [ + 1088, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 1152, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "c0e6a3558abde3b100ce9072bc9ab6dfc0d11556303b22091a0980554266b65c", + "tree_shape_sha256": "f465512a719c69f51b0cc0168b19fa3fdac821f073298e0803e20da82b3bdbbb" + }, + { + "position": [ + 1216, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "4bc3faa5dd64eefc41d86431c22a2851a773c6ebd51ba4d8814965e2fa17a9d9", + "tree_shape_sha256": "635ce30db786756077e2f13fd7e8f44b887ffdb4117bf2243cf3fd1a67627cd8" + }, + { + "position": [ + 1280, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e", + "tree_shape_sha256": "81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf" + }, + { + "position": [ + 640, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "87649732c84d3415cc19cb9cb5a8b54bf86939a8d31e1084593677dedf37125e", + "tree_shape_sha256": "81e88a1fe44b3f0747e8e8d7bccd1dfb58d00fa5a44496aa13564f142ff2aaaf" + } + ] + }, + { + "name": "split-tree-and-list", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "b78199ef26bb1ca970f06edf1868b0453038474c0189dd304bd19fb8d7e3a754", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 256, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "65cda444c639049cc359d59bca2ee1291271ba98772fb4efbe1803847f7d76cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 384, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f38d2f439f143859b95f909ec22b042b5744f19b85f04b60f23a58b4621867a2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 512, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0ff6f0c7ab6c88cdda2238366c6d113609156cf1a596c11fd22aec4a395363be", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 640, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "2f69abc26249c64e02c9bf93f9cf9780020c652761a6d93b898c87dca8432ac2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 768, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "ce430551672bc0736fa61699e30b1e4251a52c0edac72ed0894f5fe45934d03f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 896, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "4a8b68223bb91e4d7ea1e8ead25729b8d8b15585b947aaf8fa65762cd6494b06", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1024, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "52198185bd42421b5f42472e09cf81dd752429ca68128f8d77577672d8cdfbf2", + "tree_shape_sha256": "4e5e3f5ba0dbdaddbcb8bb7e5fea586249011e23cbe07fdbf0d35a070aa4ffaf" + }, + { + "position": [ + 1152, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "29269f28279478f49442ddeb316511669ea0b05ba2afd64748504e17d8915eb0", + "tree_shape_sha256": "9d11d8c45174163e45cd775852192b5f3c601d4cc3e00b5c4062d3cd9ffb1151" + }, + { + "position": [ + 1280, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "2058098fb8a951f6331a42b03fb1f4fffd935d51faf16524308334bdcd5bb9b7", + "tree_shape_sha256": "4ae62b44b3f7d02d9a036edbbed4351bdf1e88471cacf4c3649e17fae9be621f" + }, + { + "position": [ + 1408, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "a93b02668b935b7e1036ed720e59038fca6b18fb51af6ced1972d4fd956d7f93", + "tree_shape_sha256": "44bf06d402ac3bf46208d23e997eac9d85a2a74695adc4a45820e29e6e524d07" + }, + { + "position": [ + 1536, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "226a007cbc1be62b0da3ce1d43179cc487c23e5b576e0b933ae5afc74408256f", + "tree_shape_sha256": "f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "2ddb2670968f5e0ba92fb4d456359da168b80e1f79527cf1086de8f0a92756fc", + "tree_shape_sha256": "bba9b12b91ec2d8cbf357b01939459a5e81d7ee0d52f9df2be33d60fa5879b7b" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "4d8fcf95bba4233a8357c4e8889eedf311e229cbc93d72479982a45ea7d22391", + "tree_shape_sha256": "d48080bbb1d91cf68559ab959da3a42256b6871f2073e7a16632d484ac95bd6b" + }, + { + "position": [ + 320, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "ce3ad225e4b7cd750ed69cc7b26b46d7b43e75eab2421eb467972487eb239c78", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "859a413b7e585ee358afa8bc17fd673df50b2293b36ceb8620d64cddcb683676", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "ed90392a38baed8d084b6052b00442b4745459c2a6db575e311e8eaab0cfdbe7", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "99ebebb66472b46e141ee4c4530b473bb17c176c689caa155dbc63c3c870bb7b", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "0f967074d74586d94e777fba69c7738144a292257927c7e349542284e3414e96", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "9037845ee6c7370506b982d54c2f3f844929450099b7317ff600df834d51dc10", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6c0d3b26a0d013f0620b8ff2fcaa73793643fb3b8938155230e649e2a7558bef", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e3e535af2e3f5c5674d36e9649293d2e23017787146d5dd1d1b12e749cdb5619", + "tree_shape_sha256": "ebbac2e43daacc2ef7d7dc65c155fcd80a2ceb543e7ee8c4672f08bbdba60962" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "d05a3191e19d867d8cabf405353ca2cd40724ad4e939164ea36492bf05254aea", + "tree_shape_sha256": "f3646758c4374392743acc2879bfb76152a1d63516d15911088b19e89867e92b" + }, + { + "position": [ + 1664, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2", + "tree_shape_sha256": "5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "7aa232273b47857438c2f52e701a99ae491ea728b5c9c74d5524af5245e866d2", + "tree_shape_sha256": "5bf2efb3d55e2c9dce48bad4ef720eaccd3624ef17d01b7923fa065c02269799" + } + ] + }, + { + "name": "split-two-trees-then-untreeify", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "9b97a43951807f8e24f05341209184097fe784c40f59f63f3d88a2c049dc0c79", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "c9163936e003815c470e5d1e577e0bb25e41b2067fcdbf072c6a00f32dc13d15", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f4cfaea6de6a741a58353cc1ff858012531a4d1149b6ff7ee3397f58dc38d706", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 256, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "dcea6c1544f9bbaddd5591da005de0cc32e5efccc20437c80bceed2e6edefa20", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 320, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a4a5be34e1f0c8cc496bb2378bd90e790826fd143f1c75ad35a186d6caf869b9", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 384, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "3bcc9b939fc32027bf7161a8764aad56c9d50f385ee0f19ccd3e4a2bfea766ff", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 448, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "99a3581044037c296aad2df5b84a8604fdba03bf8be7809adc6ade277b775e13", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 512, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "10258c3ddb24a4c31dbb0348b185bc3821b586d54138be6c8339e7cb5ac5ad2b", + "tree_shape_sha256": "fafef81bd6f5bcb8ce3dc2a8a4d80646cfd024e4521a396a67f6868c348124c9" + }, + { + "position": [ + 576, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "b01ee63d0ca97c7ff5fc33f25054ce5bbc0f3ae5fd96d16a35a852f55dc1ecbf", + "tree_shape_sha256": "531a39d6203888b7cf2233cfc01fa5c27000c558083f7e191d0e21308cb75a28" + }, + { + "position": [ + 640, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "d31317af96cbe10e265788dfff1235854fa02d489be1e276904684e0724a31b3", + "tree_shape_sha256": "0f4ea4b5b4b10f83c152a641f9dcff0ef7e488657a3602e1442e27292f0754aa" + }, + { + "position": [ + 704, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "3f06308ddd03feb8b194ab029a8d3c268a6ff5380a2b48f667edc3ccc415b8c2", + "tree_shape_sha256": "33350b3996a39b7aaa0f82f655c6a3ddf0d5cc9a53d4d8d0f12788e873c48e9e" + }, + { + "position": [ + 768, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6e3db8553740d08187c9f968350f06db1944c44255eeb185aa2cb59310c36bcc", + "tree_shape_sha256": "103205234a7276aa198d89efcc73110e8e35c5d6c5a5abb4e90571f6c51eb33c" + }, + { + "position": [ + 832, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8a70c584875601d81e01d80db8e9a082bd24db9dcce3643e0ec2a7a68deb5711", + "tree_shape_sha256": "19b0f8bce70829868b1733fee0dd1da753933858073ce56b074947b615280816" + }, + { + "position": [ + 896, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "eb6bfff5a1121e1b2593933b788bb2c6483da0db030729523e405f4089714740", + "tree_shape_sha256": "d325f932ae765a0ef996b0107094891a1c22f238a8954ce965df385b858247d2" + }, + { + "position": [ + 960, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "4d4c7c82dbd48903ca8209bb3525470e1fdda1be4ec2da4d5d2b4571569596f2", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "61eff08a3baa241f13634e9406aa5da53488ddd0ab15ec5d17854f91cb929ffd", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "4599dc62b328bd6f29a70cb79ecb99413bf63b4a198836e64c7c0f4317bddb92", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "2276239a003d02c9c01c481170cd4edb890ce20f5a5e68fc776061cb0478dd50", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7108f5a2322417122f7886202203533ef612e7ec7126196dd7b3baea473a9ad5", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "046dc2e6a810d1b60c61771dc28f2e689545c336492f478eb47b504c2526a456", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "70fe5fe50418532d35f6c936d41b310d566708a0c6af122996e990c36c7a9e1b", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "39e2c133c42b885d3973c311d0fd2ae80a6557aafde2e34ef22cb58b21bb761a", + "tree_shape_sha256": "b113b06cff3a069ce1d2dbb5939666318c656716315397772f799aec3bf8ff10" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a888f38ad0588622f4ed4f46e54f2546c79fb15017a15322e422de99fb7060dc", + "tree_shape_sha256": "bed83c0b1895a8b232cd3048a42d2a3baeacbe447d1163c29f27a793bc0add6c" + }, + { + "position": [ + 1024, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "cdc50dfb2dc31318f379eb0c0b0afa74256f8e994d08440cc9087cd9d9fea924", + "tree_shape_sha256": "740624f785d60814ad1b10d053b6fb7a3592c2f4fe18b4212b60bfd51c980083" + }, + { + "position": [ + 1088, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33781a1db02aad1d29b4c60c3d2e8b1a267ce20dc25c612a88149951ceedc48b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "3ccc01cd8cfdc61feb802550718e0f787c06757f50dd3ab1fca746b621229b96", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "2e0e9648fb1191ee4592bd70092117e6e96207b6c3641bfc33d0a0317c348d7b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "3b91c16ee7900ea3b5939439afd261470c3b37fe4576615349b48cc5083e2e9b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "bd93d34e20882961a53e95a7d150021d9253e5206f366350fa6436f3dbbf9e89", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "2b9533a1d216ecd266502a7ee464d8d3b4e8a634ed17766ad916d65f5e850d75", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "017b31c64164e6b89d3495ab25e2cf93137b1ca69f5ac00234eae7a24c224a40", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "db20f119414c23bb98f9c3fa95eeac2e003206133302bd8f2a69a96e8d2bb271", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 48, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0d466999062222d7479af2d7a1de605b199c98b0e5e131b042a3c59e226abdcf", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 49, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "02f6a23605a0437ab0d1002609e5749ee373e90c1c33f3998df2402dba6057a1", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 50, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "ef8af77355ac51a6ea98bc146e8ddd265139b9fd2b9f3e64e788785164fe2ca8", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 51, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "3361a7a7b92eb51bc4a8bbad523c8cd9431f0bae52b4f53214b4ebbd457883a6", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 52, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "c53ae02483e6cb5e6d4280dd18019a86bc7983431bbbec13d4b782c4b87b500e", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 53, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "e8b94e181889ccd9bdb6df9fb992e2e2ac5efa429020d8b53d778665090ffffc", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 54, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "6455ece4299525a8b03c6fd853afdbeb505840c4911025a08d3fc2e1ba85304b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 55, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "bdf1ddd938f479cea520582174d02d6b91d852a8b9ce3451a8e2446642f0fded", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 56, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "9c4f30c20cf9b4ec359941df1e5fafad5299fadf5efc9867e59db746d5e9e923", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 57, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "fc487be5b93639d516233e31321d930d9c1c71491a7f2adc7cc35d7e83074d38", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 58, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "6108479245c62c06aacecbc33f0907de16c86ba8e8147308aba22f915186a76c", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 59, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "d0da7a405abe930d11afba76ca5e6ccd49d8c53c418a4aaae372b4aa076bd0eb", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 60, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "80619a5df249a65b309f15f60748ad3c9eccbd5298faa0739fb3c3c0e9e969c7", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 61, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "234566a1f45eec8b55f23a3d6d5024f28a42c71316218968d65f3c825b881dbb", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 62, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "40e2b139cc37e572578e992102ba110f29c193a7b8dcc4a824858530c0b1c566", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 63, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "f1198b28030d058975c4ea8ddfde0d9ed397c0627e7739667512dd1f30b7136b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 65, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "844ed0cca40294719622f7a5244e9eaa2119526d5525ba13ae3b3a9e9bf602e3", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 66, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "737586e22d8404af99c9c863cef2b3ca281da7c92f52c9433ad100ffcb0258a4", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 67, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "ec35df7827b3da2d29c8bf31805c7aff1c954e96a9b8a2f97c399dcd0f77a75d", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 68, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "be0b1d5e39585b54703154e3b6d51967cbb4d50aee663267814f691b5c33a2c7", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 69, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a1533222b0b5385d9665b41036d48d7270efd6c4780e37adbb9fe1f73459fc8a", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 70, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0cf65aa96a45774b0180079805fbba112a366531cf113000514a4510f6d25559", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 71, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "cdb78ee58b1f1bad0c5be4baaab0ec79471c99bd07466752ca8d5e018558f6f5", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 72, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "078a594a77f0d723933f0241fc239192a0a11dc1101b75545a74dc65d9c15bb6", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 73, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "046d4555fa313408d626a7b11cb3f25f8accc65508e8dc7477f16a9b65046af0", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 74, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "ca6ab9ad4f63add965e0d0e0e40ff4d8223d01c0b82082342668d6fa94e04156", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 75, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a8cab7f7238e1247180ae1e087c8b5cedfd72d602ded67b81080c6de3ddf91a8", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 76, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a98c9cdbe06f49e10afbd317b80cdb1edcd7fc929e77e115f2e5223c0419545c", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 77, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "8780d4356c825eb702ad3bfb8c3d0de9da3f28d200910c2a081957f08218a6b0", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 78, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "83e3a86c2d7b4c08b62bbd22473e7052e8e13e20257b3875e4b10198a70922f8", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 79, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "1c4e24a41cb5d433503faf3636aafed824727cb4994a1fb78d451c5e3cafa236", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 80, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "33a5b55dd13a08d785e2d263381abca5ca387daeabea6933ab92fbce5d48f51b", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 81, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "6e0036dcb731edee32cc9bf35f2c4a64f3e28539dd28ef6c8c1eb4df6e8c4f77", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 82, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "b092156fefefdbe327098323d224454ec04c3ff21d98ea8e768825df19466aff", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 83, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "e26fe3f36f09b2d1114dd217dea7a59669c6f29ab1aa19d9d3399c236261260c", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 84, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "37ac1020f6f194e18a2927533db22e8211a08823dc209576ba5cf14214066fa3", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 85, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0c6bb724d200edbc95d472b9476ae9728d5616a654083c959158b24ed4f3705a", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 86, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0269da117e5f662d0b995302da84ab315c447ca8d9871c42fa34705d846f09b2", + "tree_shape_sha256": "5f21508ad0dfac9f78d82a4ec990b7ef65ce881d0ae9acd37af67f11a16e7a87" + }, + { + "position": [ + 87, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "8c0d641bad96e785ed5351338f6671479a5d92abbe0608b5726b8f3a21972c88", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1152, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 0, + "order_sha256": "bf8f9f14aa073901ccf2866514f64ac72d6841463cbbffba9ca5997cc6ff12eb", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ] + }, + { + "name": "unsplit-tree-retains-shape", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 256, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a478bb8c30b66f77108ab0b45caa8ebcecce9482d6bec317fdeb24c06814f77f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 512, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "ae8ad6418957aea5000c47bfe689bccd3e85fa75f8503fe13d8f150fc96a2dfc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 768, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "4bec31b6a9b3669f759dd56296b90f10c05c5ebddd383c4e886a909c7f5e141a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1024, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a387ac29ebd18c21d385b3752135e91f16010abb678861cd63ee1d014a7e5a3d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1280, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "93dabf0ca5af28828eaeeba9bdb489ae70b8ab9836ac0344bb264d25ab052cf1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1536, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "5f9678bcfec30ac864303c89c527d643ebc39e8e3959db661091aff5414f3748", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1792, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "58fc68308a10925f00838fd5925f71c3e1fcd82c63fe134ae255c3c2ac31c3dd", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2048, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "4730fb1093e41dcb9b8ec61247362d5e576f81182123be69be18fe6845978728", + "tree_shape_sha256": "ddc69b8f7753ebfcc5bdff53e39dff4832e4aaa1082b7e906966c42316ca0976" + }, + { + "position": [ + 2304, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7a424d1fc373be358d3d9ec245478c13042e2abc1dfa58b6232c51d255aa9a92", + "tree_shape_sha256": "54e4f282ac513b8ca619d1f6867d93b186e85215a0c3c2f874786c825f27cc1f" + }, + { + "position": [ + 2560, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "d8b526cbd01254dc95b910884f55bc7ab1156e042f2a7e4c30b9b60efa2ca7a2", + "tree_shape_sha256": "d46f38deef2ff7fc9b0570c21887556c09e8428a158a71fdc52110869f1c141b" + }, + { + "position": [ + 2816, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5f6c46ce2262b67bbdf1909d35b2187d102a2f629b92d26434f215edccdf8596", + "tree_shape_sha256": "528573c7a4f186bc1a5a7f9e99130009d87f0e3b4ef217e2a357043ab8914461" + }, + { + "position": [ + 3072, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8a97bbc7db7da74c60a9bbb2fed3974adb5b763a0b007654b9fe1a7bbaf39e06", + "tree_shape_sha256": "c1cae48efac87ce79879b940782d22de6eefee3f9f5c07befb9045827129e9ac" + }, + { + "position": [ + 3328, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "40719c2f20379c09c4d1612db07c3656fb7d2dc341a3ff4b507ae85e77cd8817", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "950ded5f9c39bfeccf2e3ba6c6dda9c63dd331399df581d1c7c36cb1fe2c1a2d", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "f36254f2a6ad9eda41d53f4df2d3d4944931a1581a74d734f99295a383aab8fb", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "dd2db5efeb1a7535e5f91ee73dc2ae1d2fe65074256d9a8d3b6f090d30118675", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "516d4748202f94c3ea65ef8ac3835e3cf9c701bfe205d103af883174b565c571", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6c6e6d68ab58db6f3f1feb1291abe7ed56cb98e5c1c38494e6bc5cf516d619f6", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7fcc8e29f197ffef1448b5adbc6920b4006485fb6cedf903b863913ca3cd030b", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "61ef7f6ab2adad7c5db6e3fa529251e61541dd733614e61350b64f59f41ac760", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5c0b1c0dd5d698f20aaf04f6500a11c88783d597932a590441c29a2aee810b31", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "755567bd07c916a5fb611659a81e6619bccc698c1c7c7f2de24eaf34dc184886", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "b58897eec4519e77d9c74dc6427b79b1bc81f1c8ef3ea257a0ed4a07b05533bc", + "tree_shape_sha256": "d705e212cea40750af3bcec98a408cc39318b29898874d4c87472e55097380c5" + }, + { + "position": [ + 3584, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "941fe2c42c08c6fe141d531def8483314c3f4683bcdf881be9354d7c0eaa9ac3", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "05237242ffe4362e8011982fa80d1f8f95fdae194807c896f3564c03d10ba72c", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "75346660f88c9a02e09d0fcfcae4774e455ca28b451dfc79b4ae29a10b4055b2", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "0e948d6c42cedd8d5dcd2a99d3fad83f8eb61bf956c3cdc256994e2131359673", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "6f752248a0e2e17d495dbef0d98f9a9c0ec0d545f627e1b01a36b4fc34ae0184", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "e07388a68cbc03672fc61020a251dd573fd74d6b9eef010d3c6871ff986475eb", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 48, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "c25e02dc14660701db6adcd3f3eb11d2eda38c86b1433fd9cdd59355024576ae", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 49, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "c8e55a8de5a307bb1eefe72bd969039f54f9fa6ac42e68948f17ce20cf57da98", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 50, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "c0be431ec284f336df9e679c4ba2175f8cf399c3b553ee33928bdd550879e0ae", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 51, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "818ddb15573df1045986d47a0cae81ae0fc5e5ab965e00373131cbbb8de0131b", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 52, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "19c246794cdb9af948af72c8b16ecedb9f13cd066be144524ff603d5ee55b428", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 53, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "b38d7c9cc8af5ba04ac5f699b19e92a4262ff0a1f610dc322e774b61c811d57c", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 54, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "1bb72c8d78d723512da752689c2908cb5e8c308ff8807d57406a2a79637ecf5a", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 55, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "0b756cf2684e72ad89dc068f07498aa16fc95a72da830b6eda7ae7dcb915f4c4", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 56, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "b8d890029b2f86ea9a91bc04c9ef868326cf18d76a1429836eaf98083a8dd37e", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 57, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "e6c79b8be77b814f361b79243228d8caa359c9a6bba68c318356e4fdfacae6fb", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 58, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "3503667ec4bcfeacad1214fde9331f761ae03e458b91a3bf70aed8f9efdff2ed", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 59, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "f5738f36ddaea8dbf34bb82a9ad83a4144b52da4a20fd4f6ac68baf6601d0954", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 60, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "bee7b3649a30fd73d0636927c087053db95fbb6779a6879b8fd9d7ba8e5ee87f", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 61, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "70cf274a0c0867a30c743863cd561f6c21beb002159269aaffccf8aabd19b361", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 62, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "50acf44d23a23d495b15ac94fa178c54870ca45eaf1d3d0c5735e1950b2f9c49", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 63, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "d9f3456da6660ee5d8c690d8ff4a3741e9cae3ba6ae9e01570d2531a039b9ffc", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 65, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "4932aadcc37f950630bd3368c9f9a2c9f2cf3bbef0a70b62188e92f80a4579ac", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 66, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "fa31f8d1b22cd611123e0a6594a2cf073595988c51e4a40322bc1891b24b8ea9", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 67, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "0cee3f43430d82a7fdf3c47e43be594e5f2630609be499f0da0f916cdc629ff0", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 68, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "53b937950557ae59ea765a77ddf768d205ade40b9d533536d7e5963db82bae94", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 69, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "277eabe39f3c041c26be53843649a753d69971a66706439cfb7d10e09d47f833", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 70, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "db7ba54337cbf904b29da1e7ff8c4012fde7fdf1a5aa8f7f88c3faebff67a792", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 71, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "cd6d98ceb4f45721aeaf001bd84038d952635874c745122c037edbc1b311ee11", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 72, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "4d0be0aeb34dc6e937badbb4a0621dd18630b2f5f944d33c9db9ecd5c9261a02", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 73, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "7bcee9e1df91c562cfe5035feb0b1960a5e2d42b7b1eb91b9efd31f6bede8fc6", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 74, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "3494ccbb48891a4cdf29d7f3726586f4ab49b2d01850314385153d933130f98d", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 75, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "a003d215d6b2c61333b842cab1b725bd0209023452adcbc053a7e630e6f99f89", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 76, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "adc3aa0dde25a793f7205d2106526025851dd4bf53681552a52b73de20e3f70b", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 77, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "dc08d76987887151aa558f779c9880a2881eba5b46b3a97de775a14fffa17953", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 78, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "c95ec6970dacecb4a40127afc892f071248e025681fe702c580d2fa22dbe3879", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 79, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "0c440e2813b082d24833f5310cc548bde774bd847cb5f36d1cb56a13e51dcf70", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 80, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "60d1ca1c586c0d63c9e3cb1c904072e32f1c6716c223f717225df5e38fa337d4", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 81, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "d46cb69be72232d7aca2933707ec894e2a50d611354355ab55cf528ef254ac3b", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 82, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "712033931e1e416382f7214cbf099ec5ff3522c395c6231e7597d6bba71706a7", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 83, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "b12864d4a177cff6a56ac5badc42f7600e7c0cdaf6ddc9340968f35827194e08", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 84, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "e5a1c471a0ef01b70abd17582e76ee575cf9859907e91db97b5315be8bae4edb", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 85, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "038e7505d20626165995cab81af14db2848c3185238801a78599e7229d7e6221", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 86, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "da18d23bdb40e1bd83a49fcbc824338677e45412646c0499f4f96d8116d1294a", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 87, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "d05b8d164ceed11ea78cd93ecd2ff4b79bcf0d1f6d9b70b640b82d2b8d8ef03a", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 88, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "ed3cf90b95e4421e68cffb3b35e3b52d4067a9b7084979fd6bd4e9fdfb195833", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 89, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 1, + "order_sha256": "ffa244203bbcca255d8e1f8b2335c00bdce22331032ec3c0179ffe9da89e3c36", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 90, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "a5c8ec8eb239c8c8f7ee915ff42cf67e4d137c62c6e60141dfbd72850e6ca716", + "tree_shape_sha256": "96ee12a4d88a24f18b6fb56f9d3857b8f899ee7fc37e51cceb06fc623405e0eb" + }, + { + "position": [ + 3840, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 48, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 49, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 50, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 51, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 52, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 53, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 54, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 55, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 56, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 57, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 58, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 59, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 60, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 61, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 62, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 63, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 65, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 66, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 67, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 68, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 69, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 70, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 71, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 72, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 73, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 74, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 75, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 76, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 77, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 78, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 79, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 80, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 81, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 82, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 83, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 84, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 85, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 86, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 87, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 88, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 89, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 90, + 0, + 0 + ], + "inserted": false, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "624a0867672c90c8145e7d04fd601e4dc4919acd643cc0f326f62a13ece8f71f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 91, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0e6a4ee3fc5e7a2003cb96e18d55d2e36710424dac4a92f2e2dec60bd3b19440", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 92, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "f3a5279412d0a65f69a3180f5c25132744008d5714e45c9a6db3b448efd1bff8", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 93, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "4684ad4f099310999d7099aa479026648b410a616c265ea57cce5be04d9d5a5d", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 94, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "c1e0e14e2235b956f587c326ecabd6e645fca49ca9d4ccfc3b54c051846b29b4", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 95, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "8ba5eece1ed93890b1a5eb2908488d464910bbb6dfdea4ff64afe1026f018429", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 96, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "893bd3363de2b52c2f4395e4f55f462d550b8381a3c1a97a633cc4ee5a67d58b", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 97, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "4c1f51aecedb5d9d3c041da5cadd58cacd04c72c075c816bbb07c27849b7f761", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 98, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "1b9dd7ef0c41e339b900365da773032a1d01ecfbf86b6ef024696ea2e8636048", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 99, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "9927fd71ba266b888006436764be66e248389b8478739f89c4517e7d68c73e72", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 100, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "ff6d815ea258e27bfe4079f1df0e5dc85c7d4848a05a117b5100e9bae1ca77d2", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 101, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "099d0613c49b6e813e0ecbfe5e68724560bfb349f5eee0be8188f5c3e05d9094", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 102, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "397fb0004634916f2fe0851a6b4216af7502d421ca45c92178bbd810529bf1c8", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 103, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "7a7ec537ba825db12a3981142ec3d4f8f026d85af8569c8ea29f3ed8817bb23a", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 104, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "3d856afe77c075d20fda66e9769d07022b2e87f0e5287d3ffc8f61bfa4362113", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 105, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "e308e38825e9564bb72cfc394073016057b7ef7e0c5d574f39f82bcb6b666acf", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 106, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "19d2909aecef708e46b8975fa3a5faaa4273ac945cfc44d9fc6ef67b2ad69b03", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 107, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "ecda6d348c58adb60992ef830e83fd1e283c16a09e5578ca255fa11d64d4e6ab", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 108, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0ea2f08f790389bf4d87f6b56f37f3bed71e64e9c69dedb7d0a06a0fdc18a97b", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 109, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "c175f80948434a0b37a3527c57ee103b746d0f000382a4cb7310e74b01287876", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 110, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "3ccf3c5fa2832b9545e4a7855ce59b60ec29d158cc650497c87a441e42b527ad", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 111, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "2ee4fb24802bb16366ba51118415a11033ad7532db7ccd68bbdf172cd1c1a3f3", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 112, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "5adcea262dff9be57f812d3a617d93974d6f6e829d1f1377a9b055702dbb43a3", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 113, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0b09852fc4c1cf81d3a5e37655d623ae115a2f5a4f9e0f0158c038745a23d322", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 114, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "cd1bdca638603605e1e4edd4abb532a262b36c09c63a50cdb77042b73749e0d3", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 115, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "fd5cc68e9f6e732355c1686dfc47044604ed5bab4ac1627dfe8d4af5aed6845c", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 116, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "696257e05791a737a20d6d7ec2792c5b87e8222661e9a3ade7520a82fd475b78", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 117, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "48cbf09c346d1b0f32ee9f045ecbda4f19ba05b67345585d0b58138108bb3c31", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 118, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "265fcd80d136a232bee56a98091ab02ec1fe490bbbae55a5fdca6bf565071417", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 119, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "f096c4b39abdd517267b23138b2a9e51675b43dcb379a5986024ccdc925672f9", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 120, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "6438e5a65e31ed24581e55d4cd491dc47e60dc0c177d1b301900410309c903be", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 121, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "b7557914763965b1a7baf856ef6df5761f882dfaa3ed96b680b91f298b6bd6be", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 122, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "2b6662b2f50b627465531f437676b8e7b622d82356a7bd6239987f31e59efe67", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 123, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "85460cca22cba37f07442aa71d04ca6b7c479ed2a6cc459eb900f070d0e1cc42", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 124, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "c9a72134eef697282ce8c872cf9a6c104164ccd51d81dd4248cac7266f049169", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 125, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "d567e6dfcddf3dfe00958852e0f1ff440d655410a87897b4bb1195c927633f79", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 126, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "bf2019864bf008b6dfc7f6d5288b84b16057eaa4350de51ffb4a9fb294cab65f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 127, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "4328e641b307b021434c094958541372b3cfa57860295781966af1327c42346d", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 129, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "d2f17af24526ec97892d1c519bd850a1e2bee6ef0411b91898051e127a355d7f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 130, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "bc0d1762db14673d87bc8d4c93fe83d79bc6c737a1f8e5dd01a01236fbf12909", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 131, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "61d3d2c70b2a9827a5c326afa74ab1f1f5ff5822705a7744e472d2303ee87ebc", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 132, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "4f2540b180916ee40c87f9c918fb47987a4fd28a554cf61300e7b7c13c306f58", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 133, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "dda9353ea35cf39bb9f07345247eee41a5ead2b5adb0fb21281afb389f7e1426", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 134, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "64f2c490cd93446beb312ea6587515f365c6df9f59186c72be42e3243a744b1c", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 135, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "2250b7b97ca5f791d5ca4b4c482b4f4c04e257e11936d4741571c0a690ef06fb", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 136, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "ead2765d770d911c7b57760a6ff3d08881dfad51be47e56fa055d178868868ff", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 137, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "9b3e1fcb10a80f63d0dff6db9d40e51b29435ad2c49cbadf91b57395c84d1acd", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 138, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "80e8368549f275e07711435fb0241144889e5bee84b49579d4cad8e2ee9a3e93", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 139, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "245b80b65f88cd54b883a55048b784192556627c45f7317004ca8008041de78a", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 140, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "d27efea039e72cb592256844594d7ff87c809c9c2050f172f3aebb0680c3c482", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 141, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "94fd5ddf81a1d84bda2ea772971332910511dd3cf1792dbce3a21c94a3d54f6e", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 142, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "5d4867a53133e60c913511c356f6301f1efc625b69d9581fbc1293d637c167c5", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 143, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "ab1b9047f7d6ba974d897651bae052e78d02ed1be5fbc8eb3326eeac97f40bfb", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 144, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "1b5a754c8aec53a0531827d5c0af4d4fc7851dee38da4624e8926cd51df59903", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 145, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "5891da9146f0e437275701316bece9912d7e5825e4c78bd7f87e11cbc920873e", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 146, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "2f026211607597bb95411e9479fa9edf727c666afe1879ef2b7c0c2fc323a8d3", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 147, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "5399860da986247586e4c3145f23ca4e1dca5e1aa9e639469dcfc1e1ba5468d9", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 148, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "74b626d241a25760a59c9348cd793638d416b3e2c36f4473328402770180b484", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 149, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "b224be6b0a90307ba2370ed6bd416684dea417585ca82e2e5e18f9e172f1f0bc", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 150, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "74e21e7e01b7f4416db5d8cbb9160fde4ca40ed0a3675233391b15d3de98db2a", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 151, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "197ba5383f67f081652f2d39eaab2fe75ea6195cb987451354c53764ba14daa0", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 152, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "282ffd4270c1c3d130bc4d4c14cb2a382f15e95dc39c4877ff45b18f974acb7d", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 153, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0e3c752eda114643f5370d321cd26bb79acbc3a0aa56290ee1761dca9b046e65", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 154, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "29fb5f3cf6fce8d521899a60087ea62cf917a38600506eb1ee2023c77cf11518", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 155, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "a1bea921ae252bacd324d54cca91ae71eac170304a3e0e6b2509d395047b2dce", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 156, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "adcbdccc905b4ea49eae5c3e0f2d2c9cb5446f4e29a54bf04b085ac589adc311", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 157, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "68d0fd95a0994bd95504d3e8169b7a375c72b0204d0f79529be23b5a2b0d33ac", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 158, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "8b5cc8e091ca0ccbe2a84cf298a9827fd43592da46f413cf9db812a94c70e238", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 159, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "c2e6ab45d650a0c1baa9759f9df96643e4f3ed5b2ec147d29e2aa1767eaa2a39", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 160, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "ea05d214aa6d13343e18baec3cebee6cda7f8b5acf98a8381436d8072dd084e6", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 161, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "6e3da0f456c4c6d8aeab0aa9494c695ae76d98c70051177eecf7f5c7e6f5f6ed", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 162, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "a5c5930e2347b05b8983feab6de33d49f6210adbabe391d827a88844fc6c986e", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 163, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "04f6100f84b2808e0eff54079adec3c82b91b5c9b2f622c2a949c79675147f2a", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 164, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "8adbe144d476795d6fa952a70eac2adf2b01b696088a50e9972c9c7766299465", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 165, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "3dad537864a673f61bba48ef4bbeebd98daa7c55c1de5b2a52ff07062b33ab81", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 166, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "2c30d66e2bf9edca12839cc4324d54780c437e676bb7b6fc36b4d0edafaa48d3", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 167, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "10246f2abc45b2d877be7921328395f023ed8eabde0edc3f1913c5244e874e2f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 168, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0f176be4d9d01de2dd31d0f07a35f5880d3de4128ee8e47d26c01da12a5b38a9", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 169, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "db5f194c063882417eff0507ee0ba0ce439b79638787d2c34d0a40dbf72361d9", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 170, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "8f8ce4bdd579e04fc09878efe34078290135d4b60a5f735df06a222cdd2fe58d", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 171, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "824fe8ec4e23947b0c0d82c275e16e57c0a3bd59eb50011c9f228bb039bc97fb", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 172, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "eb266c7accaf765dc5ad36ed5e580ccb0b0957cc7228c36bf250c155c4f8a356", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 173, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "068756ea014db3ece578f2e0ca680e86f68a532813a44c9b8f49fc60e5e5c008", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 174, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "3cff71d77364b057013d57a0ee56a5b5833302cbd8636530c002585a6e9cd043", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 175, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "647e552a9233e84c6ab5a921aaffc6dc41252fc5962673ebd6e2df7f180802a9", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 176, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "cae4386e60929dd5d6fc6b65a4817bc7a4634d628d20f5455a2d12cc3f002ec2", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 177, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "da5e2c96d595707df915847731a455041edc911424fe134c37d7f35cefdaf314", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 178, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "54b041288ef61baa9e060fbace387490fc3852f2c63f25a9772780d3c9213d03", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 179, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "c60291a5bce8d065eb3a7fbc74eb72b0825f6c0d7a03201c2c101fea52ed5d85", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 180, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "fcc7fdd6d74c6252d0903d013b41e123f2e469346fb6b098947021d8c448e757", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 181, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0def948b618ab0c7dcd64707633bb896bfa25b31a4df5e8d1495bb25defbba72", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 182, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "e7ea19ab133930e634de9bb30f9bf030e2b612c1cbc2f9fead992143e88cedc0", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 183, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "869a9e09c2d4ae81267e1cab66db3d689814dc353ada065e906165b0c3c5bb69", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 184, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "0f7ab8fdfc4fbea1f04e2bf9a84accbc0d870b9d3ceae002e8b0429f3477a41f", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 185, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "6a27d86da48f827487a83e3c7f6d7961591c6971a99739ee1bd53153330713ca", + "tree_shape_sha256": "aec0fb1b85c0bfcf73567b9373f9eae989f68fe08912bffd98b14ab39c84a2af" + }, + { + "position": [ + 186, + 0, + 0 + ], + "inserted": true, + "capacity": 512, + "tree_bins": 2, + "order_sha256": "a7fd0bb9a5eba8b7a9186aa1ebb695cd0d3fe7e5f1377de75a891ee869951c52", + "tree_shape_sha256": "96034df7569f4241ab9a2cfb404d1c74fa2815aafbe4b40febf72f1a2cf33121" + }, + { + "position": [ + 4096, + 0, + 0 + ], + "inserted": true, + "capacity": 512, + "tree_bins": 2, + "order_sha256": "e23c259850469e325ebf6505decf0948a2bfedfbb8e28e66970d6e1675984bcc", + "tree_shape_sha256": "fd1da2b8dde7bb78f48c63f31348720e258ef6d9d7197b6c3323b44375f75e40" + } + ] + }, + { + "name": "overflowing-three-dimensional-hashes", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2009112444, + -1024669950, + 1548280006 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "8fddd50a279f63427e3898a1ac0522bb30021771558eed1e9bfbd18247c29f1f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2091821115, + -1737564729, + 1005828012 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "9258bf83ea976eb12b8b0340e1269a0db68a368d89686836609a2175aae95b65", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 187793916, + -1132608087, + 284384781 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "393db7bf3f2748e1c051bd4e89f56cbe1a3ae8bf9610d37dc04238f33c000f10", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1148401140, + -1824399378, + -496532742 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0314d12be9c0fc442688138b9b19b73b0a59307b69ddf90259f59a81933a0f89", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 1643660664, + -1177107225, + -636364081 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "95b87cf89b6dae9ea03f2913b914491c9051966fe6fdafb8226b0c7ef4b7abf8", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 193402067, + -1942909294, + 1144036287 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f0109644179b144bcd744a4b1e9e1836abec926cd137146526fcd15f1309046d", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 931923172, + -1331968731, + -907722591 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "9af26c7b21fab8d24fe8dedef97b2011c15bc5d86b13a89043c94e05b1c2372b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 302026495, + 1188548138, + -1044240021 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "352ae0f383bd929c4c6bd4fb447b80fc61b49efe6b446856e4a9291c49c48b19", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 488910413, + -1052404978, + 634556481 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "9b3097f5b5d68cf3705c31d01b268d952721e29c4656cee0b8b518cee71ab13c", + "tree_shape_sha256": "eb292e8c50c7d2b2e51e17929372bcf54a056308520d9c5e3a111eba8855b2cb" + }, + { + "position": [ + 239249819, + 1537778272, + 1711037189 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7614ec20b4f198897e0d201b9ce2480a7dfad9ab9774edfd473d2fcae1c675d2", + "tree_shape_sha256": "6df274ff57948bfdfbcb812244f7fcc4cc9fe18c75d110ba14fabd01c828f0dd" + }, + { + "position": [ + -96289821, + -1202460919, + -155523898 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "ec0c61cd2dcc24daf0a3836c9c1604e95c8df6134e432883dbfef89f2c3bbe83", + "tree_shape_sha256": "49de073cca5ebc4007db6fbbb8f9b361f76f93c9693a20c425ccd58bbcb7335d" + }, + { + "position": [ + -650120678, + -868465859, + 1306902403 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8c60fc6a5bb0724f8ca084a877ca41fd8051b76f01897e2014ee441e39536a31", + "tree_shape_sha256": "f04c2c3f6ade5f146d3e8efa8a37361bdd32e0d8828c9e079d49923015e02b10" + }, + { + "position": [ + -1899624886, + 50559235, + 15988249 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "75a996b11c09b62b0099d57f55a7c4d09d68f40256d9d96f8c918b0754dbc4c4", + "tree_shape_sha256": "11b976b9a2021971a6d4be38e0b8487451d1d482c052afd73decb691aafe55eb" + }, + { + "position": [ + -389288696, + -1203576701, + -1909371045 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "c70f75a2407b3eeebcdec1d4a2d9732330c50142f6ac0fe40b6568c275862cf4", + "tree_shape_sha256": "5bf2675fdea343b501d31ce69e5ecbc7ef88c3bcf14cb21a913e38c6e11ae1db" + }, + { + "position": [ + -366916050, + 40569969, + -870199645 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "21429b7b9d47d35d2ed63300ef72f22ec5d3995c99949356ea4f2d37605f51a5", + "tree_shape_sha256": "46cf5233f90e57be26599ef7e313f14ca3a66f61a690bfbc04fb419f845c9169" + }, + { + "position": [ + -1203878688, + -395921575, + -1608320167 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "fc0c8e99b42871436d9d0e6daa6ca71e32abcb74c2afc47be9ba1d3d48d62dcc", + "tree_shape_sha256": "b28662001689a519e3143d4ba39e514a9c17d9cdc90fe21aa20ada3ee9b2a1a7" + }, + { + "position": [ + -1267239570, + 1150204053, + -636901305 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "38b894868221b7433d462e8102e2fe389cdda5278ebf100ccd4347ba5c3ac40f", + "tree_shape_sha256": "0745ea2a5c9e650543ec78ab63a18bef9e4212faab5ffd9bb266150e1832a03e" + }, + { + "position": [ + -24107366, + -511222227, + -2043816781 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e43f9f2842ffc55ffac3f082f805bb60945a467fb8ec62d1b56acf45a2d766b9", + "tree_shape_sha256": "f31156e75660f87be9a815a48b0dae676f35f9cf9b97766e929b1bdf2e01d500" + }, + { + "position": [ + 1681984333, + 2116612351, + -259971950 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "040bac3faeaa3b84552bf5d4eb35a08660196e0a65c7767e547bab8a04cab09a", + "tree_shape_sha256": "583187a1129041282654e5ad45335292b67711da9195d2a74ebeabc90a12ed25" + }, + { + "position": [ + 961617563, + 872639189, + 449061466 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "b1b53058d20d3446e5f3562775ca9bd80a1f074a708889f6f3356bf612965b25", + "tree_shape_sha256": "bfa52e1da75df8295e4ff41b39c80e40e4ef9bdc28ce1b34833c3fc6cff48014" + }, + { + "position": [ + -1964818907, + -1914891690, + -1659087823 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "351ca3b82f75cdd12561bb9ff5695cdb8ca8266111e24468781d25166008a1c1", + "tree_shape_sha256": "6ffb230eceb2cb733353322b4a0dfbf541e9ca386d4a0bbaafafd38af629ae6f" + }, + { + "position": [ + -363487355, + 1584649975, + 981661586 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "46b79011d5a9a6bc6dad2765bbfe5ffe33e6abd4fee5eb973e25015e06ffd834", + "tree_shape_sha256": "acdcf0e8e805d1a9ce33fbf58f6cfa743cc2ad87c794bc1a73b2cbec2b63c2fb" + }, + { + "position": [ + -1859474891, + -1031833768, + -1133493853 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e5fcf8aa73744a7024193dd41b09b14aeba84b3b3290d5ad59f6bc8f1e2cd76d", + "tree_shape_sha256": "e2d86564526cc1862b8b91e8020f6b6e851ce2b7101de636e7703359ae8c90a7" + }, + { + "position": [ + 2144777543, + 596559531, + 1801985988 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "1f7ac6d0570e8faceed114720136f2d22c2dfa1ebf80553a94fd3a3df9617a2c", + "tree_shape_sha256": "e5293472aff59bde4e53c0ebfc8b6888499381dd2db8b5d4c337df3b084af0e4" + }, + { + "position": [ + -1493583440, + -1101309410, + -1444482258 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "ac8c7b4db72b3ea3f35d50f67dd5253c1b50b01f46efd8a392fdae7a3b6c9541", + "tree_shape_sha256": "ac6043f322977bc08fd11e9afc574252b21d4f56477d9afeee9e9e95c4553c1e" + } + ] + }, + { + "name": "signed-hash-comparison", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -2147450880, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "9e7d99338fb137098f555e67dfb1fbd488b38468eb22be493dbe6d5b5fec532c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "d7b2d45a6f8b1ce418f4a94c2a5fb9b98fdd4aac4741d9f89dc167e5df7d063a", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -2147450816, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "1957748b3f0333a633f003fc37395245a5fb6f79aeb8428a8ee254497541d914", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -65473, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "92f43cd2f8389db92fdfb3f2cab8ce75766ec92ed02256dc0f90d15f32279968", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 128, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "83cf2e30bb618fe6e4107b3db2e1f7dd614e7fd3634f70d7ec2cf2776ffa264b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -65409, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "f5e8d264f33b2e3b68606f6c7fb0a40b02bf2948b2226eed26a82cd71a93e73c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -2147450752, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "ea945e1f0ca5e3ba362ad4e81097ccaaa0470c3371f1e893a017e4342bbca9df", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "00abc47ace9e53ba1cd9d9863b8fd772b1703186765b56fa9c884cb419db8959", + "tree_shape_sha256": "e633ea12ff3a67be1378ebc0da2ef85ca2c8b4bc8ae7b2909b8c3804cd24ecdd" + }, + { + "position": [ + -65345, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "579350650332cce744176247a448e8c80452f59732a7cbcb0b5c607c3dffc921", + "tree_shape_sha256": "717656903d2345cbfceeb394711d4e8292d1a3127bab1d804b8867fe445610f0" + }, + { + "position": [ + 2147450943, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832", + "tree_shape_sha256": "fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b" + }, + { + "position": [ + -2147450880, + 0, + 0 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "83828ad225d72d77a4ae21db9e21f81ec519d0d55449e57ffbbae991d2cd4832", + "tree_shape_sha256": "fbbfdc03b29cbd105a003f024b4fe64d4f4bbdee17460a4c60d9abcfcb36a68b" + } + ] + }, + { + "name": "equal-hash-sequential-identities", + "insertions": [ + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "a17138988e1387532b5cb0bd7a23f18a11d537123873e67154dada3c6359e53e", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -961, + 0, + 1 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "d6a6531cdfb49fca2419e941ccc457424f5732758f4e70c91ee08c20d235fa23", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -1922, + 0, + 2 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "6cb4ed34dc91bbaa0d8a0266f624f550fd7371740184bcaab75a2e62ab5fd520", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -2883, + 0, + 3 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f670f1e579f1606ea556d019d70c1c70ca36a132f0fe2fb711f36b051a80d67c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -3844, + 0, + 4 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0782b23169ff830a7e8df9b4c4336e9908a5b618936d718a617dbc0866f93965", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -4805, + 0, + 5 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "92c131f85ba57172f6ef57267c35e1857786f591a11e244deb47985910a1f099", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -5766, + 0, + 6 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "abf7f527a15b390fe5733abc7acdb925b079c2ff885322aa8f5589ee1e6cac92", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -6727, + 0, + 7 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "6c796ec96389ca6cfca7e5efaaf471480bc843642007be3e69e7f470e19f782f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -7688, + 0, + 8 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "2c40726d335d09f9ac592403037ea85f4119aaa0e5a37d30ccceffea1061d57f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -8649, + 0, + 9 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "43ae91711870b9b55a68184065080060c7cab20d8bf9eef81b8808e02cadf6b7", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -9610, + 0, + 10 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "daf20b594761396b22bc4010a41ff55595f32082779bb688b3f8d894dcd0edf2", + "tree_shape_sha256": "84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca" + }, + { + "position": [ + -10571, + 0, + 11 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "4a8338e6843a7d60b19524ded28b0dc059f803630db8d3f276194062cf77e27d", + "tree_shape_sha256": "b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39" + }, + { + "position": [ + -11532, + 0, + 12 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "d44f108d60f8482a660065e505d27ef2e46073232086a3ed64cfe4f4866321c8", + "tree_shape_sha256": "b78ebc662f60e63bfb405ba6bc7f6c08e2182547bce1400cedbdf1bcc398fd89" + }, + { + "position": [ + -12493, + 0, + 13 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "f34b88eb8bc718fbd41c430fa915c46b61203e273da761402e5b00e960251b71", + "tree_shape_sha256": "570275ca3b9b4944601bafb2d7a82937862fce41b785acaa499138d4ba2320f4" + }, + { + "position": [ + -13454, + 0, + 14 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "069f90a71a6d817b2db6575425e67facc5916d60929c412cf4e85bd94d7462b9", + "tree_shape_sha256": "70d228f8cb5adcbc87aaa1a2a6858a0795cac2b2e805fef7095de635172b41c7" + }, + { + "position": [ + -14415, + 0, + 15 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "db6992057b9ba2d862ec1c3130aea7b5c06413f7d2b35b08d07737a3617a663d", + "tree_shape_sha256": "ef887de5be1e543580d1ba3c9c864467f7845bc53f347cc530d56c96d494c412" + }, + { + "position": [ + -15376, + 0, + 16 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "76db8af81c29dfb29154fd8c3e55db095fc74edd591367b659dbde03d59882b8", + "tree_shape_sha256": "db5d2d544ca06fd3ddb3438a7d449478cac9608c4ce874bb793220ce13ba1462" + }, + { + "position": [ + -16337, + 0, + 17 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7b3edda6525338f416c78b70d5775addd8db22dd8628d93de36cd485004c2fc7", + "tree_shape_sha256": "9fdaedecf5af2930aee1ab8bbc30d795d16e3510042da2b149d1d1172990b240" + }, + { + "position": [ + -17298, + 0, + 18 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "81112a160eb871be093acd07fe44b24a14f90bf293503f64381ccb659f9897b8", + "tree_shape_sha256": "0a7d05282271ab28563c6255dc126e00e8c990f3e09497b5a0c52ee332c7599e" + }, + { + "position": [ + -18259, + 0, + 19 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "cdb89cacc4756f858e17bd59989f30432fd1a6e4912b82d415233d96b70bb043", + "tree_shape_sha256": "908e37aa30b2690bb95e1ef971016b7716a101ca6b69599392aa5e1eb1c5f537" + }, + { + "position": [ + -19220, + 0, + 20 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8b4c98a175713a69e7a39c5c1ccbf6f2a89bcae693ca24c31b19698609237dd0", + "tree_shape_sha256": "563d08119299c94b013c9ddd150ad54c90de5f29b4b87c54144155ec0171864e" + }, + { + "position": [ + -20181, + 0, + 21 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6683fdb4d582ee74dda1b74f682a1d32ef54580ccf6f69eec82b8439dd29c2bb", + "tree_shape_sha256": "0d388687314a5852a16f7b40468c194ba94bb756e7699f377c86e2d310c7b2a2" + }, + { + "position": [ + -21142, + 0, + 22 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "c7de33105dd48cea06df66b17a3a38f724dec841e9a0e298e1f44dd86afa20be", + "tree_shape_sha256": "cfcad2fc0e3ba75e18fd14d22133409234b75f996b22cc44d7d2bb9c2e7ed89d" + }, + { + "position": [ + -22103, + 0, + 23 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "c653fcc4eb5db571f293ca8b7b228d0544cee709378f5750f6d1816af09a02ad", + "tree_shape_sha256": "b8bf175f1204adb56a775b5c643313a3f7f642e6f74c321789c6f366d3e43a22" + }, + { + "position": [ + -23064, + 0, + 24 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "1b4c6fcde97e76a295facb7dec9f1d550baf95fc2d7cb63a38d4b1c49c5895dc", + "tree_shape_sha256": "727bf5a5e698bb7bfc66022f7eac0ec6dc6bef46e90750ee86501b71f7d99963" + }, + { + "position": [ + -24025, + 0, + 25 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "993c50fd0595f1f8f396f78222f73058256037c0701006b9604834d87dc205e5", + "tree_shape_sha256": "1449819139f42b851bdded67a306bbc38f991c7db4740b10e2d9016b0211349d" + }, + { + "position": [ + -24986, + 0, + 26 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8f84cce6f4b53cf2a394541a25085a70a93d4a46271d8005191cdced8ca15f06", + "tree_shape_sha256": "8fbc8fb6069cc09077fc762fda3845bceeb1e2b64751f9dde3a7a1bb5a2fa26e" + }, + { + "position": [ + -25947, + 0, + 27 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "b3d3123e481e7e62376a0df3259d20f56e27f9208f58ea6f7e02ba9cdf14e6e5", + "tree_shape_sha256": "378bb234dc80cfa68fbb9e19b5f5d320758235454f00a68a76d473c120b20880" + }, + { + "position": [ + -26908, + 0, + 28 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "113b77d0076533415ae3cc64e0e484dbf19f978dd283cd791d09676d976a2114", + "tree_shape_sha256": "9d9db3b20eb9232d566bdb8a887f6d834957f9530d865a5ecb2df5343f0efa98" + }, + { + "position": [ + -27869, + 0, + 29 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "8b85c249b492245ed7f7556656b545b72970dd3e0adb0554c6a0d698a842d152", + "tree_shape_sha256": "2d4cea9148bc69fb9c7da49f0425ecab77f8c31a7595539fe0f5635670476079" + }, + { + "position": [ + -28830, + 0, + 30 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "9d79923f219c004b189fa8b9aeb0e61299290454e28099798918f0d7ebf3834b", + "tree_shape_sha256": "d9d3c6b81d39eb30686d491a2124feff758bfa4224b6fb0aac6724f73278a95c" + }, + { + "position": [ + -29791, + 0, + 31 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "48cc02b217db0ce88a9d7f2962528e89513862cbd9a8bd0561fb4719c871c325", + "tree_shape_sha256": "672d63946b4c94c0658a67235d236b3dde70feac49efffba4468616fdbcc9411" + }, + { + "position": [ + -30752, + 0, + 32 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "c323a6afe71851d3fdc10a55414ba5cab722e81a3b3c8d5a70835cedff1eae83", + "tree_shape_sha256": "bfd6951f83c11e3caf9cec5f9d2cdb1ea44be9caeaa25c64d4248881698d25d2" + }, + { + "position": [ + -31713, + 0, + 33 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5b2a9db04b6b27f6010eaf03d05af6b6be95abb8455e282a863a61a80e396a10", + "tree_shape_sha256": "74bae8284c0972d5bc5735b9716375fa1110ef05ce6a2ba8832b95b87673068a" + }, + { + "position": [ + -32674, + 0, + 34 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "f2a68e6ed1be399c7e05aea8d1773e4b0a6ce8cbc680ac0e4e0e37e324c880af", + "tree_shape_sha256": "a1f35a34df27609ef104f0123902335709e78afaa40591e8a8d9fa845d903107" + }, + { + "position": [ + -33635, + 0, + 35 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "0df0d72efdd77cdb5cd5eb8de80f5b71d968b54855bc3f8ee97d7ef4c0faf956", + "tree_shape_sha256": "3b1d10d5264b5e2c3206a7d338b4b5e09f873969b893f4bbefef59c775029adc" + }, + { + "position": [ + -34596, + 0, + 36 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "3724862237ec4000879297e6b56eae5dec4ef78b76df8d29a14f2855584955af", + "tree_shape_sha256": "16f188b175398518f638953266664293ca12507574a37cd297e8eabf4e2cf1d7" + }, + { + "position": [ + -35557, + 0, + 37 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "ebfb5279a8b869a7e8e3f2de372b76b9ada20f90e0d6ca14f0ad6795c61fdc5e", + "tree_shape_sha256": "126026f7603a9412df5f1eb0275d8dfa91e2579ce716dd47a4797fbee05fc645" + }, + { + "position": [ + -36518, + 0, + 38 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7984f3a1f54ded99dfb88142c1a205bb3b5daf29fb805cc15f42efc32f9c5cf3", + "tree_shape_sha256": "cd4ab893ff909a144c37f7805d81e488bc32427febb63104a8a7c2bad4df7a53" + }, + { + "position": [ + -37479, + 0, + 39 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -961, + 0, + 1 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -1922, + 0, + 2 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -2883, + 0, + 3 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -3844, + 0, + 4 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -4805, + 0, + 5 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -5766, + 0, + 6 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -6727, + 0, + 7 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -7688, + 0, + 8 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -8649, + 0, + 9 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -9610, + 0, + 10 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -10571, + 0, + 11 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -11532, + 0, + 12 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -12493, + 0, + 13 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -13454, + 0, + 14 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -14415, + 0, + 15 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -15376, + 0, + 16 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -16337, + 0, + 17 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -17298, + 0, + 18 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -18259, + 0, + 19 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -19220, + 0, + 20 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -20181, + 0, + 21 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -21142, + 0, + 22 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -22103, + 0, + 23 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -23064, + 0, + 24 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -24025, + 0, + 25 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -24986, + 0, + 26 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -25947, + 0, + 27 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -26908, + 0, + 28 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -27869, + 0, + 29 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -28830, + 0, + 30 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -29791, + 0, + 31 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -30752, + 0, + 32 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -31713, + 0, + 33 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -32674, + 0, + 34 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -33635, + 0, + 35 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -34596, + 0, + 36 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -35557, + 0, + 37 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -36518, + 0, + 38 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + }, + { + "position": [ + -37479, + 0, + 39 + ], + "inserted": false, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "e2e5e51678d8996587571e02301b7b64e9722552b6b7ba8a538367a9b16c9be6", + "tree_shape_sha256": "8d1c1270f68fa5ab255e247494186b13965d71716157c3f8b18d6fe4778d6b6e" + } + ] + }, + { + "name": "equal-hash-rebuild-after-split", + "insertions": [ + { + "position": [ + 1, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "32f84fa8edc8853c4c330222bec2017632295bad29b50f65a779b2ac73c4a8db", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 2, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "f815f2e9b7fa42f7387a233b931e00b6fba3cc3eb77b041253c4293478cfaec0", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 3, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "1d47779a5f2f28b77a7cfb39ad46d1bab3d2e4167b3cd3d493c6682b581b637f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 4, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "e6a66f45c2cfd992974b5ea3fac5cb070db39159b688b142a0778401c7f14de5", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 5, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "301b15aa4c7f6366faa2d90d660aecbe40835ed754c6c215bbbf9664ebfda9e1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 6, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "7a21e0386267e35364a4b47f3debc3609a36f2622a2c0a2086c618b4fe8292b1", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 7, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "b2d8080c72ac097f7eff16fb612f55316c3cfaacde3f57cc25279247863b51f3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 8, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "eabcb5d9a364b769918d21f402343095735d9284a8438fe1e3a0e4b07e946022", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 9, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0a5a1c0d4978d0b55f6b81d4822a3cc3b5098b4f91e8dc56685d3628d241c3cc", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 10, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "4af6404c0d89c3ac3ac68a07e4b989b875df1fdcedb00ac9eb2826f52da0d865", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 11, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "c04a85175d6fd84afa493b7ba074cb32881e1afc78dd1715bcf8a2892fdb7caa", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 12, + 0, + 0 + ], + "inserted": true, + "capacity": 16, + "tree_bins": 0, + "order_sha256": "0069f16f28d0332bc69ae5078077fab5b237e03ce772a713205cba9a23b2a51f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 13, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "85c387b1df556db48bc66dbc63eaeb75f761a993ad73a8188e718d4f91fd56ce", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 14, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c5c55b38c86b7eb2ffa5711f15458727b328d2b67c4589443b8560b2997ad35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 15, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cd5f7d5401e4125ea0f5ff22ecb897da244f9b7ddfceb4c331b22e3c5fd342d4", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 16, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "6c918ea838e980f1b726756ad7d3e19fe1f4488812fdf0911aa655e62d9b534c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 17, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "f69f4aab7579881673d8f68a5efe4c8942badf841e77e5a5c56b006381128e9b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 18, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e6ac23932eb06b27f382cf4d8d941fa5086380227abe60ff9bcdfef3b0836544", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 19, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "cc9e859de09cc489eb7f611d4c0ff2ef80b693dfd7033793adb872bf7ea25e35", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 20, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "86435d887a0c8cb360fa61240d5e9e076732a9096f74ef1efbf69cfd5463f1ad", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 21, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "30b2f43d6e62646f15d936d6f21eb17a467db117b971932443b1f45dd7b3e74c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 22, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "e47f33fb935ca7aad567bbbdc4f6c5ff259f4dc94ec76c29da9bbf2bcd8981a3", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 23, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "49d130c6e6ea2b72ad2f57ef0ec7aa25dc757bbe9c91e6d5932066aff4536f01", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 24, + 0, + 0 + ], + "inserted": true, + "capacity": 32, + "tree_bins": 0, + "order_sha256": "8781f9a1994bdb6fcec20b580f60d965b3cbd6428671e8864e1b176f074dc536", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 25, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "0bdb70aadfdf2a6e2acfcd5e13068ef657c3d4622f136df5bb1fd9ed660f3118", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + 0, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "e25b7749bcda772893b7eda75962c24555efb887d49f76cf162efad24fec066c", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -961, + 0, + 1 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "28f2321938e91464724d62b132c2f9cf7fb0a2baaa6befc9687934c4368a2e02", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -1922, + 0, + 2 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "ed07bc1e4a9c7734c2036f91f06d491a989db5bed82a77e1caa0a191a399bb2b", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -2883, + 0, + 3 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "a0605ca52e48537e4e4549ce64bae2dec257487e72aea756bdaa4bbf7d83a4e2", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -3844, + 0, + 4 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "672ba16f45b976346fc4ccb484100bf9c5f1170432fecb4770129511ea9a41f8", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -4805, + 0, + 5 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "899284429a4dfb8f5be1bb62bc8c04e5cb2626e81ca1816ae48b555de321898f", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -5766, + 0, + 6 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "b86f2ae672461be3dbc4fb1be156e114c773542663767aea7dff21d418d90d20", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -6727, + 0, + 7 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 0, + "order_sha256": "031f818ccaf1a0f6f3ac2695a5f9396ead2b0cdbea6a9691eff0e89ec30b4756", + "tree_shape_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "position": [ + -7688, + 0, + 8 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "dcdb4d5436aa1cdc03b47f05ff26b33571707ca1d57032698d5a3cced8eb81cf", + "tree_shape_sha256": "75328e67ca3609924e3438d04b5afba893309762643ec69321741c1a0f64e55b" + }, + { + "position": [ + -8649, + 0, + 9 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6ef7a69d8b4474527f202668739628a9e84e8aee57babb31cc6de596950dfe4d", + "tree_shape_sha256": "7f2c5e0a7aded0b2c93c77aa27b6df7976285865977c3f6a03eec123c1968e8f" + }, + { + "position": [ + -9610, + 0, + 10 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5ddf4fb23faa9c9cadd10a231a4b59c868dc1e721218bdd16643d9030d5c70c6", + "tree_shape_sha256": "84be79d6028723f6adbf84545124091ea61b8e40b68e3fc4c94a6b65906298ca" + }, + { + "position": [ + -10571, + 0, + 11 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "b2b41c4135ab2534db7afdc937e1be1291e068da834de83130d3bf65a51d0006", + "tree_shape_sha256": "b1d1873b003889fcd69d85a6a2d52445be8e08d2fd34bed44bc890dda6f86c39" + }, + { + "position": [ + 64, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "d44012d5a8ed3653116e274ef413b876f79946247a682bdd9b2fc3db8b269a3b", + "tree_shape_sha256": "c6af71d6450f9fa424506e20f0685616c89fe043a5dda19fbde83cc7dfc1207e" + }, + { + "position": [ + 192, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "7ab4bea925c1e1571e245aca6a4ab0e2406373715e21cc746a3513a038add9cc", + "tree_shape_sha256": "62c009a4a11fb122e75e62b432c5ad7d1bc1bb82b6d72008535176714378714b" + }, + { + "position": [ + 320, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "6fc46d6c4985c56e9c8917daafc02e6a131de058c1ed9a59a710d4140b80053c", + "tree_shape_sha256": "38ec63211d138852829eb8487aa68cb4c20c3f632e5c81d25948c35befe41639" + }, + { + "position": [ + 448, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "bb752e67c8ffb88c8015fe8bf231dc3f83d6ea7c05be9bd1012115434b400749", + "tree_shape_sha256": "767e9ce7a87d89a4846460dc74a883f62cd2b6ab50136a2486e49867684d78d4" + }, + { + "position": [ + 576, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "5fa455a6d315c342715490a477a70c30006a6659614879cae764e898d4c83ae4", + "tree_shape_sha256": "77840e858d833d2c07b8a8268c5e1536a41aacca07f44680da897966b85d9170" + }, + { + "position": [ + 704, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "a3613e36165f18d9fe8e1046bb04664aad6109a65317e1f3cf1e9a3c288af2b0", + "tree_shape_sha256": "3afd26cc69fbddc34af7c0339a91bda92ddfe07e617c0eb9f665a632d683a0ad" + }, + { + "position": [ + 832, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "a3f108c97653742c1916cf89e79cbcc90d0779310f3741ed84b0086ee0ff596c", + "tree_shape_sha256": "3a63beba0d2b43f8d749d16ee4ce63604715f7d4895adf69dbe8e2dcff3ea330" + }, + { + "position": [ + 960, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "9b27867c5693c0750508e589991ffbea799567656d25b2247648f7616c80a725", + "tree_shape_sha256": "60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "34cd9f42fdb5ec5552e05d587bd65ef4d9bb2910ccc8f4d9d7e2f5f1558a6679", + "tree_shape_sha256": "60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "479869cf52945b2811ea6e932b61f5f85e417555d76dc7da0040428aec50d1c4", + "tree_shape_sha256": "60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": true, + "capacity": 64, + "tree_bins": 1, + "order_sha256": "1232cf3630377c25240f08168a7f0fb24fef6820ab40fe2d895d26bae9acc7f7", + "tree_shape_sha256": "60a9ca69cbb099f2d2ea6543ca0a805b201c221797b359f2d23a757646f3401b" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "58bddd158d069b4a2f196e37235229d9a81ef08a1a1a4b818b9352483b0a1ff5", + "tree_shape_sha256": "3b685fe49ef22d30c6786b08b8c27bf481e5f4d00dd9bc0c343ab50f1bfe629d" + }, + { + "position": [ + -11532, + 0, + 12 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 33, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 34, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 35, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 36, + 0, + 0 + ], + "inserted": false, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "26dbb9fa4dd5c105b3905337ffc835a71b5737aabee764b47194d7f0529a24af", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 37, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "81ca95157f58410f9a0cfbee34db2fc8cf5d7c38c6be4b58de764090fbbe00a1", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 38, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "7e416ded09240ca979d531b444983b40ccd3a9ded76428f8bc0c9ffc61243bd0", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 39, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "426e9bbfe7eb695d7196e2684a0c1d84c5dd7f0e070dc339d436c28206de96a4", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 40, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "627e964d4df803a029a8368d3e922d9ee0e7dd08d149cb9e01d181349afbda08", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 41, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "1cd561a978f7853d42e13064290aa124467be6e898c04aa85fd7f9c5081693e5", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 42, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "5f24daead54530e127923a342164f3ace1a5877836675aa47196616fdd7f9574", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 43, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "d82898824c3b6ef18d8ae955a6cfa5f4d587096d79f03abeb2753ba05ea39027", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 44, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "43cb7761d8fd4aedf0c779c029a6e0a3d0ff424d6fa4a81128a840fa97427356", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 45, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "036a22cfeecfe626de7af464c0f5d714e5820122b7dcff8da90d3be1baf45230", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 46, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "f55359b81f8e2d499fb44f7b66f35cf58c369126552decc9b6bfc9f1a7a798f5", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 47, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "b886e62743d53a30d851716697c8f1a3d9f739fa931807578b0fae5663251fa7", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 48, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "de325163135313f0d3a0ac02bad4df4a1ebcd7b4002a0b715920a0eac07181e6", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 49, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "3d3034b9091cf442697b49a8ceeb38a88647a79637c1a86a836c70c818b2f9e9", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 50, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "8944b73b66cb104bd6d4fe191d8b492cb94d0d014e39948da1e4f7cf9dbf77bb", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 51, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "97796bac3f0defd81b0107eca78b172089e2fa94de73966b96894d0e865c8708", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 52, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "4ad7ef6ff5268c8cfb1bf07d45b437987189dac882481f4236cf7f3962be748d", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 53, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "e15512c30cdd2b2fefbccfb28d948a73bf8e270659854090ebfe974f8014b866", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 54, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "4bbc0655dca0176a06116313a8250df5b3abc8806391864dab4638e12660c590", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 55, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "2920265c3294bfaafc6a0fe861dc3286e0a2e58fba57e655498830dcbbb4e91f", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 56, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0b4723197fb30f51f8324a9345329eb26537384ad7a279cedc9a49517ede1c21", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 57, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "967d783bee8347d24535c9e2109b6b96f19e978800880db01c895e9bba76d849", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 58, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a2b1aa1ce8bf0b09ae9184a24762e9117f3b8b24aca9eace218a3f402a42eb98", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 59, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "53e998b5c0fc11cb84735c34b0a4f7bf75875b43753113c9384fc5cd33b40f7b", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 60, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "c6b4566964cee98292c5da1eb27a83984e2d0dad8260ee2738934e1c96775a27", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 61, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "d0f468d617714b8cc0816e9e0774bfc562ce77bfe25dd46b439b6787ee46b9c8", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 62, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "3a6cffbd7773e3dbd5df13f8302ea4aadaca610a0274b5ed3885e3ea5a9e9cd1", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 63, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "7b12fc9cfc3682a72333b8684bf24d7db1d3265806cbbc0110e7f79f6884c611", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 65, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "7f91bef82718e5772ac27a284d115fa788fdeaab6d54d31da9f40048f6e328d4", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 66, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "38f3868ea0556fef5f51da993555c8d60b2b5a5b9206647ba044c82d90a4d7ee", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 67, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "4aa7087bbe3e3e010226b048d350122b6442e3dcf8dffb13966aa4f4a178c206", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 68, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "a3f6a41680a6a130f8f9dc079f63b400d66ab382f8453c28c55a4c3efd3547bd", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 69, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "0a31f19dc364caba577b9687297c236e90d2fee166c0aae401a6d34e785daa9d", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 70, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "b16289a9b4473eb6db7ff1107a2cced757ef974256861d6607c902b07cdbd8a4", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 71, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "70037c27de33d04d6b52f8f31bb00eb894ae841f3ce7a78c3af6cd375a4b6522", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 72, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "7c6c6950efcc128dc0059fbe5d51ac1d500bbfb00fd4946df471881448b3b71f", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 73, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "f6ff623325609ec3585f8bf254f11fd246aee17af0dbacbb35ba963504737d81", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 74, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "74521421e0e3195e4d8d02fb69ddf11e5cd404a5d1aa01fa17f6a48c1f22eee2", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 75, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "b902ee1a8d822bf990e9c974bdac88847d0b95faa21f6a28ec30a4eeeb49f68b", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 76, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "61f36b928ac7bc49bda4515600637a032af4c0c1320cd6d9416b1f7d5df58a75", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 77, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "615e2921695e0af402e876698e65e229ecee3e579f05e24a0bb171dd964fc0db", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 78, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "f03979dc4e93ad8ec89d042f145edd27a9c2b61b74d3b418828fdfe7384a9f0d", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 79, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "ee281edd421da3ea1f270f5929090d14e93092f0c0068ca395d765a89a031f16", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 80, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "d47edbde3765827b9c958382f3d9d34f65e3d52e33d98a229928e9346b61afc3", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 81, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "44732290e14ba7102004e7f517662afc8e219147138b5a61b5c82d43d4dc3270", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 82, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "4a1c410eac5a048f3a3718609fb373422acdc616ce953e5f3205822730b24f26", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 83, + 0, + 0 + ], + "inserted": true, + "capacity": 128, + "tree_bins": 2, + "order_sha256": "8fc19a64f73c7ea721400c7cbc19137769e89d34cfeefa59633c82a213bab96b", + "tree_shape_sha256": "9d2d2a751f325154ea089001db7f936625a64f2a0cd1139de991189d0c125356" + }, + { + "position": [ + 84, + 0, + 0 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "483b278321ce9f366efb1240e44251f0af4ab866ea680315ef96a5cee3d59cdb", + "tree_shape_sha256": "94ca30e3be6601240004b38ab375c316c5d4af052423ba6058019e1e22d96734" + }, + { + "position": [ + -12493, + 0, + 13 + ], + "inserted": true, + "capacity": 256, + "tree_bins": 1, + "order_sha256": "32d900d9e0043e975a69fdc9345b35c3b293088edce67f9e69166e6df0cf3a20", + "tree_shape_sha256": "ae31d94b5ba98a6be4ac62effefc8e2eb9732294235edc0d1bff4d1721ad025e" + } + ] + } + ] +} From bf62392cc81921fcad6e8e2f1acda852d7e22928 Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 26 Oct 2026 03:17:38 +0100 Subject: [PATCH 16/26] more json --- .../server/block_rays/tests/gameplay.rs | 122 ++- .../block_rays/tests/vanilla-tnt-order.txt | 160 ---- steel-core/test_assets/vanilla_tnt_order.json | 826 ++++++++++++++++++ 3 files changed, 904 insertions(+), 204 deletions(-) delete mode 100644 steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt create mode 100644 steel-core/test_assets/vanilla_tnt_order.json diff --git a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs index c59822632fd..a4540fb1d1b 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs @@ -1,4 +1,4 @@ -//! Radius-4 TNT callback order and fuses captured from Minecraft 26.2 on JDK 25. +//! TNT ray order, block callbacks, and priming checked against SteelExtractor output. use std::collections::BTreeSet; use std::fmt::Write as _; @@ -12,6 +12,28 @@ use steel_utils::{Downcast as _, WorldAabb}; use super::*; +#[derive(serde::Deserialize)] +struct TntExplosionFixture { + minecraft_version: String, + java_feature_version: u32, + random_seed: u64, + explosion_radius: f32, + source_position: [f64; 3], + explosion_center: [f64; 3], + tnt_positions: Vec<[i32; 3]>, + insertions_sha256: String, + iteration: Vec<[i32; 3]>, + callbacks: Vec<[i32; 3]>, + primed_tnt: Vec, + surviving_tnt: Vec<[i32; 3]>, +} + +#[derive(serde::Deserialize)] +struct CapturedPrimedTnt { + position: [i32; 3], + fuse: i32, +} + struct RecordingCalculator(SyncMutex>); impl ExplosionDamageCalculator for RecordingCalculator { @@ -38,28 +60,39 @@ fn format_positions(positions: &[BlockPos]) -> String { #[test] fn radius_four_tnt_matches_vanilla_order_and_fuse_bounds() { - let expected = include_str!("vanilla-tnt-order.txt"); - compare_ray_mode(expected, true); - compare_ray_mode(expected, false); + let expected: TntExplosionFixture = serde_json::from_str(include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/test_assets/vanilla_tnt_order.json" + ))) + .expect("SteelExtractor TNT fixture must be valid JSON"); + assert_eq!(expected.minecraft_version, steel_utils::MC_VERSION); + assert_eq!(expected.java_feature_version, 25); + compare_ray_mode(&expected, true); + compare_ray_mode(&expected, false); } -fn compare_ray_mode(expected: &str, recorded_rays: bool) { +fn compare_ray_mode(expected: &TntExplosionFixture, recorded_rays: bool) { init_vanilla_registry(); init_behaviors(); let fixture = fresh_test_world("explosion_java_tree_gameplay"); let world = &fixture.world; - let tnt = populate_fixture(world); + let tnt = populate_fixture(world, &expected.tnt_positions); let source: SharedEntity = Arc::new(PrimedTntEntity::primed( &vanilla_entities::TNT, next_entity_id(), - DVec3::new(8.5, 65.0, 8.5), + DVec3::from_array(expected.source_position), world, None, )); + let [x, y, z] = expected.source_position; let center = DVec3::new( - 8.5, - 65.0 + f64::from(source.base().dimensions().height) * 0.0625, - 8.5, + x, + y + f64::from(source.base().dimensions().height) * 0.0625, + z, + ); + assert_eq!( + center.to_array().map(f64::to_bits), + expected.explosion_center.map(f64::to_bits) ); let calculator = RecordingCalculator(SyncMutex::new(Vec::new())); let explosion = ServerExplosion::new( @@ -70,7 +103,7 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { (!recorded_rays) .then_some(&DefaultExplosionDamageCalculator as &dyn ImmutableExplosionBlockCalculator), center, - 4.0, + expected.explosion_radius, false, BlockInteraction::DestroyWithDecay, ); @@ -82,7 +115,7 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { .try_with_block_region(bounds, |region| assert!(region.has_complete_data())) .expect("fixture has a resident ray region"); } - let mut random = LegacyRandom::from_seed(1); + let mut random = LegacyRandom::from_seed(expected.random_seed); let mut affected = explosion.calculate_exploded_positions(|| random.next_f32()); let mut hashes = BTreeSet::new(); for &pos in &affected { @@ -91,42 +124,41 @@ fn compare_ray_mode(expected: &str, recorded_rays: bool) { "fixture has equal full hashes" ); } - let iteration = format_positions(&affected); + let iteration = affected + .iter() + .map(|pos| [pos.x(), pos.y(), pos.z()]) + .collect::>(); explosion.interact_with_blocks(&mut affected, &mut random); - let callbacks = format_positions(&affected); - let (insertion_digest, expected) = expected.split_once('\n').expect("capture insertion digest"); + let callbacks = affected + .iter() + .map(|pos| [pos.x(), pos.y(), pos.z()]) + .collect::>(); if recorded_rays { let insertions = format_positions(&calculator.0.lock()); let mut digest = String::new(); for byte in Sha256::digest(insertions.as_bytes()) { write!(digest, "{byte:02x}").expect("string write"); } - assert_eq!(format!("insertions-sha256 {digest}"), insertion_digest); + assert_eq!(digest, expected.insertions_sha256); } - let (expected_order, expected) = expected - .split_once("fuses\n") - .expect("capture fuse section"); - assert_eq!( - format!("iteration\n{iteration}callbacks\n{callbacks}"), - expected_order - ); - let (expected_fuses, expected_survivors) = expected - .split_once("surviving-tnt\n") - .expect("capture survivor section"); - assert_tnt_priming(world, &affected, &tnt, expected_fuses); + assert_eq!(iteration, expected.iteration); + assert_eq!(callbacks, expected.callbacks); + assert_tnt_priming(world, &affected, &tnt, &expected.primed_tnt); let survivors = tnt .into_iter() .filter(|&pos| world.get_block_state(pos).get_block() == &vanilla_blocks::TNT) + .map(|pos| [pos.x(), pos.y(), pos.z()]) .collect::>(); - assert_eq!(format_positions(&survivors), expected_survivors); + assert_eq!(survivors, expected.surviving_tnt); } fn assert_tnt_priming( world: &Arc, affected: &[BlockPos], tnt: &[BlockPos], - expected_fuses: &str, + expected_tnt: &[CapturedPrimedTnt], ) { + // Live TNT uses runtime RNG; compare fuse bounds and priming order. let mut primed_positions = Vec::new(); for entity in world.get_entities_in_aabb(&WorldAabb::new(-16.0, 52.0, -16.0, 32.0, 81.0, 32.0)) { @@ -149,19 +181,22 @@ fn assert_tnt_priming( .collect::>(), "TNT must be primed in callback order" ); - let mut actual_positions = format_positions(&primed_positions) - .lines() - .map(str::to_owned) - .collect::>(); - actual_positions.sort(); - let expected_positions = expected_fuses - .lines() - .map(|line| line.rsplit_once(' ').expect("captured fuse").0) + let expected_positions = expected_tnt + .iter() + .map(|captured| { + assert!( + (10..30).contains(&captured.fuse), + "captured vanilla fuse={}", + captured.fuse + ); + let [x, y, z] = captured.position; + BlockPos::new(x, y, z) + }) .collect::>(); - assert_eq!(actual_positions, expected_positions); + assert_eq!(primed_positions, expected_positions); } -fn populate_fixture(world: &Arc) -> [BlockPos; 3] { +fn populate_fixture(world: &Arc, tnt_positions: &[[i32; 3]]) -> Vec { for x in -1..=1 { for z in -1..=1 { insert_ready_full_chunk(world, ChunkPos::new(x, z)); @@ -180,11 +215,10 @@ fn populate_fixture(world: &Arc) -> [BlockPos; 3] { } } } - let tnt = [ - BlockPos::new(6, 65, 9), - BlockPos::new(10, 65, 5), - BlockPos::new(8, 65, 7), - ]; + let tnt = tnt_positions + .iter() + .map(|&[x, y, z]| BlockPos::new(x, y, z)) + .collect::>(); for &pos in &tnt { assert!(world.set_block( pos, diff --git a/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt b/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt deleted file mode 100644 index fad156d9288..00000000000 --- a/steel-core/src/world/explosion/server/block_rays/tests/vanilla-tnt-order.txt +++ /dev/null @@ -1,160 +0,0 @@ -insertions-sha256 69372ac012665fd6c906d8a602e817de12ee5e6ba2293f64d27a0876344300e5 -iteration -5 66 10 -3 66 12 -7 66 8 -9 66 6 -11 66 4 -4 66 12 -6 66 10 -8 66 8 -10 66 6 -12 66 4 -5 66 12 -7 66 10 -9 66 8 -11 66 6 -5 67 10 -7 67 8 -9 67 6 -11 67 4 -4 67 12 -6 67 10 -8 67 8 -10 67 6 -12 67 4 -6 65 9 -9 67 8 -4 65 11 -5 67 12 -8 65 7 -7 67 10 -10 65 5 -11 67 6 -7 65 9 -5 65 11 -9 65 7 -11 65 5 -6 65 11 -8 65 9 -10 65 7 -12 65 5 -4 66 11 -6 66 9 -8 66 7 -10 66 5 -5 66 11 -7 66 9 -9 66 7 -11 66 5 -6 66 11 -4 66 13 -8 66 9 -10 66 7 -12 66 5 -4 67 11 -6 67 9 -8 67 7 -10 67 5 -5 67 11 -7 67 9 -9 67 7 -11 67 5 -7 65 8 -5 65 10 -6 67 11 -9 65 6 -8 67 9 -10 67 7 -11 65 4 -12 67 5 -8 65 8 -6 65 10 -4 65 12 -10 65 6 -7 65 10 -5 65 12 -9 65 8 -11 65 6 -callbacks -9 65 6 -8 67 9 -8 65 8 -4 65 11 -9 66 8 -11 66 5 -9 66 6 -5 66 12 -7 67 9 -10 66 7 -6 66 9 -6 65 10 -8 65 7 -5 65 11 -10 65 5 -12 66 5 -3 66 12 -9 65 8 -10 67 5 -11 65 5 -4 67 11 -4 66 12 -7 67 10 -8 66 7 -7 67 8 -12 65 5 -4 66 13 -10 66 6 -9 67 6 -11 67 6 -5 67 11 -8 67 8 -7 66 8 -5 65 12 -6 65 11 -7 66 10 -6 65 9 -9 65 7 -8 66 9 -10 67 6 -5 66 10 -4 65 12 -9 66 7 -12 67 5 -8 65 9 -10 65 6 -6 67 9 -6 67 10 -11 65 6 -9 67 8 -8 66 8 -6 66 10 -10 67 7 -7 66 9 -10 66 5 -11 66 4 -5 67 10 -9 67 7 -7 65 9 -5 67 12 -11 67 4 -5 65 10 -6 66 11 -8 67 7 -12 67 4 -6 67 11 -5 66 11 -4 66 11 -11 67 5 -11 65 4 -10 65 7 -11 66 6 -12 66 4 -4 67 12 -7 65 8 -7 65 10 -fuses -10 65 5 25 -6 65 9 22 -8 65 7 20 -surviving-tnt diff --git a/steel-core/test_assets/vanilla_tnt_order.json b/steel-core/test_assets/vanilla_tnt_order.json new file mode 100644 index 00000000000..78745d654d1 --- /dev/null +++ b/steel-core/test_assets/vanilla_tnt_order.json @@ -0,0 +1,826 @@ +{ + "minecraft_version": "26.3", + "java_feature_version": 25, + "java_runtime_version": "25.0.4.1+1", + "random_seed": 1, + "explosion_radius": 4.0, + "source_position": [ + 8.5, + 65.0, + 8.5 + ], + "explosion_center": [ + 8.5, + 65.0612500011921, + 8.5 + ], + "tnt_positions": [ + [ + 6, + 65, + 9 + ], + [ + 10, + 65, + 5 + ], + [ + 8, + 65, + 7 + ] + ], + "insertions_sha256": "69372ac012665fd6c906d8a602e817de12ee5e6ba2293f64d27a0876344300e5", + "iteration": [ + [ + 5, + 66, + 10 + ], + [ + 3, + 66, + 12 + ], + [ + 7, + 66, + 8 + ], + [ + 9, + 66, + 6 + ], + [ + 11, + 66, + 4 + ], + [ + 4, + 66, + 12 + ], + [ + 6, + 66, + 10 + ], + [ + 8, + 66, + 8 + ], + [ + 10, + 66, + 6 + ], + [ + 12, + 66, + 4 + ], + [ + 5, + 66, + 12 + ], + [ + 7, + 66, + 10 + ], + [ + 9, + 66, + 8 + ], + [ + 11, + 66, + 6 + ], + [ + 5, + 67, + 10 + ], + [ + 7, + 67, + 8 + ], + [ + 9, + 67, + 6 + ], + [ + 11, + 67, + 4 + ], + [ + 4, + 67, + 12 + ], + [ + 6, + 67, + 10 + ], + [ + 8, + 67, + 8 + ], + [ + 10, + 67, + 6 + ], + [ + 12, + 67, + 4 + ], + [ + 6, + 65, + 9 + ], + [ + 9, + 67, + 8 + ], + [ + 4, + 65, + 11 + ], + [ + 5, + 67, + 12 + ], + [ + 8, + 65, + 7 + ], + [ + 7, + 67, + 10 + ], + [ + 10, + 65, + 5 + ], + [ + 11, + 67, + 6 + ], + [ + 7, + 65, + 9 + ], + [ + 5, + 65, + 11 + ], + [ + 9, + 65, + 7 + ], + [ + 11, + 65, + 5 + ], + [ + 6, + 65, + 11 + ], + [ + 8, + 65, + 9 + ], + [ + 10, + 65, + 7 + ], + [ + 12, + 65, + 5 + ], + [ + 4, + 66, + 11 + ], + [ + 6, + 66, + 9 + ], + [ + 8, + 66, + 7 + ], + [ + 10, + 66, + 5 + ], + [ + 5, + 66, + 11 + ], + [ + 7, + 66, + 9 + ], + [ + 9, + 66, + 7 + ], + [ + 11, + 66, + 5 + ], + [ + 6, + 66, + 11 + ], + [ + 4, + 66, + 13 + ], + [ + 8, + 66, + 9 + ], + [ + 10, + 66, + 7 + ], + [ + 12, + 66, + 5 + ], + [ + 4, + 67, + 11 + ], + [ + 6, + 67, + 9 + ], + [ + 8, + 67, + 7 + ], + [ + 10, + 67, + 5 + ], + [ + 5, + 67, + 11 + ], + [ + 7, + 67, + 9 + ], + [ + 9, + 67, + 7 + ], + [ + 11, + 67, + 5 + ], + [ + 7, + 65, + 8 + ], + [ + 5, + 65, + 10 + ], + [ + 6, + 67, + 11 + ], + [ + 9, + 65, + 6 + ], + [ + 8, + 67, + 9 + ], + [ + 10, + 67, + 7 + ], + [ + 11, + 65, + 4 + ], + [ + 12, + 67, + 5 + ], + [ + 8, + 65, + 8 + ], + [ + 6, + 65, + 10 + ], + [ + 4, + 65, + 12 + ], + [ + 10, + 65, + 6 + ], + [ + 7, + 65, + 10 + ], + [ + 5, + 65, + 12 + ], + [ + 9, + 65, + 8 + ], + [ + 11, + 65, + 6 + ] + ], + "callbacks": [ + [ + 9, + 65, + 6 + ], + [ + 8, + 67, + 9 + ], + [ + 8, + 65, + 8 + ], + [ + 4, + 65, + 11 + ], + [ + 9, + 66, + 8 + ], + [ + 11, + 66, + 5 + ], + [ + 9, + 66, + 6 + ], + [ + 5, + 66, + 12 + ], + [ + 7, + 67, + 9 + ], + [ + 10, + 66, + 7 + ], + [ + 6, + 66, + 9 + ], + [ + 6, + 65, + 10 + ], + [ + 8, + 65, + 7 + ], + [ + 5, + 65, + 11 + ], + [ + 10, + 65, + 5 + ], + [ + 12, + 66, + 5 + ], + [ + 3, + 66, + 12 + ], + [ + 9, + 65, + 8 + ], + [ + 10, + 67, + 5 + ], + [ + 11, + 65, + 5 + ], + [ + 4, + 67, + 11 + ], + [ + 4, + 66, + 12 + ], + [ + 7, + 67, + 10 + ], + [ + 8, + 66, + 7 + ], + [ + 7, + 67, + 8 + ], + [ + 12, + 65, + 5 + ], + [ + 4, + 66, + 13 + ], + [ + 10, + 66, + 6 + ], + [ + 9, + 67, + 6 + ], + [ + 11, + 67, + 6 + ], + [ + 5, + 67, + 11 + ], + [ + 8, + 67, + 8 + ], + [ + 7, + 66, + 8 + ], + [ + 5, + 65, + 12 + ], + [ + 6, + 65, + 11 + ], + [ + 7, + 66, + 10 + ], + [ + 6, + 65, + 9 + ], + [ + 9, + 65, + 7 + ], + [ + 8, + 66, + 9 + ], + [ + 10, + 67, + 6 + ], + [ + 5, + 66, + 10 + ], + [ + 4, + 65, + 12 + ], + [ + 9, + 66, + 7 + ], + [ + 12, + 67, + 5 + ], + [ + 8, + 65, + 9 + ], + [ + 10, + 65, + 6 + ], + [ + 6, + 67, + 9 + ], + [ + 6, + 67, + 10 + ], + [ + 11, + 65, + 6 + ], + [ + 9, + 67, + 8 + ], + [ + 8, + 66, + 8 + ], + [ + 6, + 66, + 10 + ], + [ + 10, + 67, + 7 + ], + [ + 7, + 66, + 9 + ], + [ + 10, + 66, + 5 + ], + [ + 11, + 66, + 4 + ], + [ + 5, + 67, + 10 + ], + [ + 9, + 67, + 7 + ], + [ + 7, + 65, + 9 + ], + [ + 5, + 67, + 12 + ], + [ + 11, + 67, + 4 + ], + [ + 5, + 65, + 10 + ], + [ + 6, + 66, + 11 + ], + [ + 8, + 67, + 7 + ], + [ + 12, + 67, + 4 + ], + [ + 6, + 67, + 11 + ], + [ + 5, + 66, + 11 + ], + [ + 4, + 66, + 11 + ], + [ + 11, + 67, + 5 + ], + [ + 11, + 65, + 4 + ], + [ + 10, + 65, + 7 + ], + [ + 11, + 66, + 6 + ], + [ + 12, + 66, + 4 + ], + [ + 4, + 67, + 12 + ], + [ + 7, + 65, + 8 + ], + [ + 7, + 65, + 10 + ] + ], + "primed_tnt": [ + { + "position": [ + 8, + 65, + 7 + ], + "fuse": 20 + }, + { + "position": [ + 10, + 65, + 5 + ], + "fuse": 25 + }, + { + "position": [ + 6, + 65, + 9 + ], + "fuse": 22 + } + ], + "surviving_tnt": [] +} From 1f83186782090a43442198f26cd6e5dcf7569b9c Mon Sep 17 00:00:00 2001 From: coco875 <59367621+coco875@users.noreply.github.com> Date: Mon, 26 Oct 2026 04:13:20 +0100 Subject: [PATCH 17/26] fix warning --- steel-core/src/java_block_pos_set/mod.rs | 3 ++- .../src/world/explosion/server/block_rays/tests/gameplay.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/steel-core/src/java_block_pos_set/mod.rs b/steel-core/src/java_block_pos_set/mod.rs index 4735ecd11f8..82f6e1d5af4 100644 --- a/steel-core/src/java_block_pos_set/mod.rs +++ b/steel-core/src/java_block_pos_set/mod.rs @@ -5,6 +5,7 @@ //! existing deterministic policies: coordinate ordering for worldgen, and insertion //! indices for explosions (matching increasing JVM identities). +use std::iter::from_fn; use std::mem; use std::vec::IntoIter; @@ -153,7 +154,7 @@ impl JavaBlockPosSet { pub(crate) fn java_order(&self) -> impl Iterator { self.buckets.iter().flat_map(move |bucket| { let mut current = bucket.head; - std::iter::from_fn(move || { + from_fn(move || { if current == EMPTY { return None; } diff --git a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs index a4540fb1d1b..d8978a8dd80 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests/gameplay.rs @@ -1,4 +1,4 @@ -//! TNT ray order, block callbacks, and priming checked against SteelExtractor output. +//! TNT ray order, block callbacks, and priming checked against `SteelExtractor` output. use std::collections::BTreeSet; use std::fmt::Write as _; From f9f04d89a9207f5117af702c295bb1c5a2a7abc2 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Tue, 27 Oct 2026 01:17:02 +0100 Subject: [PATCH 18/26] fixes from claude --- .../src/behavior/blocks/redstone/tnt_block.rs | 38 ++++++++++++++----- .../blocks/redstone/tnt_block/tests.rs | 26 +++++++++++++ steel-core/src/entity/base/mod.rs | 13 +++++++ .../entities/objects/explosives/primed_tnt.rs | 4 ++ .../objects/explosives/primed_tnt/tests.rs | 23 +++++++++++ .../entities/objects/items/falling_block.rs | 3 ++ steel-core/src/entity/movement_sync.rs | 4 ++ steel-core/src/entity/tracker/mod.rs | 1 + 8 files changed, 103 insertions(+), 9 deletions(-) diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block.rs b/steel-core/src/behavior/blocks/redstone/tnt_block.rs index 7d8d774e545..0a36662945a 100644 --- a/steel-core/src/behavior/blocks/redstone/tnt_block.rs +++ b/steel-core/src/behavior/blocks/redstone/tnt_block.rs @@ -55,14 +55,32 @@ impl TntBlock { )) } - /// Primes TNT at `pos`, returning whether a primed entity was added to the world. + /// Primes TNT at `pos` without a source, returning whether a primed entity was added. /// /// This is crate-visible so shared fire burn ticking can call the same entry point when that /// foundation is implemented. - pub(crate) fn prime(world: &Arc, pos: BlockPos, owner: Option<&dyn Entity>) -> bool { + pub(crate) fn prime(world: &Arc, pos: BlockPos) -> bool { + Self::prime_with_source(world, pos, None, || false) + } + + /// Primes TNT for `owner`. An Adventure-mode player also needs `can_break_in_adventure`, + /// which checks the igniting stack's `can_break` predicate. + fn prime_with_source( + world: &Arc, + pos: BlockPos, + owner: Option<&dyn Entity>, + can_break_in_adventure: impl FnOnce() -> bool, + ) -> bool { if !world.get_game_rule(&TNT_EXPLODES) { return false; } + if owner + .and_then(|owner| owner.as_player()) + .is_some_and(|player| player.game_mode() == GameType::Adventure) + && !can_break_in_adventure() + { + return false; + } let entity = Self::primed_entity(world, pos, owner); let position = entity.position(); @@ -108,7 +126,7 @@ impl BlockBehavior for TntBlock { ) { if old_state.get_block() != state.get_block() && world.has_neighbor_signal(pos) - && Self::prime(world, pos, None) + && Self::prime(world, pos) { world.remove_block(pos, false); } @@ -122,7 +140,7 @@ impl BlockBehavior for TntBlock { _source_block: BlockRef, _moved_by_piston: bool, ) { - if world.has_neighbor_signal(pos) && Self::prime(world, pos, None) { + if world.has_neighbor_signal(pos) && Self::prime(world, pos) { world.remove_block(pos, false); } } @@ -135,7 +153,7 @@ impl BlockBehavior for TntBlock { player: &Player, ) -> BlockStateId { if !player.has_infinite_materials() && state.get_value(&BlockStateProperties::UNSTABLE) { - Self::prime(world, pos, None); + Self::prime(world, pos); } state } @@ -178,9 +196,9 @@ impl BlockBehavior for TntBlock { return InteractionResult::TryEmptyHandInteraction; } - let can_ignite = player.game_mode() != GameType::Adventure - || inv.with_item(|item| can_break(item, world, pos)); - if !can_ignite || !Self::prime(world, pos, Some(player)) { + if !Self::prime_with_source(world, pos, Some(player), || { + inv.with_item(|item| can_break(item, world, pos)) + }) { if !world.get_game_rule(&TNT_EXPLODES) { Self::send_disabled_message(player); return InteractionResult::Pass; @@ -220,7 +238,9 @@ impl BlockBehavior for TntBlock { let owner = projectile.get_owner(); let living_owner = owner.as_deref().filter(|owner| owner.is_living_entity()); - if Self::prime(world, hit.block_pos, living_owner) { + // TODO: Check an arrow's pickup stack once arrows exist. Other projectiles use an empty + // stack, which never permits Adventure-mode breaking. + if Self::prime_with_source(world, hit.block_pos, living_owner, || false) { world.remove_block(hit.block_pos, false); } } diff --git a/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs b/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs index c3ec3320108..6aa2930febf 100644 --- a/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs +++ b/steel-core/src/behavior/blocks/redstone/tnt_block/tests.rs @@ -8,6 +8,32 @@ use crate::player::ResetReason; use crate::test_support::{TestPlayerBuilder, fresh_test_world, insert_ready_full_chunk}; use crate::world::{ExplosionInteraction, ExplosionOptions}; +#[test] +fn adventure_owner_primes_only_when_the_igniting_stack_can_break_tnt() { + init_vanilla_registry(); + init_behaviors(); + let fixture = fresh_test_world("tnt_adventure_priming"); + let world = &fixture.world; + let pos = BlockPos::new(8, 64, 8); + insert_ready_full_chunk(world, ChunkPos::from_block_pos(pos)); + let owner = + TestPlayerBuilder::new(Arc::clone(world), "AdventureOwner", next_entity_id()).build(); + owner.restore_game_modes(GameType::Adventure, None); + + assert!(!TntBlock::prime_with_source( + world, + pos, + Some(&*owner), + || false + )); + assert!(TntBlock::prime_with_source( + world, + pos, + Some(&*owner), + || true + )); +} + #[test] fn explosion_replaces_tnt_with_a_short_fuse_entity_owned_by_the_igniter() { init_vanilla_registry(); diff --git a/steel-core/src/entity/base/mod.rs b/steel-core/src/entity/base/mod.rs index 44ea2d9abba..ab4f0a3fc9e 100644 --- a/steel-core/src/entity/base/mod.rs +++ b/steel-core/src/entity/base/mod.rs @@ -237,6 +237,7 @@ pub struct EntityBaseState { no_physics: bool, needs_velocity_sync: bool, hurt_marked: bool, + requires_precise_position: bool, } impl EntityBaseState { @@ -270,6 +271,7 @@ impl EntityBaseState { no_physics: false, needs_velocity_sync: false, hurt_marked: false, + requires_precise_position: false, } } @@ -782,6 +784,17 @@ impl EntityBase { self.state.lock().hurt_marked } + /// Returns true when tracker movement sync must send a full position packet. + #[inline] + pub fn requires_precise_position(&self) -> bool { + self.state.lock().requires_precise_position + } + + /// Sets whether tracker movement sync must send a full position packet. + pub fn set_requires_precise_position(&self, requires_precise_position: bool) { + self.state.lock().requires_precise_position = requires_precise_position; + } + /// Gets the world this entity is in. /// /// Returns `None` if the world has been dropped. diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs index 44b0aaa6d86..657541b08b6 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt.rs @@ -303,6 +303,10 @@ impl Entity for PrimedTntEntity { self.refresh_fluid_contact_with_currents(); self.base.reset_fall_distance_in_water(); } + + self.base.set_requires_precise_position( + self.horizontal_collision() || self.vertical_collision(), + ); } fn blocks_building(&self) -> bool { diff --git a/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs index 4f0603175df..6e8b0b5cece 100644 --- a/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs +++ b/steel-core/src/entity/entities/objects/explosives/primed_tnt/tests.rs @@ -166,6 +166,29 @@ fn flowing_water_pushes_primed_tnt_trajectory() { assert!(entity.position().x > test_tnt_position().x); } +#[test] +fn colliding_tnt_requests_precise_position_sync_only_while_colliding() { + let fixture = ready_tnt_world("primed_tnt_precise_position"); + let world = &fixture.world; + assert!(world.set_block( + TEST_TNT_BLOCK_POS.below(), + vanilla_blocks::STONE.default_state(), + UpdateFlags::UPDATE_NONE, + )); + let entity = primed_tnt(world, test_tnt_position()); + entity.set_velocity(DVec3::new(0.0, -0.1, 0.0)); + + Arc::clone(&entity).tick(); + assert!(entity.base().requires_precise_position()); + + entity + .base() + .set_position_local(test_tnt_position() + DVec3::new(0.0, 4.0, 0.0)); + entity.set_velocity(DVec3::ZERO); + Arc::clone(&entity).tick(); + assert!(!entity.base().requires_precise_position()); +} + #[test] fn teleported_tnt_preserves_nether_portal_but_explodes_other_blocks() { let fixture = ready_tnt_world("primed_tnt_portal_explosion"); diff --git a/steel-core/src/entity/entities/objects/items/falling_block.rs b/steel-core/src/entity/entities/objects/items/falling_block.rs index fc6fa8e8081..d95d236dc86 100644 --- a/steel-core/src/entity/entities/objects/items/falling_block.rs +++ b/steel-core/src/entity/entities/objects/items/falling_block.rs @@ -412,6 +412,9 @@ impl Entity for FallingBlockEntity { self.tick_server(&world, block); } self.set_velocity(self.velocity() * DEFAULT_AIR_DRAG); + self.base.set_requires_precise_position( + self.horizontal_collision() || self.vertical_collision(), + ); } fn get_default_gravity(&self) -> f64 { diff --git a/steel-core/src/entity/movement_sync.rs b/steel-core/src/entity/movement_sync.rs index a91220817cc..24f383ce3b7 100644 --- a/steel-core/src/entity/movement_sync.rs +++ b/steel-core/src/entity/movement_sync.rs @@ -488,6 +488,8 @@ pub struct ServerEntityMovementSyncUpdate { pub vertical_collision: bool, /// whether the latest movement collided horizontally pub horizontal_collision: bool, + /// Whether the entity requested a full-precision position sync. + pub requires_precise_position: bool, /// Entity network id. pub entity_id: i32, /// Whether the entity is currently riding another entity. @@ -655,6 +657,7 @@ impl ServerEntityMovementSyncState { && packed_delta.is_some_and(|delta| update.is_full_precision_encoding_required(delta)); let force_full = delta_too_big + || update.requires_precise_position || self.teleport_delay > FORCED_TELEPORT_PERIOD || self.was_riding || self.was_on_ground != update.on_ground; @@ -1089,6 +1092,7 @@ mod tests { entity_type: &vanilla_entities::PIG, vertical_collision: false, horizontal_collision: false, + requires_precise_position: false, entity_id: 12, is_passenger: false, position, diff --git a/steel-core/src/entity/tracker/mod.rs b/steel-core/src/entity/tracker/mod.rs index 10759d689a6..f5eaba7ae90 100644 --- a/steel-core/src/entity/tracker/mod.rs +++ b/steel-core/src/entity/tracker/mod.rs @@ -392,6 +392,7 @@ impl EntityTracker { entity_type: entity.entity_type(), vertical_collision: entity.vertical_collision(), horizontal_collision: entity.horizontal_collision(), + requires_precise_position: entity.base().requires_precise_position(), entity_id, is_passenger: entity.is_passenger(), position: entity.position(), From 3b0b1ba99cd92e4748c712f136385348acb2dedf Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Wed, 28 Oct 2026 02:07:00 +0100 Subject: [PATCH 19/26] I think for big explosions every microsecond counts, so here I was able to reduce the encoding cutting encodes from ~50,000 to ~1,000 for 50 players and 1000 TNT, though each gain is on the order of microseconds --- steel-core/src/world/explosion/mod.rs | 42 ++++++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/steel-core/src/world/explosion/mod.rs b/steel-core/src/world/explosion/mod.rs index e17fd0cfc3f..64e47e3a74d 100644 --- a/steel-core/src/world/explosion/mod.rs +++ b/steel-core/src/world/explosion/mod.rs @@ -3,7 +3,9 @@ use std::sync::Arc; use glam::DVec3; +use steel_protocol::packet_traits::EncodedPacket; use steel_protocol::packets::game::{CExplode, ExplosionParticleInfo, ExplosionParticlePalette}; +use steel_protocol::utils::ConnectionProtocol; use steel_registry::blocks::block_state_ext::BlockStateExt as _; use steel_registry::fluid::FluidState; use steel_registry::game_rules::GameRule; @@ -18,6 +20,7 @@ use steel_utils::{BlockPos, BlockStateId}; use crate::behavior::FLUID_BEHAVIORS; use crate::entity::damage::DamageSource; use crate::entity::{Entity, SharedEntity}; +use crate::player::connection::NetworkConnection as _; use crate::world::World; use server::ServerExplosion; @@ -453,18 +456,35 @@ impl World { .is_none_or(|source| !source.is_silent()); let packet_block_count = i32::try_from(affected_block_count).unwrap_or(i32::MAX); + let packet = |player_knockback| CExplode { + center: options.center, + radius: options.radius, + block_count: packet_block_count, + player_knockback, + explosion_particle: explosion_particle.clone(), + explosion_sound: options.explosion_sound.clone(), + block_particles: options.block_particles.clone(), + play_sound, + }; + // Players without knockback receive identical bytes, so that packet is encoded once. + let mut shared = None; self.players.iter_players(|_, player| { - if player.position().distance_squared(options.center) < MAX_PACKET_DISTANCE_SQUARED { - player.send_packet(CExplode { - center: options.center, - radius: options.radius, - block_count: packet_block_count, - player_knockback: explosion.hit_players.get(&player.id()).copied(), - explosion_particle: explosion_particle.clone(), - explosion_sound: options.explosion_sound.clone(), - block_particles: options.block_particles.clone(), - play_sound, - }); + if player.position().distance_squared(options.center) >= MAX_PACKET_DISTANCE_SQUARED { + return true; + } + if let Some(&knockback) = explosion.hit_players.get(&player.id()) { + player.send_packet(packet(Some(knockback))); + return true; + } + let encoded = shared.get_or_insert_with(|| { + EncodedPacket::from_bare(packet(None), self.compression, ConnectionProtocol::Play) + .inspect_err(|error| { + tracing::warn!(?error, "Failed to encode explosion packet"); + }) + .ok() + }); + if let Some(encoded) = encoded { + player.connection.send_encoded(encoded.clone()); } true }); From 961df4edfce14d37d8863c81d8326a5905afd020 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 01:03:08 +0100 Subject: [PATCH 20/26] add todo --- steel-core/src/world/explosion/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/steel-core/src/world/explosion/mod.rs b/steel-core/src/world/explosion/mod.rs index 64e47e3a74d..d702289dbd1 100644 --- a/steel-core/src/world/explosion/mod.rs +++ b/steel-core/src/world/explosion/mod.rs @@ -494,6 +494,7 @@ impl World { } } + // TODO: Add trigger branch with windcharges fn resolve_block_interaction(&self, interaction: ExplosionInteraction) -> BlockInteraction { match interaction { ExplosionInteraction::None => BlockInteraction::Keep, From 4726dff25e91bf69f50849abb491d07434f7b66f Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 01:36:11 +0100 Subject: [PATCH 21/26] some inlining --- steel-core/src/world/explosion/server/block_rays.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs index b5b2772c9be..52dda2c8243 100644 --- a/steel-core/src/world/explosion/server/block_rays.rs +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -260,6 +260,7 @@ impl ServerExplosion<'_> { } } +#[inline(always)] fn ray_direction(xx: i32, yy: i32, zz: i32) -> DVec3 { let mut xd = ray_direction_component(xx); let mut yd = ray_direction_component(yy); @@ -271,15 +272,18 @@ fn ray_direction(xx: i32, yy: i32, zz: i32) -> DVec3 { DVec3::new(xd, yd, zd) } +#[inline(always)] fn ray_direction_component(index: i32) -> f64 { f64::from(index as f32 / RAY_GRID_LAST_INDEX as f32 * 2.0 - 1.0) } -fn initial_ray_power(radius: f32, random: f32) -> f32 { +#[inline(always)] +const fn initial_ray_power(radius: f32, random: f32) -> f32 { radius * (INITIAL_RAY_POWER_BASE + random * INITIAL_RAY_POWER_RANDOM_SCALE) } -fn ray_power_loss_from_resistance(resistance: f32) -> f32 { +#[inline(always)] +const fn ray_power_loss_from_resistance(resistance: f32) -> f32 { (resistance + RESISTANCE_POWER_OFFSET) * RESISTANCE_POWER_SCALE } From 16f6bb80f0f21194e2b97078d850c8f49732a8f4 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 01:43:21 +0100 Subject: [PATCH 22/26] more const --- steel-core/src/world/explosion/server/exposure.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steel-core/src/world/explosion/server/exposure.rs b/steel-core/src/world/explosion/server/exposure.rs index 85cb62ca6f6..6ac06ea5336 100644 --- a/steel-core/src/world/explosion/server/exposure.rs +++ b/steel-core/src/world/explosion/server/exposure.rs @@ -195,7 +195,7 @@ impl EntityExplosionExposure { } } -fn exposure_axis_step(axis_length: f64) -> f64 { +const fn exposure_axis_step(axis_length: f64) -> f64 { 1.0 / (axis_length * EXPOSURE_SAMPLE_DENSITY + 1.0) } From 61f14b731fe05b9b81de75fdb7532d0a34d1bbed Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 01:57:39 +0100 Subject: [PATCH 23/26] remove 26.2 comments --- steel-core/src/inventory/fuel_values.rs | 2 +- steel-core/src/world/explosion/server/block_rays/tests.rs | 2 +- steel-core/src/world/raycast.rs | 4 ++-- steel-registry/src/vanilla_ticket_types.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/steel-core/src/inventory/fuel_values.rs b/steel-core/src/inventory/fuel_values.rs index bf4718751b5..fcf652df1d0 100644 --- a/steel-core/src/inventory/fuel_values.rs +++ b/steel-core/src/inventory/fuel_values.rs @@ -80,7 +80,7 @@ impl FuelValuesBuilder { } } -/// Vanilla 26.2 fuel values, built from the generated item registry and tags. +/// Vanilla fuel values, built from the generated item registry and tags. pub static VANILLA_FUEL_VALUES: LazyLock = LazyLock::new(|| { const BASE: i32 = 200; diff --git a/steel-core/src/world/explosion/server/block_rays/tests.rs b/steel-core/src/world/explosion/server/block_rays/tests.rs index 14dcc32e9a5..9c2b671ac81 100644 --- a/steel-core/src/world/explosion/server/block_rays/tests.rs +++ b/steel-core/src/world/explosion/server/block_rays/tests.rs @@ -192,7 +192,7 @@ fn precomputed_ray_steps_match_java_bit_digest() { } } - // Produced by the Minecraft 26.2 ServerExplosion expression under OpenJDK 25. + // Produced by the Minecraft ServerExplosion expression under OpenJDK 25. assert_eq!(digest, EXPECTED_RAY_STEPS_FNV1A_64); } diff --git a/steel-core/src/world/raycast.rs b/steel-core/src/world/raycast.rs index 49ec6655c95..54d5d86aea6 100644 --- a/steel-core/src/world/raycast.rs +++ b/steel-core/src/world/raycast.rs @@ -1162,7 +1162,7 @@ impl World { result } - /// Mirrors Minecraft 26.2 `AABB.getDirection` for one block-local shape box. + /// Mirrors Minecraft `AABB.getDirection` for one block-local shape box. pub(super) fn intersects_aabb_with_t( start: DVec3, end: DVec3, @@ -1433,7 +1433,7 @@ mod voxel_shape_clip_tests { fn voxel_shape_clip_matches_java_full_cube_at_exact_opposite_boundaries() { let shape = OffsetVoxelShape::without_offset(VoxelShape::FULL_BLOCK); - // Minecraft 26.2's VoxelShape.clip probes 0.001 along the segment. For a + // Minecraft VoxelShape.clip probes 0.001 along the segment. For a // CubeVoxelShape the resulting x=1 index is outside, while x=0 is inside. assert!( World::clip_shape( diff --git a/steel-registry/src/vanilla_ticket_types.rs b/steel-registry/src/vanilla_ticket_types.rs index 18d1c56a9f7..3d06278d9d4 100644 --- a/steel-registry/src/vanilla_ticket_types.rs +++ b/steel-registry/src/vanilla_ticket_types.rs @@ -1,4 +1,4 @@ -//! Ticket types hardcoded by vanilla 26.2 in `TicketType.java`. +//! Ticket types hardcoded by vanilla in `TicketType.java`. use steel_utils::Identifier; From f63340d0e627a54cdce5f3aa08e701634bba5a29 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 02:01:20 +0100 Subject: [PATCH 24/26] use alias, now consistance --- steel-core/src/entity/ticking.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/steel-core/src/entity/ticking.rs b/steel-core/src/entity/ticking.rs index f558ec9fdd4..c823acb7717 100644 --- a/steel-core/src/entity/ticking.rs +++ b/steel-core/src/entity/ticking.rs @@ -7,7 +7,7 @@ use smallvec::SmallVec; use super::{Entity, SharedEntity}; /// Cycle-detection stack sized for the shallow passenger chains vanilla produces. -pub(crate) type VehicleChain = SmallVec<[i32; 4]>; +pub(crate) type VehicleChain = SmallVec<[i32; 8]>; /// Snapshots vanilla old position and rotation before an entity tick. pub(crate) fn snapshot_old_pos_and_rot_for_tick(entity: &dyn Entity) { @@ -29,7 +29,7 @@ pub(crate) fn tick_vehicle_passengers_if( return; } - let mut visited = SmallVec::<[i32; 8]>::new(); + let mut visited = VehicleChain::new(); visited.push(vehicle.id()); for passenger in passengers { @@ -42,7 +42,7 @@ fn tick_passenger( entity: &SharedEntity, post_tick: &mut impl FnMut(&SharedEntity), can_tick: &mut impl FnMut(&SharedEntity) -> bool, - visited: &mut SmallVec<[i32; 8]>, + visited: &mut VehicleChain, ) { let entity_id = entity.id(); assert!( From 63e3707009e7c83c331846457d9608f585159326 Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 02:24:26 +0100 Subject: [PATCH 25/26] merge??? --- steel-core/src/entity/entities/objects/items/item_entity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/steel-core/src/entity/entities/objects/items/item_entity.rs b/steel-core/src/entity/entities/objects/items/item_entity.rs index c42b5c78349..ca4fce09013 100644 --- a/steel-core/src/entity/entities/objects/items/item_entity.rs +++ b/steel-core/src/entity/entities/objects/items/item_entity.rs @@ -537,7 +537,7 @@ impl Entity for ItemEntity { fn ignore_explosion(&self, explosion: &dyn Explosion) -> bool { !explosion.should_affect_blocklike_entities() } - + fn movement_emission(&self) -> EntityMovementEmission { EntityMovementEmission::None } From 6b4ac21db298c44cdd3fc235de7ab4d754e1562b Mon Sep 17 00:00:00 2001 From: JunkyDeveloper Date: Fri, 30 Oct 2026 02:31:46 +0100 Subject: [PATCH 26/26] remove my inline... --- steel-core/src/world/explosion/server/block_rays.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/steel-core/src/world/explosion/server/block_rays.rs b/steel-core/src/world/explosion/server/block_rays.rs index 52dda2c8243..38001deb780 100644 --- a/steel-core/src/world/explosion/server/block_rays.rs +++ b/steel-core/src/world/explosion/server/block_rays.rs @@ -260,7 +260,6 @@ impl ServerExplosion<'_> { } } -#[inline(always)] fn ray_direction(xx: i32, yy: i32, zz: i32) -> DVec3 { let mut xd = ray_direction_component(xx); let mut yd = ray_direction_component(yy); @@ -272,17 +271,14 @@ fn ray_direction(xx: i32, yy: i32, zz: i32) -> DVec3 { DVec3::new(xd, yd, zd) } -#[inline(always)] fn ray_direction_component(index: i32) -> f64 { f64::from(index as f32 / RAY_GRID_LAST_INDEX as f32 * 2.0 - 1.0) } -#[inline(always)] const fn initial_ray_power(radius: f32, random: f32) -> f32 { radius * (INITIAL_RAY_POWER_BASE + random * INITIAL_RAY_POWER_RANDOM_SCALE) } -#[inline(always)] const fn ray_power_loss_from_resistance(resistance: f32) -> f32 { (resistance + RESISTANCE_POWER_OFFSET) * RESISTANCE_POWER_SCALE }