diff --git a/perovskite_client/src/cache.rs b/perovskite_client/src/cache.rs
index 5a24799..34cf139 100644
--- a/perovskite_client/src/cache.rs
+++ b/perovskite_client/src/cache.rs
@@ -174,7 +174,7 @@ impl CacheManager {
     fn hash_texture_reference(
         &self,
         tex: Option<&TextureReference>,
-        hasher: &mut sha2::Sha256,
+        hasher: &mut Sha256,
     ) -> Option<()> {
         if let Some(tex) = tex {
             hasher.update(b"tex_ref:");
@@ -195,7 +195,7 @@ impl CacheManager {
     fn hash_cube_render_info(
         &self,
         cube: &perovskite_core::protocol::blocks::CubeRenderInfo,
-        hasher: &mut sha2::Sha256,
+        hasher: &mut Sha256,
     ) -> Option<()> {
         hasher.update(b"cube:");
         hasher.update(cube.render_mode.to_le_bytes());
@@ -212,7 +212,7 @@ impl CacheManager {
     fn hash_aabox_render_info(
         &self,
         aa_boxes: &perovskite_core::protocol::blocks::AxisAlignedBoxes,
-        hasher: &mut sha2::Sha256,
+        hasher: &mut Sha256,
     ) -> Option<()> {
         hasher.update(b"aa_boxes:");
         for aa_box in aa_boxes.boxes.iter() {
@@ -239,7 +239,7 @@ impl CacheManager {
     fn hash_plant_render_info(
         &self,
         plant: &perovskite_core::protocol::blocks::PlantLikeRenderInfo,
-        hasher: &mut sha2::Sha256,
+        hasher: &mut Sha256,
     ) -> Option<()> {
         hasher.update(b"plant_like:");
         hasher.update(plant.wave_effect_scale.to_le_bytes());
@@ -277,7 +277,7 @@ impl CacheManager {
 
 fn get_cache_path(expected_hash: &[u8; 32], cache_dir: &Path) -> PathBuf {
     let hash_hex = hex::encode(expected_hash);
-    assert!(hash_hex.len() == 64);
+    assert_eq!(hash_hex.len(), 64);
     // Security: The hash is untrusted... But we verified its length, and we generate the hex string ourselves.
     let prefix = hash_hex[0..2].to_string();
     let suffix = hash_hex[2..].to_string();
diff --git a/perovskite_client/src/game_state/block_types.rs b/perovskite_client/src/game_state/block_types.rs
index 28fea3c..197d76e 100644
--- a/perovskite_client/src/game_state/block_types.rs
+++ b/perovskite_client/src/game_state/block_types.rs
@@ -12,8 +12,8 @@ use rustc_hash::FxHashMap;
 use super::make_fallback_blockdef;
 
 pub(crate) struct ClientBlockTypeManager {
-    block_defs: Vec<Option<blocks_proto::BlockTypeDef>>,
-    fallback_block_def: blocks_proto::BlockTypeDef,
+    block_defs: Vec<Option<BlockTypeDef>>,
+    fallback_block_def: BlockTypeDef,
     air_block: BlockId,
     light_propagators: bv::BitVec,
     light_emitters: Vec<u8>,
@@ -23,9 +23,7 @@ pub(crate) struct ClientBlockTypeManager {
     name_to_id: FxHashMap<String, BlockId>,
 }
 impl ClientBlockTypeManager {
-    pub(crate) fn new(
-        server_defs: Vec<blocks_proto::BlockTypeDef>,
-    ) -> Result<ClientBlockTypeManager> {
+    pub(crate) fn new(server_defs: Vec<BlockTypeDef>) -> Result<ClientBlockTypeManager> {
         let max_id = server_defs
             .iter()
             .map(|x| x.id)
@@ -126,14 +124,14 @@ impl ClientBlockTypeManager {
         })
     }
 
-    pub(crate) fn all_block_defs(&self) -> impl Iterator<Item = &blocks_proto::BlockTypeDef> {
+    pub(crate) fn all_block_defs(&self) -> impl Iterator<Item = &BlockTypeDef> {
         self.block_defs.iter().flatten()
     }
 
-    pub(crate) fn get_fallback_blockdef(&self) -> &blocks_proto::BlockTypeDef {
+    pub(crate) fn get_fallback_blockdef(&self) -> &BlockTypeDef {
         &self.fallback_block_def
     }
-    pub(crate) fn get_blockdef(&self, id: BlockId) -> Option<&blocks_proto::BlockTypeDef> {
+    pub(crate) fn get_blockdef(&self, id: BlockId) -> Option<&BlockTypeDef> {
         match self.block_defs.get(id.index()) {
             // none if get() failed due to bounds check
             None => None,
diff --git a/perovskite_client/src/game_state/entities.rs b/perovskite_client/src/game_state/entities.rs
index e3ff61f..68f5fe1 100644
--- a/perovskite_client/src/game_state/entities.rs
+++ b/perovskite_client/src/game_state/entities.rs
@@ -132,7 +132,7 @@ impl GameEntity {
                         m.seq,
                         m.start_tick,
                         m.total_time_seconds,
-                        (m.start_tick as f64 / 1_000_000_000.0 - tick as f64 / 1_000_000_000.0)
+                        m.start_tick as f64 / 1_000_000_000.0 - tick as f64 / 1_000_000_000.0
                     )
                 })
                 .collect::<String>()
@@ -307,11 +307,7 @@ impl GameEntity {
         Ok(())
     }
 
-    pub(crate) fn as_transform(
-        &self,
-        base_position: Vector3<f64>,
-        time_tick: u64,
-    ) -> cgmath::Matrix4<f32> {
+    pub(crate) fn as_transform(&self, base_position: Vector3<f64>, time_tick: u64) -> Matrix4<f32> {
         let (pos, face_dir, pitch) = self.position(time_tick);
 
         build_transform(base_position, pos, face_dir, pitch)
@@ -449,7 +445,7 @@ fn build_transform(
     face_dir: Rad<f32>,
     pitch: Rad<f32>,
 ) -> Matrix4<f32> {
-    let translation = cgmath::Matrix4::from_translation(
+    let translation = Matrix4::from_translation(
         (pos - base_position).mul_element_wise(Vector3::new(1., -1., 1.)),
     )
     .cast()
@@ -487,7 +483,7 @@ impl EntityState {
 
         Ok(Self {
             entities: FxHashMap::default(),
-            fallback_entity: VkCgvBufferGpu::from_buffers(&vtx, &idx, &block_renderer.allocator())?
+            fallback_entity: VkCgvBufferGpu::from_buffers(&vtx, &idx, block_renderer.allocator())?
                 .unwrap(),
             attached_to_entity: None,
         })
diff --git a/perovskite_client/src/game_state/mod.rs b/perovskite_client/src/game_state/mod.rs
index 35dd7ea..c07a964 100644
--- a/perovskite_client/src/game_state/mod.rs
+++ b/perovskite_client/src/game_state/mod.rs
@@ -112,7 +112,7 @@ pub(crate) enum GameAction {
     Tap(DigTapAction),
     Place(PlaceAction),
     Inventory(InventoryAction),
-    PopupResponse(perovskite_core::protocol::ui::PopupResponse),
+    PopupResponse(protocol::ui::PopupResponse),
     InteractKey(InteractKeyAction),
     ChatMessage(String),
 }
@@ -123,7 +123,7 @@ pub(crate) struct ChunkManager {
     chunks: parking_lot::RwLock<ChunkMap>,
     renderable_chunks: parking_lot::RwLock<ChunkMap>,
     light_columns: parking_lot::RwLock<LightColumnMap>,
-    mesh_batches: parking_lot::Mutex<(FxHashMap<u64, MeshBatch>, MeshBatchBuilder)>,
+    mesh_batches: Mutex<(FxHashMap<u64, MeshBatch>, MeshBatchBuilder)>,
 }
 impl ChunkManager {
     pub(crate) fn new() -> ChunkManager {
@@ -131,7 +131,7 @@ impl ChunkManager {
             chunks: parking_lot::RwLock::new(FxHashMap::default()),
             renderable_chunks: parking_lot::RwLock::new(FxHashMap::default()),
             light_columns: parking_lot::RwLock::new(FxHashMap::default()),
-            mesh_batches: parking_lot::Mutex::new((FxHashMap::default(), MeshBatchBuilder::new())),
+            mesh_batches: Mutex::new((FxHashMap::default(), MeshBatchBuilder::new())),
         }
     }
     /// Locks the chunk manager and returns a struct that can be used to access chunks in a read/write manner,
@@ -191,7 +191,7 @@ impl ChunkManager {
         proto: protocol::game_rpc::MapChunk,
         snappy_helper: &mut SnappyDecodeHelper,
         block_types: &ClientBlockTypeManager,
-    ) -> anyhow::Result<usize> {
+    ) -> Result<usize> {
         // Lock order: chunks -> [renderable_chunks] -> light_columns
         let mut chunks_lock = {
             let _span = span!("Acquire global chunk lock");
@@ -239,7 +239,7 @@ impl ChunkManager {
             let block_coord: BlockCoordinate = match &update.block_coord {
                 Some(x) => x.into(),
                 None => {
-                    log::warn!("Got delta with missing block_coord {:?}", update);
+                    warn!("Got delta with missing block_coord {:?}", update);
                     missing_coord = true;
                     continue;
                 }
@@ -260,7 +260,7 @@ impl ChunkManager {
                     }
                 }
                 None => {
-                    log::warn!("Got delta for unknown chunk {:?}", block_coord);
+                    warn!("Got delta for unknown chunk {:?}", block_coord);
                     unknown_coords.push(block_coord);
                     None
                 }
@@ -608,11 +608,11 @@ pub(crate) struct ClientState {
 
     pub(crate) block_types: Arc<ClientBlockTypeManager>,
     pub(crate) items: Arc<ClientItemManager>,
-    pub(crate) last_update: parking_lot::Mutex<Instant>,
-    pub(crate) physics_state: parking_lot::Mutex<physics::PhysicsState>,
+    pub(crate) last_update: Mutex<Instant>,
+    pub(crate) physics_state: Mutex<physics::PhysicsState>,
     pub(crate) chunks: ChunkManager,
-    pub(crate) inventories: parking_lot::Mutex<InventoryViewManager>,
-    pub(crate) tool_controller: parking_lot::Mutex<ToolController>,
+    pub(crate) inventories: Mutex<InventoryViewManager>,
+    pub(crate) tool_controller: Mutex<ToolController>,
     pub(crate) shutdown: tokio_util::sync::CancellationToken,
     pub(crate) actions: mpsc::Sender<GameAction>,
     pub(crate) light_cycle: Arc<Mutex<LightCycle>>,
@@ -672,8 +672,8 @@ impl ClientState {
             pending_error: Mutex::new(None),
             wants_exit_from_game: Mutex::new(false),
             last_position_weak: Mutex::new(PlayerPositionUpdate {
-                position: cgmath::Vector3::zero(),
-                velocity: cgmath::Vector3::zero(),
+                position: Vector3::zero(),
+                velocity: Vector3::zero(),
                 face_direction: (0.0, 0.0),
             }),
             timekeeper,
@@ -689,7 +689,7 @@ impl ClientState {
         PlayerPositionUpdate {
             // TODO tick, velocity
             position: lock.pos(),
-            velocity: cgmath::Vector3::zero(),
+            velocity: Vector3::zero(),
             face_direction: lock.angle(),
         }
     }
@@ -757,7 +757,7 @@ impl ClientState {
 
         *self.last_position_weak.lock() = PlayerPositionUpdate {
             position: player_position,
-            velocity: cgmath::Vector3::zero(),
+            velocity: Vector3::zero(),
             face_direction: (az, el),
         };
 
@@ -836,7 +836,7 @@ impl ClientState {
 
 pub(crate) struct FrameState {
     pub(crate) scene_state: SceneState,
-    pub(crate) player_position: cgmath::Vector3<f64>,
+    pub(crate) player_position: Vector3<f64>,
     pub(crate) tool_state: ToolState,
 }
 
diff --git a/perovskite_client/src/game_state/physics.rs b/perovskite_client/src/game_state/physics.rs
index 6fd10cb..570a38a 100644
--- a/perovskite_client/src/game_state/physics.rs
+++ b/perovskite_client/src/game_state/physics.rs
@@ -117,7 +117,7 @@ impl PhysicsMode {
 }
 
 pub(crate) struct PhysicsState {
-    pos: cgmath::Vector3<f64>,
+    pos: Vector3<f64>,
     az: Deg<f64>,
     el: Deg<f64>,
     target_az: Deg<f64>,
@@ -138,7 +138,7 @@ pub(crate) struct PhysicsState {
 impl PhysicsState {
     pub(crate) fn new(settings: Arc<ArcSwap<GameSettings>>) -> Self {
         Self {
-            pos: cgmath::vec3(0., 0., -4.),
+            pos: vec3(0., 0., -4.),
             az: Deg(0.),
             el: Deg(0.),
             target_az: Deg(0.),
@@ -161,7 +161,7 @@ impl PhysicsState {
         client_state: &ClientState,
         _aspect_ratio: f64,
         delta: Duration,
-    ) -> (cgmath::Vector3<f64>, (f64, f64)) {
+    ) -> (Vector3<f64>, (f64, f64)) {
         let mut input = client_state.input.lock();
         if input.take_just_pressed(BoundAction::TogglePhysics) {
             self.physics_mode = self.physics_mode.next(self.can_fly, self.can_noclip);
@@ -193,7 +193,7 @@ impl PhysicsState {
     fn update_standard(
         &mut self,
         input: &mut InputState,
-        delta: std::time::Duration,
+        delta: Duration,
         client_state: &ClientState,
     ) {
         let _span = span!("physics_standard");
@@ -289,7 +289,7 @@ impl PhysicsState {
         bump_height: f64,
         target: Vector3<f64>,
         chunks: ChunkManagerView<'_>,
-        block_types: &std::sync::Arc<ClientBlockTypeManager>,
+        block_types: &Arc<ClientBlockTypeManager>,
         delta_secs: f64,
     ) -> Vector3<f64> {
         let pre_bump_y = new_pos.y;
@@ -422,7 +422,7 @@ impl PhysicsState {
     fn update_flying(
         &mut self,
         input: &mut InputState,
-        delta: std::time::Duration,
+        delta: Duration,
         collisions: bool,
         client_state: &ClientState,
     ) {
@@ -494,7 +494,7 @@ impl PhysicsState {
         (self.az.0, self.el.0)
     }
 
-    fn update_smooth_angles(&mut self, delta: std::time::Duration) {
+    fn update_smooth_angles(&mut self, delta: Duration) {
         let factor =
             ANGLE_SMOOTHING_FACTOR.powf(delta.as_secs_f64() / ANGLE_SMOOTHING_REFERENCE_DELTA);
         self.el = (self.el * factor) + (self.target_el * (1.0 - factor));
@@ -755,10 +755,10 @@ fn push_collision_boxes(
     output: &mut Vec<CollisionBox>,
 ) {
     match &block.physics_info {
-        Some(block_type_def::PhysicsInfo::Solid(_)) => output.push(CollisionBox::full_cube(coord)),
-        Some(block_type_def::PhysicsInfo::Fluid(_)) => {}
-        Some(block_type_def::PhysicsInfo::Air(_)) => {}
-        Some(block_type_def::PhysicsInfo::SolidCustomCollisionboxes(boxes)) => {
+        Some(PhysicsInfo::Solid(_)) => output.push(CollisionBox::full_cube(coord)),
+        Some(PhysicsInfo::Fluid(_)) => {}
+        Some(PhysicsInfo::Air(_)) => {}
+        Some(PhysicsInfo::SolidCustomCollisionboxes(boxes)) => {
             for box_ in &boxes.boxes {
                 if box_.variant_mask == 0 || (variant & box_.variant_mask as u16) != 0 {
                     output.push(CollisionBox::from_aabb(coord, box_, variant));
diff --git a/perovskite_client/src/game_state/tool_controller.rs b/perovskite_client/src/game_state/tool_controller.rs
index f028442..4b589a4 100644
--- a/perovskite_client/src/game_state/tool_controller.rs
+++ b/perovskite_client/src/game_state/tool_controller.rs
@@ -413,4 +413,4 @@ const POINTEE_DISTANCE: f64 = 6.;
 // line_drawing seems to have problems when using Center rather than Corner
 // Fudge it manually
 // TODO file a bug for that crate
-const RAYCAST_FUDGE_VEC: cgmath::Vector3<f64> = cgmath::vec3(0.5, 0.5, 0.5);
+const RAYCAST_FUDGE_VEC: cgmath::Vector3<f64> = vec3(0.5, 0.5, 0.5);
diff --git a/perovskite_client/src/game_ui/egui_ui.rs b/perovskite_client/src/game_ui/egui_ui.rs
index 38307ae..5af0f2e 100644
--- a/perovskite_client/src/game_ui/egui_ui.rs
+++ b/perovskite_client/src/game_ui/egui_ui.rs
@@ -129,7 +129,7 @@ impl EguiUi {
     }
     pub(crate) fn draw_all_uis(
         &mut self,
-        ctx: &egui::Context,
+        ctx: &Context,
         atlas_texture_id: TextureId,
         client_state: &ClientState,
     ) {
@@ -228,7 +228,7 @@ impl EguiUi {
         ui: &mut egui::Ui,
         popup: &PopupDescription,
         element: &proto::UiElement,
-        id: egui::Id,
+        id: Id,
         atlas_texture_id: TextureId,
         client_state: &ClientState,
         clicked_button: &mut Option<(String, bool)>,
@@ -244,9 +244,9 @@ impl EguiUi {
                     .or_insert(text_field.initial.clone());
                 // todo support multiline, other styling
                 let editor = if text_field.multiline {
-                    egui::TextEdit::multiline(value)
+                    TextEdit::multiline(value)
                 } else {
-                    egui::TextEdit::singleline(value)
+                    TextEdit::singleline(value)
                 };
                 ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
                     let label = ui.label(text_field.label.clone());
@@ -262,7 +262,7 @@ impl EguiUi {
                 ui.checkbox(value, checkbox.label.clone());
             }
             Some(proto::ui_element::Element::Button(button_def)) => {
-                let button = egui::Button::new(button_def.label.clone());
+                let button = Button::new(button_def.label.clone());
                 if ui
                     .add_enabled(button_def.enabled && self.allow_button_interaction, button)
                     .clicked()
@@ -280,7 +280,7 @@ impl EguiUi {
                 };
                 egui::CollapsingHeader::new(label)
                     .default_open(true)
-                    .id_source(egui::Id::new("collapsing_header_inv").with(inventory.inventory_key))
+                    .id_source(Id::new("collapsing_header_inv").with(inventory.inventory_key))
                     .show(ui, |ui| {
                         self.draw_inventory_view(
                             ui,
@@ -321,8 +321,8 @@ impl EguiUi {
 
     fn draw_popup(
         &mut self,
-        popup: &proto::PopupDescription,
-        ctx: &egui::Context,
+        popup: &PopupDescription,
+        ctx: &Context,
         atlas_texture_id: TextureId,
         client_state: &ClientState,
         enabled: bool,
@@ -341,7 +341,7 @@ impl EguiUi {
                         ui,
                         popup,
                         element,
-                        egui::Id::new("popup_elem").with(popup.popup_id).with(index),
+                        Id::new("popup_elem").with(popup.popup_id).with(index),
                         atlas_texture_id,
                         client_state,
                         &mut clicked_button,
@@ -491,10 +491,9 @@ impl EguiUi {
             .render
             .scale_inventories_with_high_dpi
         {
-            vec2((frame_pixels.w) as f32, (frame_pixels.h) as f32) * self.scale_override
+            vec2(frame_pixels.w as f32, frame_pixels.h as f32) * self.scale_override
         } else {
-            vec2((frame_pixels.w) as f32, (frame_pixels.h) as f32) * self.scale_override
-                / (self.scale)
+            vec2(frame_pixels.w as f32, frame_pixels.h as f32) * self.scale_override / (self.scale)
         };
         let inv_rect = egui::Rect::from_min_size(
             ui.cursor().min,
@@ -636,22 +635,19 @@ impl EguiUi {
         egui::Rect::from_x_y_ranges(left..=right, top..=bottom)
     }
 
-    pub(crate) fn get_texture_uv(
-        &self,
-        item: &perovskite_core::protocol::items::ItemStack,
-    ) -> egui::Rect {
+    pub(crate) fn get_texture_uv(&self, item: &ItemStack) -> egui::Rect {
         let pixel_rect = get_texture(item, &self.atlas_coords, &self.item_defs);
         self.pixel_rect_to_uv(pixel_rect)
     }
 
-    fn draw_pause_menu(&mut self, ctx: &egui::Context, client_state: &ClientState) {
+    fn draw_pause_menu(&mut self, ctx: &Context, client_state: &ClientState) {
         if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
             self.pause_menu_open = false;
         }
         egui::Window::new("Game paused")
             .collapsible(false)
             .resizable(false)
-            .anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
+            .anchor(egui::Align2::CENTER_CENTER, vec2(0.0, 0.0))
             .show(ctx, |ui| {
                 if ui.add_enabled(true, Button::new("Resume")).clicked() {
                     self.pause_menu_open = false;
@@ -675,7 +671,7 @@ impl EguiUi {
             });
     }
 
-    fn render_chat_history(&mut self, ctx: &egui::Context, client_state: &ClientState) {
+    fn render_chat_history(&mut self, ctx: &Context, client_state: &ClientState) {
         let chat = client_state.chat.lock();
         if self.chat_open {
             egui::TopBottomPanel::top("chat_panel")
@@ -704,7 +700,7 @@ impl EguiUi {
                         }
                     });
                     let editor = ui.add(
-                        egui::TextEdit::singleline(&mut self.chat_message_input)
+                        TextEdit::singleline(&mut self.chat_message_input)
                             .hint_text("Type a message; press Enter to send or Escape to close.")
                             .lock_focus(true)
                             .desired_width(f32::INFINITY),
@@ -716,7 +712,7 @@ impl EguiUi {
                     if self.chat_force_cursor_to_end {
                         self.chat_force_cursor_to_end = false;
                         let id = editor.id;
-                        if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), id) {
+                        if let Some(mut state) = TextEdit::load_state(ui.ctx(), id) {
                             let ccursor =
                                 egui::text::CCursor::new(self.chat_message_input.chars().count());
                             state.set_ccursor_range(Some(egui::text::CCursorRange::one(ccursor)));
@@ -756,7 +752,7 @@ impl EguiUi {
                     .max_height(240.0)
                     .frame(egui::Frame {
                         fill: Color32::from_black_alpha(192),
-                        stroke: egui::Stroke {
+                        stroke: Stroke {
                             width: 0.0,
                             color: Color32::TRANSPARENT,
                         },
@@ -789,7 +785,7 @@ impl EguiUi {
             .max_height(240.0)
             .frame(egui::Frame {
                 fill: Color32::from_black_alpha(192),
-                stroke: egui::Stroke {
+                stroke: Stroke {
                     width: 0.0,
                     color: Color32::TRANSPARENT,
                 },
diff --git a/perovskite_client/src/game_ui/hud.rs b/perovskite_client/src/game_ui/hud.rs
index b245e68..74bb22a 100644
--- a/perovskite_client/src/game_ui/hud.rs
+++ b/perovskite_client/src/game_ui/hud.rs
@@ -77,7 +77,7 @@ impl GameHud {
                 }
             }
             if let Some(x) = slot_selection {
-                if x < total_slots as u32 {
+                if x < total_slots {
                     self.set_slot(x, client_state);
                 }
             }
@@ -133,7 +133,7 @@ impl GameHud {
         ctx: &VulkanWindow,
         window_size: (u32, u32),
     ) -> Result<FlatTextureDrawCall> {
-        let mut builder = flat_texture::FlatTextureDrawBuilder::new();
+        let mut builder = FlatTextureDrawBuilder::new();
         builder.centered_rect(
             (window_size.0 / 2, window_size.1 / 2),
             *self.texture_coords.get(CROSSHAIR).unwrap(),
@@ -149,7 +149,7 @@ impl GameHud {
         window_size: (u32, u32),
         client_state: &ClientState,
     ) -> Result<Option<FlatTextureDrawCall>> {
-        let mut builder = flat_texture::FlatTextureDrawBuilder::new();
+        let mut builder = FlatTextureDrawBuilder::new();
         let unselected_frame = *self.texture_coords.get(FRAME_UNSELECTED).unwrap();
         let selected_frame = *self.texture_coords.get(FRAME_SELECTED).unwrap();
 
diff --git a/perovskite_client/src/game_ui/mod.rs b/perovskite_client/src/game_ui/mod.rs
index bf81acd..6e73c03 100644
--- a/perovskite_client/src/game_ui/mod.rs
+++ b/perovskite_client/src/game_ui/mod.rs
@@ -30,7 +30,7 @@ pub(crate) async fn make_uis(
     cache_manager: &Arc<Mutex<CacheManager>>,
     ctx: Arc<VulkanContext>,
     block_renderer: &BlockRenderer,
-) -> Result<(hud::GameHud, egui_ui::EguiUi)> {
+) -> Result<(GameHud, EguiUi)> {
     let (texture_atlas, texture_coords) =
         build_texture_atlas(&item_defs, cache_manager, ctx, block_renderer).await?;
 
@@ -164,7 +164,7 @@ async fn build_texture_atlas(
         texture_outlines: false,
         force_max_dimensions: false,
     };
-    let mut texture_packer = texture_packer::TexturePacker::new_skyline(config);
+    let mut texture_packer = TexturePacker::new_skyline(config);
 
     pack_tex(
         &mut texture_packer,
diff --git a/perovskite_client/src/net_client/client_context.rs b/perovskite_client/src/net_client/client_context.rs
index e6d01a4..e135b21 100644
--- a/perovskite_client/src/net_client/client_context.rs
+++ b/perovskite_client/src/net_client/client_context.rs
@@ -36,7 +36,7 @@ use super::mesh_worker::{
 struct SharedState {
     protocol_version: u32,
     // Some messages are sent straight from the inbound context, namely protocol bugchecks
-    outbound_tx: mpsc::Sender<rpc::StreamToServer>,
+    outbound_tx: mpsc::Sender<StreamToServer>,
     client_state: Arc<ClientState>,
     ack_map: Mutex<HashMap<u64, Instant>>,
     mesh_workers: Vec<Arc<MeshWorker>>,
@@ -50,7 +50,7 @@ impl SharedState {
     async fn send_bugcheck(&self, description: String) -> Result<()> {
         log::error!("Protocol bugcheck: {}", description);
         self.outbound_tx
-            .send(rpc::StreamToServer {
+            .send(StreamToServer {
                 sequence: 0,
                 client_tick: 0,
                 client_message: Some(rpc::stream_to_server::ClientMessage::BugCheck(
@@ -151,7 +151,7 @@ impl OutboundContext {
             .insert(self.sequence, Instant::now());
         self.shared_state
             .outbound_tx
-            .send(rpc::StreamToServer {
+            .send(StreamToServer {
                 sequence: self.sequence,
                 client_tick: 0,
                 client_message: Some(message),
@@ -317,7 +317,7 @@ impl OutboundContext {
 }
 
 pub(crate) struct InboundContext {
-    inbound_rx: tonic::Streaming<rpc::StreamToClient>,
+    inbound_rx: Streaming<StreamToClient>,
     shared_state: Arc<SharedState>,
 
     mesh_worker_handles: futures::stream::FuturesUnordered<tokio::task::JoinHandle<Result<()>>>,
@@ -411,7 +411,7 @@ impl InboundContext {
                         }
                     }
                 }
-            };
+            }
         }
         log::warn!("Exiting inbound loop");
         // Notify the mesh worker so it can exit soon
@@ -424,7 +424,7 @@ impl InboundContext {
         self.shared_state.batcher.cancel();
         Ok(())
     }
-    async fn handle_message(&mut self, message: &rpc::StreamToClient) -> Result<()> {
+    async fn handle_message(&mut self, message: &StreamToClient) -> Result<()> {
         if message.tick == 0 {
             log::warn!("Got message with tick 0");
         } else {
diff --git a/perovskite_client/src/net_client/mod.rs b/perovskite_client/src/net_client/mod.rs
index 7367b29..f4bc11c 100644
--- a/perovskite_client/src/net_client/mod.rs
+++ b/perovskite_client/src/net_client/mod.rs
@@ -55,12 +55,10 @@ pub(crate) mod mesh_worker;
 const MIN_PROTOCOL_VERSION: u32 = 4;
 const MAX_PROTOCOL_VERSION: u32 = 4;
 
-async fn connect_grpc(
-    server_addr: String,
-) -> Result<rpc::perovskite_game_client::PerovskiteGameClient<Channel>> {
+async fn connect_grpc(server_addr: String) -> Result<PerovskiteGameClient<Channel>> {
     PerovskiteGameClient::connect(server_addr)
         .await
-        .map_err(|e| anyhow::Error::msg(e.to_string()))
+        .map_err(|e| Error::msg(e.to_string()))
 }
 
 const TOTAL_STEPS: f32 = 10.0;
@@ -201,9 +199,7 @@ pub(crate) async fn connect_game(
         .send(StreamToServer {
             sequence: 0,
             client_tick: 0,
-            client_message: Some(rpc::stream_to_server::ClientMessage::ClientInitialReady(
-                rpc::Nop {},
-            )),
+            client_message: Some(ClientMessage::ClientInitialReady(rpc::Nop {})),
         })
         .await?;
     let initial_state_notification = Arc::new(tokio::sync::Notify::new());
diff --git a/perovskite_client/src/vulkan/block_renderer.rs b/perovskite_client/src/vulkan/block_renderer.rs
index 34f76ed..ea475c6 100644
--- a/perovskite_client/src/vulkan/block_renderer.rs
+++ b/perovskite_client/src/vulkan/block_renderer.rs
@@ -1020,7 +1020,7 @@ impl BlockRenderer {
 
     pub(crate) fn make_pointee_cube(
         &self,
-        player_position: cgmath::Vector3<f64>,
+        player_position: Vector3<f64>,
         pointee: perovskite_core::coordinates::BlockCoordinate,
     ) -> Result<CubeGeometryDrawCall> {
         let mut vtx = vec![];
@@ -1326,7 +1326,7 @@ lazy_static::lazy_static! {
 fn make_cgv(
     coord: Vector3<f32>,
     normal: Vector3<f32>,
-    tex_uv: cgmath::Vector2<f32>,
+    tex_uv: Vector2<f32>,
     brightness: f32,
     global_brightness: f32,
     wave_horizontal: f32,
diff --git a/perovskite_client/src/vulkan/game_renderer.rs b/perovskite_client/src/vulkan/game_renderer.rs
index 03b727c..d6983b0 100644
--- a/perovskite_client/src/vulkan/game_renderer.rs
+++ b/perovskite_client/src/vulkan/game_renderer.rs
@@ -62,7 +62,7 @@ pub(crate) struct ActiveGame {
     entities_provider: entity_geometry::EntityPipelineProvider,
     entities_pipeline: entity_geometry::EntityPipelineWrapper,
 
-    egui_adapter: Option<egui_adapter::EguiAdapter>,
+    egui_adapter: Option<EguiAdapter>,
 
     client_state: Arc<ClientState>,
     // Held across frames to avoid constant reallocations
diff --git a/perovskite_client/src/vulkan/mod.rs b/perovskite_client/src/vulkan/mod.rs
index 1b4ae70..01ef43b 100644
--- a/perovskite_client/src/vulkan/mod.rs
+++ b/perovskite_client/src/vulkan/mod.rs
@@ -365,11 +365,9 @@ fn find_best_depth_format(physical_device: &PhysicalDevice) -> Result<Format> {
         {
             log::info!("Depth format found: {format:?}");
             if format == Format::D16_UNORM {
-                log::warn!(
-                    "Depth format D16_UNORM may have low precision and cause visual glitches"
-                );
-                log::warn!("According to the vulkan spec, at least one of the other formats should be supported by any compliant GPU.");
-                log::warn!("Please reach out to the devs with details about your GPU.");
+                warn!("Depth format D16_UNORM may have low precision and cause visual glitches");
+                warn!("According to the vulkan spec, at least one of the other formats should be supported by any compliant GPU.");
+                warn!("Please reach out to the devs with details about your GPU.");
             }
             return Ok(format);
         }
diff --git a/perovskite_client/src/vulkan/shaders/egui_adapter.rs b/perovskite_client/src/vulkan/shaders/egui_adapter.rs
index b0e49dd..5e061ec 100644
--- a/perovskite_client/src/vulkan/shaders/egui_adapter.rs
+++ b/perovskite_client/src/vulkan/shaders/egui_adapter.rs
@@ -43,7 +43,7 @@ impl EguiAdapter {
     }
 
     pub(crate) fn new(
-        ctx: &crate::vulkan::VulkanWindow,
+        ctx: &VulkanWindow,
         event_loop: &EventLoopWindowTarget<()>,
         egui_ui: Arc<Mutex<EguiUi>>,
     ) -> Result<EguiAdapter> {
diff --git a/perovskite_client/src/vulkan/shaders/entity_geometry.rs b/perovskite_client/src/vulkan/shaders/entity_geometry.rs
index 1fe1a30..42b748d 100644
--- a/perovskite_client/src/vulkan/shaders/entity_geometry.rs
+++ b/perovskite_client/src/vulkan/shaders/entity_geometry.rs
@@ -66,7 +66,7 @@ impl PipelineWrapper<Vec<EntityGeometryDrawCall>, SceneState> for EntityPipeline
         &mut self,
         builder: &mut CommandBufferBuilder<L>,
         draw_calls: Vec<EntityGeometryDrawCall>,
-        pass: (),
+        _pass: (),
     ) -> Result<()> {
         let _span = span!("draw entities");
         let pipeline = self.pipeline.clone();
@@ -221,8 +221,8 @@ impl EntityPipelineProvider {
             .build(self.device.clone())?;
 
         let solid_descriptor = tex.descriptor_set(&solid_pipeline, 0, 0)?;
-        let sparse_descriptor = tex.descriptor_set(&sparse_pipeline, 0, 0)?;
-        let translucent_descriptor = tex.descriptor_set(&translucent_pipeline, 0, 0)?;
+        tex.descriptor_set(&sparse_pipeline, 0, 0)?;
+        tex.descriptor_set(&translucent_pipeline, 0, 0)?;
         Ok(EntityPipelineWrapper {
             pipeline: solid_pipeline,
             descriptor: solid_descriptor,
diff --git a/perovskite_client/src/vulkan/shaders/flat_texture.rs b/perovskite_client/src/vulkan/shaders/flat_texture.rs
index b65e7f1..555275c 100644
--- a/perovskite_client/src/vulkan/shaders/flat_texture.rs
+++ b/perovskite_client/src/vulkan/shaders/flat_texture.rs
@@ -74,8 +74,8 @@ impl FlatTextureDrawBuilder {
         }
     }
     pub(crate) fn rect(&mut self, screen_coord: Rect, tex_coord: Rect, tex_dimension: (u32, u32)) {
-        let width = (tex_dimension.0) as f32;
-        let height = (tex_dimension.1) as f32;
+        let width = tex_dimension.0 as f32;
+        let height = tex_dimension.1 as f32;
 
         let bl = FlatTextureVertex {
             position: [screen_coord.left() as f32, screen_coord.bottom() as f32],
@@ -158,7 +158,7 @@ impl<'a> PipelineWrapper<&'a [FlatTextureDrawCall], ()> for FlatTexPipelineWrapp
         builder: &mut CommandBufferBuilder<L>,
         calls: &'a [FlatTextureDrawCall],
         _pass: (),
-    ) -> anyhow::Result<()> {
+    ) -> Result<()> {
         let _span = span!("Draw flat graphics");
         builder.bind_pipeline_graphics(self.pipeline.clone());
         for call in calls {
@@ -171,11 +171,11 @@ impl<'a> PipelineWrapper<&'a [FlatTextureDrawCall], ()> for FlatTexPipelineWrapp
 
     fn bind<L>(
         &mut self,
-        ctx: &crate::vulkan::VulkanContext,
+        ctx: &VulkanContext,
         _per_frame_config: (),
         command_buf_builder: &mut CommandBufferBuilder<L>,
         _pass: (),
-    ) -> anyhow::Result<()> {
+    ) -> Result<()> {
         let layout = self.pipeline.layout().clone();
         let per_frame_set_layout = layout
             .set_layouts()
diff --git a/perovskite_game_api/src/blocks.rs b/perovskite_game_api/src/blocks.rs
index 1760c7c..63dfb64 100644
--- a/perovskite_game_api/src/blocks.rs
+++ b/perovskite_game_api/src/blocks.rs
@@ -295,7 +295,7 @@ impl BlockBuilder {
     /// These can affect diggability, dig speed, and other behavior in
     /// the map.
     ///
-    /// See [crate::constants::block_groups] for useful values.
+    /// See [block_groups] for useful values.
     pub fn add_block_group(mut self, group: impl Into<String>) -> Self {
         let group = group.into();
         if !self.client_info.groups.contains(&group) {
@@ -323,7 +323,7 @@ impl BlockBuilder {
     /// Adds a group to the list of groups for the item corresponding to this block.
     /// These can affect crafting with this block (crafting API is TBD)
     ///
-    /// See [crate::constants::block_groups] for useful values.
+    /// See [block_groups] for useful values.
     pub fn add_item_group(mut self, group: impl Into<String>) -> Self {
         self.item.proto.groups.push(group.into());
         self
@@ -451,7 +451,7 @@ impl BlockBuilder {
         self
     }
 
-    /// Sets the matter type of this block for tool interactions, and adds the corresponding block groun (see [crate::constants::block_groups]).
+    /// Sets the matter type of this block for tool interactions, and adds the corresponding block groun (see [block_groups]).
     /// The default is Solid
     pub fn set_matter_type(mut self, matter_type: MatterType) -> Self {
         self.matter_type = matter_type;
@@ -474,7 +474,7 @@ impl BlockBuilder {
         block.client_info.groups.dedup();
         block.dig_handler_inline = Some(self.dropped_item.build_dig_handler(game_builder));
         for modifier in self.modifiers {
-            (modifier)(&mut block);
+            modifier(&mut block);
         }
         let block_handle = game_builder.inner.blocks_mut().register_block(block)?;
 
@@ -487,7 +487,7 @@ impl BlockBuilder {
                 return Ok(None);
             }
             let extended_data = match &extended_data_initializer {
-                Some(x) => (x)(ctx.clone(), coord, anchor, stack)?,
+                Some(x) => x(ctx.clone(), coord, anchor, stack)?,
                 None => None,
             };
             let variant = match self.variant_effect {
@@ -513,7 +513,7 @@ impl BlockBuilder {
                             .client_info
                             .groups
                             .iter()
-                            .any(|g| g == block_groups::TRIVIALLY_REPLACEABLE))
+                            .any(|g| g == TRIVIALLY_REPLACEABLE))
                     },
                     block_handle.with_variant(variant)?,
                     extended_data,
@@ -838,7 +838,7 @@ impl BulkUpdateCallback for LiquidPropagator {
                                 // Source stays source
                                 0xfff if is_same_liquid => 0xfff,
                                 // and flowing water drops by a level
-                                x => (x.saturating_sub(1)).min(7) as i32,
+                                x => x.saturating_sub(1).min(7) as i32,
                             };
                             let mut variant_from_flow = -1;
                             for (dx, dy, dz) in [(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)] {
@@ -904,11 +904,7 @@ impl BulkUpdateCallback for LiquidPropagator {
 }
 
 impl LiquidPropagator {
-    fn can_flow_laterally_over(
-        &self,
-        x: perovskite_core::block_id::BlockId,
-        liquid_type: &BlockTypeHandle,
-    ) -> bool {
+    fn can_flow_laterally_over(&self, x: BlockId, liquid_type: &BlockTypeHandle) -> bool {
         if x.equals_ignore_variant(AIR_ID) {
             // if it's air below, don't let it flow laterally
             false
diff --git a/perovskite_game_api/src/carts/interlocking.rs b/perovskite_game_api/src/carts/interlocking.rs
index 99e8fee..c8039b7 100644
--- a/perovskite_game_api/src/carts/interlocking.rs
+++ b/perovskite_game_api/src/carts/interlocking.rs
@@ -209,7 +209,7 @@ fn single_pathfind_attempt(
                     if block.equals_ignore_variant(cart_config.interlocking_signal)
                         && state.signal_rotation_ok(block.variant())
                     {
-                        match interlocking_signal_preacquire(signal_coord, block, *block) {
+                        match interlocking_signal_preacquire(signal_coord, block) {
                             signals::SignalLockOutcome::Contended
                             | signals::SignalLockOutcome::InvalidSignal => {
                                 Ok(SignalParseOutcome::Deny)
@@ -242,7 +242,7 @@ fn single_pathfind_attempt(
                             .is_some_and(|(coord, _)| coord == signal_coord)
                     }) {
                         // This is the starting signal that this cart stopped in front of.
-                        match starting_signal_depart_forward(signal_coord, block, *block) {
+                        match starting_signal_depart_forward(signal_coord, block) {
                             signals::SignalLockOutcome::InvalidSignal
                             | signals::SignalLockOutcome::Contended => Ok(SignalParseOutcome::Deny),
                             signals::SignalLockOutcome::Acquired => {
@@ -272,7 +272,7 @@ fn single_pathfind_attempt(
                     } else if block.equals_ignore_variant(cart_config.starting_signal)
                         && state.signal_rotation_ok(block.variant())
                     {
-                        match starting_signal_preacquire_front(signal_coord, block, *block) {
+                        match starting_signal_preacquire_front(signal_coord, block) {
                             signals::SignalLockOutcome::InvalidSignal
                             | signals::SignalLockOutcome::Contended => Ok(SignalParseOutcome::Deny),
                             signals::SignalLockOutcome::Acquired => {
@@ -312,7 +312,7 @@ fn single_pathfind_attempt(
                     {
                         // Starting signal, approaching it from the back
 
-                        match starting_signal_acquire_back(signal_coord, block, *block) {
+                        match starting_signal_acquire_back(signal_coord, block) {
                             signals::SignalLockOutcome::InvalidSignal => {
                                 Ok(SignalParseOutcome::Deny)
                             }
diff --git a/perovskite_game_api/src/carts/mod.rs b/perovskite_game_api/src/carts/mod.rs
index f06815d..5775780 100644
--- a/perovskite_game_api/src/carts/mod.rs
+++ b/perovskite_game_api/src/carts/mod.rs
@@ -256,7 +256,7 @@ pub fn register_carts(game_builder: &mut crate::game_builder::GameBuilder) -> Re
         .inner
         .items_mut()
         .register_item(game_state::items::Item {
-            proto: perovskite_core::perovskite::protocol::items::ItemDef {
+            proto: protocol::items::ItemDef {
                 short_name: "carts:cart_temp".to_string(),
                 display_name: "High-speed minecart".to_string(),
                 inventory_texture: Some(cart_tex.into()),
@@ -386,7 +386,7 @@ fn actually_spawn_cart(
     board_cart: bool,
 ) -> Result<()> {
     let initial_state =
-        tracks::ScanState::spawn_at(rail_pos, (variant as u8 + 2) % 4, ctx.game_map(), &config)?;
+        ScanState::spawn_at(rail_pos, (variant as u8 + 2) % 4, ctx.game_map(), &config)?;
     let initial_state = match initial_state {
         Some(x) => x,
         None => {
@@ -469,7 +469,7 @@ fn actually_spawn_cart(
     Ok(())
 }
 
-fn b2vec(b: BlockCoordinate) -> cgmath::Vector3<f64> {
+fn b2vec(b: BlockCoordinate) -> Vector3<f64> {
     Vector3::new(b.x as f64, b.y as f64, b.z as f64)
 }
 
@@ -490,9 +490,9 @@ impl TrackSegment {
             + (self.from.z - self.to.z).abs()
     }
     fn distance(&self) -> f64 {
-        let dx = self.from.x as f64 - self.to.x as f64;
-        let dy = self.from.y as f64 - self.to.y as f64;
-        let dz = self.from.z as f64 - self.to.z as f64;
+        let dx = self.from.x - self.to.x;
+        let dy = self.from.y - self.to.y;
+        let dz = self.from.z - self.to.z;
         (dx * dx + dy * dy + dz * dz).sqrt()
     }
 
@@ -632,7 +632,7 @@ struct CartCoroutine {
     last_submitted_move_exit_speed: f64,
     id: u32,
     // Track scan state
-    scan_state: tracks::ScanState,
+    scan_state: ScanState,
     // TODO improve this as needed
     cleared_signals: FxHashMap<BlockCoordinate, BlockId>,
     held_signal: Option<(BlockCoordinate, BlockId)>,
@@ -652,20 +652,20 @@ struct CartCoroutine {
 impl EntityCoroutine for CartCoroutine {
     fn plan_move(
         mut self: std::pin::Pin<&mut Self>,
-        services: &perovskite_server::game_state::entities::EntityCoroutineServices<'_>,
-        _current_position: cgmath::Vector3<f64>,
-        whence: cgmath::Vector3<f64>,
+        services: &EntityCoroutineServices<'_>,
+        _current_position: Vector3<f64>,
+        whence: Vector3<f64>,
         when: f32,
         queue_space: usize,
-    ) -> perovskite_server::game_state::entities::CoroutineResult {
+    ) -> CoroutineResult {
         let trace_buffer = TraceBuffer::new(false);
         self.plan_move_impl(services, whence, when, queue_space, trace_buffer)
     }
     fn continuation(
         mut self: std::pin::Pin<&mut Self>,
-        services: &perovskite_server::game_state::entities::EntityCoroutineServices<'_>,
-        _current_position: cgmath::Vector3<f64>,
-        whence: cgmath::Vector3<f64>,
+        services: &EntityCoroutineServices<'_>,
+        _current_position: Vector3<f64>,
+        whence: Vector3<f64>,
         when: f32,
         queue_space: usize,
         continuation_result: ContinuationResult,
@@ -735,7 +735,7 @@ impl CartCoroutine {
     fn plan_move_impl(
         &mut self,
         services: &EntityCoroutineServices<'_>,
-        whence: cgmath::Vector3<f64>,
+        whence: Vector3<f64>,
         when: f32,
         queue_space: usize,
         trace_buffer: TraceBuffer,
@@ -1362,7 +1362,7 @@ impl CartCoroutine {
     fn last_segment_exit_speed(&self) -> f64 {
         self.scheduled_segments
             .back()
-            .map(|x| (x.speed + (x.acceleration * x.move_time)))
+            .map(|x| x.speed + (x.acceleration * x.move_time))
             .unwrap_or(self.last_submitted_move_exit_speed)
     }
 
@@ -1749,10 +1749,8 @@ impl CartCoroutine {
             // Scan every second, trying to start again.
             tracing::debug!("No schedulable segments");
             trace_buffer.log("No schedulable segments");
-            return perovskite_server::game_state::entities::CoroutineResult::Successful(
-                perovskite_server::game_state::entities::EntityMoveDecision::AskAgainLaterFlexible(
-                    0.5..1.0,
-                ),
+            return CoroutineResult::Successful(
+                game_state::entities::EntityMoveDecision::AskAgainLaterFlexible(0.5..1.0),
             );
         }
         trace_buffer.log("Building moves");
@@ -1895,11 +1893,9 @@ impl CartCoroutine {
         }
         tracing::debug!("returning {} moves", returned_moves.len());
         trace_buffer.log("Done!");
-        CoroutineResult::Successful(
-            perovskite_server::game_state::entities::EntityMoveDecision::QueueUpMultiple(
-                returned_moves,
-            ),
-        )
+        CoroutineResult::Successful(game_state::entities::EntityMoveDecision::QueueUpMultiple(
+            returned_moves,
+        ))
     }
 
     fn estimate_panic_max_index(&self) -> usize {
diff --git a/perovskite_game_api/src/carts/signals.rs b/perovskite_game_api/src/carts/signals.rs
index 6096db4..fddd609 100644
--- a/perovskite_game_api/src/carts/signals.rs
+++ b/perovskite_game_api/src/carts/signals.rs
@@ -171,7 +171,7 @@ const VARIANT_PRELOCKED: u16 = 256;
 const VARIANT_STARTING_HELD: u16 = 512;
 
 pub(crate) fn register_signal_blocks(
-    game_builder: &mut crate::game_builder::GameBuilder,
+    game_builder: &mut GameBuilder,
 ) -> Result<(BlockId, BlockId, BlockId)> {
     include_texture_bytes!(
         game_builder,
@@ -801,7 +801,6 @@ pub(crate) fn signal_release(
 pub(crate) fn interlocking_signal_preacquire(
     _block_coord: BlockCoordinate,
     id: &mut BlockId,
-    expected_id_with_rotation: BlockId,
 ) -> SignalLockOutcome {
     let mut variant = id.variant();
 
@@ -832,7 +831,6 @@ pub(crate) fn interlocking_signal_preacquire(
 pub(crate) fn starting_signal_preacquire_front(
     _block_coord: BlockCoordinate,
     id: &mut BlockId,
-    expected_id_with_rotation: BlockId,
 ) -> SignalLockOutcome {
     let mut variant = id.variant();
 
@@ -862,7 +860,6 @@ pub(crate) fn starting_signal_preacquire_front(
 pub(crate) fn starting_signal_depart_forward(
     _block_coord: BlockCoordinate,
     id: &mut BlockId,
-    expected_id_with_rotation: BlockId,
 ) -> SignalLockOutcome {
     let mut variant = id.variant();
 
@@ -882,7 +879,6 @@ pub(crate) fn starting_signal_depart_forward(
 pub(crate) fn starting_signal_acquire_back(
     _block_coord: BlockCoordinate,
     id: &mut BlockId,
-    expected_id_with_rotation: BlockId,
 ) -> SignalLockOutcome {
     let mut variant = id.variant();
 
diff --git a/perovskite_game_api/src/carts/track_tool.rs b/perovskite_game_api/src/carts/track_tool.rs
index f4356c4..73e0cab 100644
--- a/perovskite_game_api/src/carts/track_tool.rs
+++ b/perovskite_game_api/src/carts/track_tool.rs
@@ -206,7 +206,7 @@ fn build_track(
     for tile in template.entries.iter() {
         let (cx, cz) = eval_rotation(tile.offset_x, tile.offset_z, flip, face_dir_as_variant);
         let coord = initial_coord
-            .try_delta(cx as i32, tile.offset_y, cz as i32)
+            .try_delta(cx, tile.offset_y, cz)
             .with_context(|| {
                 format!(
                     "Out of bounds for {:?} + ({:?}, {:?})",
diff --git a/perovskite_game_api/src/carts/tracks.rs b/perovskite_game_api/src/carts/tracks.rs
index 2c2acc1..019cfd4 100644
--- a/perovskite_game_api/src/carts/tracks.rs
+++ b/perovskite_game_api/src/carts/tracks.rs
@@ -558,16 +558,12 @@ fn build_track_tiles() -> ([[Option<TrackTile>; 16]; 11], Vec<Template>) {
             TileId::empty(),
         ],
         allowed_prev_tiles: [TileId::new(0, 0, 2, false, false, false), TileId::empty()],
-        straight_track_eligible_connections: (
-            // We can enter a straight track with the same rotation, regardless of flip_x
-            TileId::new(0, 0, 0, false, false, false).rotation_flip_bitset_mask()
-                | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask()
-        ),
-        reverse_straight_track_eligible_connections: (
-            // We can enter a straight track from the other side, and do a reverse move through it
-            TileId::new(0, 0, 2, false, true, false).rotation_flip_bitset_mask()
-                | TileId::new(0, 0, 2, true, true, false).rotation_flip_bitset_mask()
-        ),
+        straight_track_eligible_connections: TileId::new(0, 0, 0, false, false, false)
+            .rotation_flip_bitset_mask()
+            | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask(),
+        reverse_straight_track_eligible_connections: TileId::new(0, 0, 2, false, true, false)
+            .rotation_flip_bitset_mask()
+            | TileId::new(0, 0, 2, true, true, false).rotation_flip_bitset_mask(),
         ..TrackTile::default()
     });
     // [8][8] is the 90 degree curve without a switch
@@ -580,21 +576,12 @@ fn build_track_tiles() -> ([[Option<TrackTile>; 16]; 11], Vec<Template>) {
             TileId::empty(),
         ],
         allowed_prev_tiles: [TileId::new(0, 0, 2, false, false, false), TileId::empty()],
-        straight_track_eligible_connections: (
-            // We can enter the 90-degree elbow at (8, 8) in its forward scan direction.
-            TileId::new(8, 8, 0, false, false, false).rotation_flip_bitset_mask()
-                // including if X is flipped
-                | TileId::new(8, 8, 0, true, false, false).rotation_flip_bitset_mask()
-        ),
-        reverse_straight_track_eligible_connections: (
-            // We can enter the 90-degree elbow at (8, 8) in its reverse scan direction.
-            // For this to work, the elbow must be turned 90 degrees so we're seeing its end-of-path
-            // side. We set reverse = true because we want the scan to continue in the reverse direction
-            //
-            // but if X is flipped on the elbow, we need the opposite rotation
-            TileId::new(8, 8, 1, false, true, false).rotation_flip_bitset_mask()
-                | TileId::new(8, 8, 3, true, true, false).rotation_flip_bitset_mask()
-        ),
+        straight_track_eligible_connections: TileId::new(8, 8, 0, false, false, false).rotation_flip_bitset_mask()
+            // including if X is flipped
+            | TileId::new(8, 8, 0, true, false, false).rotation_flip_bitset_mask(),
+        reverse_straight_track_eligible_connections: TileId::new(8, 8, 1, false, true, false)
+            .rotation_flip_bitset_mask()
+            | TileId::new(8, 8, 3, true, true, false).rotation_flip_bitset_mask(),
         max_speed: 10,
         ..TrackTile::default()
     });
@@ -831,20 +818,13 @@ fn build_folded_switch(
                 [
                     TileId::new(
                         switch_xmin + 1,
-                        switch_ymin + (y + 1) as u16,
+                        switch_ymin + (y + 1),
                         0,
                         false,
                         false,
                         true,
                     ),
-                    TileId::new(
-                        diag_xmin + 1,
-                        diag_ymin + (y + 1) as u16,
-                        0,
-                        false,
-                        false,
-                        false,
-                    ),
+                    TileId::new(diag_xmin + 1, diag_ymin + (y + 1), 0, false, false, false),
                 ]
             },
             allowed_prev_tiles: [TileId::new(0, 0, 2, false, false, false), TileId::empty()],
@@ -853,35 +833,16 @@ fn build_folded_switch(
                 [TileId::new(0, 0, 2, false, false, false), TileId::empty()]
             } else {
                 [
-                    TileId::new(
-                        switch_xmin + 1,
-                        switch_ymin + (y - 1) as u16,
-                        0,
-                        false,
-                        true,
-                        true,
-                    ),
-                    TileId::new(
-                        diag_xmin + 1,
-                        diag_ymin + (y - 1) as u16,
-                        0,
-                        false,
-                        true,
-                        true,
-                    ),
+                    TileId::new(switch_xmin + 1, switch_ymin + (y - 1), 0, false, true, true),
+                    TileId::new(diag_xmin + 1, diag_ymin + (y - 1), 0, false, true, true),
                 ]
             },
-            straight_track_eligible_connections: (
-                // We can enter a straight track with the same rotation, regardless of flip_x
-                // Note that X and Y don't matter here; we're just getting the bitset mask.
-                TileId::new(0, 0, 0, false, false, false).rotation_flip_bitset_mask()
-                    | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask()
-            ),
-            reverse_straight_track_eligible_connections: (
-                // We can enter a straight track from the other side, and do a reverse move through it
-                TileId::new(0, 0, 2, false, true, false).rotation_flip_bitset_mask()
-                    | TileId::new(0, 0, 2, true, true, false).rotation_flip_bitset_mask()
-            ),
+            straight_track_eligible_connections: TileId::new(0, 0, 0, false, false, false)
+                .rotation_flip_bitset_mask()
+                | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask(),
+            reverse_straight_track_eligible_connections: TileId::new(0, 0, 2, false, true, false)
+                .rotation_flip_bitset_mask()
+                | TileId::new(0, 0, 2, true, true, false).rotation_flip_bitset_mask(),
 
             // At X+1, we need the counterpart to our main tile.
             secondary_coord: if y >= skip_secondary_tiles {
@@ -957,20 +918,13 @@ fn build_folded_switch(
                 [
                     TileId::new(
                         switch_xmin + 1,
-                        switch_ymin + (y + 1) as u16,
+                        switch_ymin + (y + 1),
                         0,
                         false,
                         false,
                         true,
                     ),
-                    TileId::new(
-                        diag_xmin + 1,
-                        diag_ymin + (y + 1) as u16,
-                        0,
-                        false,
-                        false,
-                        false,
-                    ),
+                    TileId::new(diag_xmin + 1, diag_ymin + (y + 1), 0, false, false, false),
                     TileId::empty(),
                 ]
             },
@@ -979,22 +933,8 @@ fn build_folded_switch(
                 [TileId::new(0, 0, 2, false, false, false), TileId::empty()]
             } else {
                 [
-                    TileId::new(
-                        switch_xmin + 1,
-                        switch_ymin + (y - 1) as u16,
-                        0,
-                        false,
-                        true,
-                        true,
-                    ),
-                    TileId::new(
-                        diag_xmin + 1,
-                        diag_ymin + (y - 1) as u16,
-                        0,
-                        false,
-                        true,
-                        false,
-                    ),
+                    TileId::new(switch_xmin + 1, switch_ymin + (y - 1), 0, false, true, true),
+                    TileId::new(diag_xmin + 1, diag_ymin + (y - 1), 0, false, true, false),
                 ]
             },
             // Only connects to straight track if y == 0, and only at the input side, regardless of flip_x
@@ -1093,56 +1033,80 @@ pub(crate) fn register_tracks(
     );
     let rail_tile = game_builder.add_block(
         BlockBuilder::new(StaticBlockName("carts:rail_tile"))
-            .set_axis_aligned_boxes_appearance(AxisAlignedBoxesAppearanceBuilder::new().add_box_with_variant_mask_and_slope(
-                rail_tile_box,
-                (-0.5, 0.5),
-                (-0.5, -0.4375),
-                (-0.5, 0.5),
-                0,
-                0.0,
-                0.0,
-                0.0,
-                0.0,
-            ))
+            .set_axis_aligned_boxes_appearance(
+                AxisAlignedBoxesAppearanceBuilder::new().add_box_with_variant_mask_and_slope(
+                    rail_tile_box,
+                    (-0.5, 0.5),
+                    (-0.5, -0.4375),
+                    (-0.5, 0.5),
+                    0,
+                    0.0,
+                    0.0,
+                    0.0,
+                    0.0,
+                ),
+            )
             .set_allow_light_propagation(true)
             .set_display_name("Railway track")
             .add_modifier(Box::new(|bt| {
                 bt.interact_key_handler = Some(Box::new(|ctx, coord| {
                     let block = ctx.game_map().get_block(coord)?;
                     let tile_id = TileId::from_variant(block.variant(), false, false);
-                    ctx.initiator().send_chat_message(ChatMessage::new(
-                        "[INFO]",
-                        format!("{:?}", tile_id),
-                    ))?;
-
-                    Ok(Some(ctx.new_popup()
-                        .title("Mr. Yellow")
-                        .label("Update tile:")
-                        .text_field("tile_x", "Tile X", tile_id.x().to_string(), true, false)
-                        .text_field("tile_y", "Tile Y", tile_id.y().to_string(), true, false)
-                        .text_field("rotation", "Rotation", tile_id.rotation().to_string(), true, false)
-                        .checkbox("flip_x", "Flip X", tile_id.flip_x(), true)
-                        .button("apply", "Apply", true, true)
-                        .label("Scan tile:")
-                        .checkbox("reverse", "Reverse", false, true)
-                        .checkbox("diverging", "Diverging", false, true)
-                        .button("scan", "Scan", true, false)
-                        .button("multiscan", "Multi-Scan", true, false)
-                        .set_button_callback(Box::new(move |response: PopupResponse<'_>| {
-                            match handle_popup_response(&response, coord, response.ctx.extension::<CartsGameBuilderExtension>().as_ref().unwrap()) {
-                                Ok(_) => {}
-                                Err(e) => {
-                                    response.ctx.initiator().send_chat_message(ChatMessage::new("[ERROR]", "Failed to parse popup response: ".to_string() + &e.to_string())).unwrap();
+                    ctx.initiator()
+                        .send_chat_message(ChatMessage::new("[INFO]", format!("{:?}", tile_id)))?;
+
+                    Ok(Some(
+                        ctx.new_popup()
+                            .title("Mr. Yellow")
+                            .label("Update tile:")
+                            .text_field("tile_x", "Tile X", tile_id.x().to_string(), true, false)
+                            .text_field("tile_y", "Tile Y", tile_id.y().to_string(), true, false)
+                            .text_field(
+                                "rotation",
+                                "Rotation",
+                                tile_id.rotation().to_string(),
+                                true,
+                                false,
+                            )
+                            .checkbox("flip_x", "Flip X", tile_id.flip_x(), true)
+                            .button("apply", "Apply", true, true)
+                            .label("Scan tile:")
+                            .checkbox("reverse", "Reverse", false, true)
+                            .checkbox("diverging", "Diverging", false, true)
+                            .button("scan", "Scan", true, false)
+                            .button("multiscan", "Multi-Scan", true, false)
+                            .set_button_callback(Box::new(move |response: PopupResponse<'_>| {
+                                match handle_popup_response(
+                                    &response,
+                                    coord,
+                                    response
+                                        .ctx
+                                        .extension::<CartsGameBuilderExtension>()
+                                        .as_ref()
+                                        .unwrap(),
+                                ) {
+                                    Ok(_) => {}
+                                    Err(e) => {
+                                        response
+                                            .ctx
+                                            .initiator()
+                                            .send_chat_message(ChatMessage::new(
+                                                "[ERROR]",
+                                                "Failed to parse popup response: ".to_string()
+                                                    + &e.to_string(),
+                                            ))
+                                            .unwrap();
+                                    }
                                 }
-                            }
-                        }))))
+                            })),
+                    ))
                 }));
                 let ri = bt.client_info.render_info.as_mut().unwrap();
                 match ri {
-                    perovskite_core::protocol::blocks::block_type_def::RenderInfo::AxisAlignedBoxes(aabb) => {
+                    protocol::blocks::block_type_def::RenderInfo::AxisAlignedBoxes(aabb) => {
                         aabb.boxes.iter_mut().for_each(|b| {
-                            b.tex_top.as_mut().unwrap().crop.as_mut().unwrap().dynamic = Some(
-                                DynamicCrop {
+                            b.tex_top.as_mut().unwrap().crop.as_mut().unwrap().dynamic =
+                                Some(DynamicCrop {
                                     x_selector_bits: 0b0000_0000_0011_1100,
                                     y_selector_bits: 0b0000_0011_1100_0000,
                                     x_cells: 16,
@@ -1151,23 +1115,26 @@ pub(crate) fn register_tracks(
                                     flip_y_bit: 0,
                                     extra_flip_x: false,
                                     extra_flip_y: false,
-                                }
-                            );
-                            b.tex_bottom.as_mut().unwrap().crop.as_mut().unwrap().dynamic = Some(
-                                DynamicCrop {
-                                    x_selector_bits: 0b0000_0000_0011_1100,
-                                    y_selector_bits: 0b0000_0011_1100_0000,
-                                    x_cells: 16,
-                                    y_cells: 11,
-                                    flip_x_bit: 0b0000_0100_0000_0000,
-                                    flip_y_bit: 0,
-                                    extra_flip_x: true,
-                                    extra_flip_y: false,
-                                }
-                            )
+                                });
+                            b.tex_bottom
+                                .as_mut()
+                                .unwrap()
+                                .crop
+                                .as_mut()
+                                .unwrap()
+                                .dynamic = Some(DynamicCrop {
+                                x_selector_bits: 0b0000_0000_0011_1100,
+                                y_selector_bits: 0b0000_0011_1100_0000,
+                                x_cells: 16,
+                                y_cells: 11,
+                                flip_x_bit: 0b0000_0100_0000_0000,
+                                flip_y_bit: 0,
+                                extra_flip_x: true,
+                                extra_flip_y: false,
+                            })
                         })
                     }
-                    _ => unreachable!()
+                    _ => unreachable!(),
                 }
             })),
     )?;
@@ -1385,13 +1352,10 @@ fn build_slope_tiles() -> [Option<TrackTile>; 16] {
         allowed_next_diverging_tiles: [TileId::empty(); 2],
         allowed_prev_tiles: [TileId::new(0, 0, 2, false, false, false), TileId::empty()],
         allowed_prev_diverging_tiles: [TileId::empty(); 2],
-        straight_track_eligible_connections: (
-            // We can enter this track in its forward scan direction
-            TileId::new(0, 0, 0, false, false, false)
-                .rotation_flip_bitset_mask()
-                // including ff X is flipped
-                | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask()
-        ),
+        straight_track_eligible_connections: TileId::new(0, 0, 0, false, false, false)
+            .rotation_flip_bitset_mask()
+            // including ff X is flipped
+            | TileId::new(0, 0, 0, true, false, false).rotation_flip_bitset_mask(),
         // Slopes are special: we enter them in reverse from a straight track one level HIGHER
         // Therefore, this case cannot be encoded in this field.
         reverse_straight_track_eligible_connections: 0,
@@ -1487,7 +1451,7 @@ impl std::fmt::Debug for ScanOutcome {
             ScanOutcome::Success(state) => state.fmt(f),
             ScanOutcome::CannotAdvance => f.write_str("CannotAdvance"),
             ScanOutcome::NotOnTrack => f.write_str("NotOnTrack"),
-            ScanOutcome::Deferral(deferral) => f.write_str("Deferral {..}"),
+            ScanOutcome::Deferral(_deferral) => f.write_str("Deferral {..}"),
         }
     }
 }
diff --git a/perovskite_game_api/src/circuits/gates.rs b/perovskite_game_api/src/circuits/gates.rs
index d3556ab..8e98342 100644
--- a/perovskite_game_api/src/circuits/gates.rs
+++ b/perovskite_game_api/src/circuits/gates.rs
@@ -164,7 +164,7 @@ impl CircuitBlockCallbacks for CombinationalGateImpl {
 
     fn on_overheat(
         &self,
-        ctx: &perovskite_server::game_state::event::HandlerContext,
+        ctx: &HandlerContext,
         coord: perovskite_core::coordinates::BlockCoordinate,
     ) {
         ctx.game_map().set_block(coord, &self.broken, None).unwrap();
@@ -464,7 +464,7 @@ impl CircuitBlockCallbacks for DelayGateImpl {
 
     fn on_overheat(
         &self,
-        ctx: &perovskite_server::game_state::event::HandlerContext,
+        ctx: &HandlerContext,
         coord: perovskite_core::coordinates::BlockCoordinate,
     ) {
         ctx.game_map().set_block(coord, &self.broken, None).unwrap();
@@ -643,7 +643,7 @@ impl CircuitBlockCallbacks for DffImpl {
 
     fn on_overheat(
         &self,
-        ctx: &perovskite_server::game_state::event::HandlerContext,
+        ctx: &HandlerContext,
         coord: perovskite_core::coordinates::BlockCoordinate,
     ) {
         ctx.game_map().set_block(coord, &self.broken, None).unwrap();
diff --git a/perovskite_game_api/src/circuits/mod.rs b/perovskite_game_api/src/circuits/mod.rs
index d4d889b..fb3d75c 100644
--- a/perovskite_game_api/src/circuits/mod.rs
+++ b/perovskite_game_api/src/circuits/mod.rs
@@ -818,7 +818,7 @@ pub enum PinReading {
 /// Returns:
 ///     The state of the pin
 pub fn get_pin_state(
-    ctx: &events::CircuitHandlerContext<'_>,
+    ctx: &CircuitHandlerContext<'_>,
     coord: BlockCoordinate,
     into: BlockCoordinate,
 ) -> PinReading {
diff --git a/perovskite_game_api/src/circuits/simple_blocks.rs b/perovskite_game_api/src/circuits/simple_blocks.rs
index 5cbe565..50c8f3d 100644
--- a/perovskite_game_api/src/circuits/simple_blocks.rs
+++ b/perovskite_game_api/src/circuits/simple_blocks.rs
@@ -58,7 +58,7 @@ impl CircuitBlockCallbacks for SourceBlockCallbacks {
         _ctx: &super::events::CircuitHandlerContext<'_>,
         _coordinate: perovskite_core::coordinates::BlockCoordinate,
         _from: perovskite_core::coordinates::BlockCoordinate,
-        _state: super::PinState,
+        _state: PinState,
     ) -> Result<()> {
         // Likewise, no need to do anything.
         Ok(())
@@ -69,7 +69,7 @@ impl CircuitBlockCallbacks for SourceBlockCallbacks {
         _ctx: &super::events::CircuitHandlerContext<'_>,
         _coord: perovskite_core::coordinates::BlockCoordinate,
         _destination: perovskite_core::coordinates::BlockCoordinate,
-    ) -> super::PinState {
+    ) -> PinState {
         self.0
     }
 
@@ -109,11 +109,11 @@ impl CircuitBlockCallbacks for SimpleLampCallbacks {
         ctx: &super::events::CircuitHandlerContext<'_>,
         coord: perovskite_core::coordinates::BlockCoordinate,
         _from: perovskite_core::coordinates::BlockCoordinate,
-        _state: super::PinState,
+        _state: PinState,
     ) -> Result<()> {
         let desired = if get_incoming_pin_states(ctx, coord)
             .iter()
-            .any(|(_, _, state)| state == &super::PinState::High)
+            .any(|(_, _, state)| state == &PinState::High)
         {
             self.lamp_on
         } else {
@@ -135,8 +135,8 @@ impl CircuitBlockCallbacks for SimpleLampCallbacks {
         _ctx: &super::events::CircuitHandlerContext<'_>,
         _coord: perovskite_core::coordinates::BlockCoordinate,
         _destination: perovskite_core::coordinates::BlockCoordinate,
-    ) -> super::PinState {
-        super::PinState::Low
+    ) -> PinState {
+        PinState::Low
     }
 }
 
diff --git a/perovskite_game_api/src/circuits/wire.rs b/perovskite_game_api/src/circuits/wire.rs
index cea56df..8d5e59d 100644
--- a/perovskite_game_api/src/circuits/wire.rs
+++ b/perovskite_game_api/src/circuits/wire.rs
@@ -198,7 +198,7 @@ impl CircuitBlockCallbacks for WireCallbacksImpl {
         _ctx: &CircuitHandlerContext<'_>,
         _coord: BlockCoordinate,
         _destination: BlockCoordinate,
-    ) -> super::PinState {
+    ) -> PinState {
         self.state
     }
 }
@@ -214,7 +214,7 @@ pub(crate) fn recalculate_wire(
     first_wire: BlockCoordinate,
     // The coordinate of the block that signalled us
     who_signalled: BlockCoordinate,
-    _new_state: super::PinState,
+    _new_state: PinState,
 ) -> Result<()> {
     // TODO: use the edge type as an optimization hint
     // Essentially, do a breadth-first search of the wire, starting at first_wire. Signal all
@@ -276,7 +276,7 @@ pub(crate) fn recalculate_wire(
             };
             // prev is the wire we just explored
             let drive = callbacks.sample_pin(ctx, coord, prev);
-            if drive == super::PinState::High {
+            if drive == PinState::High {
                 any_driven_high = true;
             }
 
@@ -297,9 +297,9 @@ pub(crate) fn recalculate_wire(
     }
 
     let pin_state = if any_driven_high {
-        super::PinState::High
+        PinState::High
     } else {
-        super::PinState::Low
+        PinState::Low
     };
 
     for ((coord, prev), callbacks) in need_transition_signals {
diff --git a/perovskite_game_api/src/default_game/basic_blocks.rs b/perovskite_game_api/src/default_game/basic_blocks.rs
index 2e0ad29..62955a7 100644
--- a/perovskite_game_api/src/default_game/basic_blocks.rs
+++ b/perovskite_game_api/src/default_game/basic_blocks.rs
@@ -883,7 +883,7 @@ fn make_chest_popup(
     coord: perovskite_core::coordinates::BlockCoordinate,
     p: &perovskite_server::game_state::event::PlayerInitiator<'_>,
 ) -> Result<perovskite_server::game_state::client_ui::Popup, anyhow::Error> {
-    Ok((ctx
+    Ok(ctx
         .new_popup()
         .title("Chest")
         .inventory_view_block(
@@ -902,5 +902,5 @@ fn make_chest_popup(
             p.player.main_inventory(),
             true,
             true,
-        ))?)
+        )?)
 }
diff --git a/perovskite_game_api/src/default_game/commands.rs b/perovskite_game_api/src/default_game/commands.rs
index 8d2299a..0ebf202 100644
--- a/perovskite_game_api/src/default_game/commands.rs
+++ b/perovskite_game_api/src/default_game/commands.rs
@@ -314,7 +314,7 @@ impl ChatCommandHandler for TeleportCommand {
             }
             5 => (
                 params[1],
-                (params[2].parse()?, (params[3]).parse()?, params[4].parse()?),
+                (params[2].parse()?, params[3].parse()?, params[4].parse()?),
             ),
             _ => bail!("Incorrect usage: should be /teleport [player] <x> <y> <z>"),
         };
diff --git a/perovskite_game_api/src/default_game/mapgen/mod.rs b/perovskite_game_api/src/default_game/mapgen/mod.rs
index 26ae0a4..afe8615 100644
--- a/perovskite_game_api/src/default_game/mapgen/mod.rs
+++ b/perovskite_game_api/src/default_game/mapgen/mod.rs
@@ -661,9 +661,9 @@ impl DefaultMapgen {
         vert_offset: i32,
         block_coord: BlockCoordinate,
         gen_ore: F,
-    ) -> perovskite_core::block_id::BlockId
+    ) -> BlockId
     where
-        F: Fn() -> perovskite_core::block_id::BlockId,
+        F: Fn() -> BlockId,
     {
         if vert_offset > 0 {
             if block_coord.y > 0 {
@@ -686,9 +686,9 @@ impl DefaultMapgen {
         vert_offset: i32,
         block_coord: BlockCoordinate,
         gen_ore: F,
-    ) -> perovskite_core::block_id::BlockId
+    ) -> BlockId
     where
-        F: Fn() -> perovskite_core::block_id::BlockId,
+        F: Fn() -> BlockId,
     {
         if vert_offset > 0 {
             if block_coord.y > 0 {
@@ -709,9 +709,9 @@ impl DefaultMapgen {
         vert_offset: i32,
         block_coord: BlockCoordinate,
         gen_ore: F,
-    ) -> perovskite_core::block_id::BlockId
+    ) -> BlockId
     where
-        F: Fn() -> perovskite_core::block_id::BlockId,
+        F: Fn() -> BlockId,
     {
         if vert_offset > 0 {
             if block_coord.y > 0 {
diff --git a/perovskite_game_api/src/default_game/recipes.rs b/perovskite_game_api/src/default_game/recipes.rs
index f316613..c99a395 100644
--- a/perovskite_game_api/src/default_game/recipes.rs
+++ b/perovskite_game_api/src/default_game/recipes.rs
@@ -182,7 +182,7 @@ impl<const N: usize, T> RecipeImpl<N, T> {
                 RecipeSlot::Exact(_) => None,
             })
             .collect();
-        assert!(remaining_slots.len() == remaining_stacks.len());
+        assert_eq!(remaining_slots.len(), remaining_stacks.len());
 
         match remaining_slots.len() {
             0 => return true,
diff --git a/perovskite_game_api/src/game_builder.rs b/perovskite_game_api/src/game_builder.rs
index 07e98c9..6d123c2 100644
--- a/perovskite_game_api/src/game_builder.rs
+++ b/perovskite_game_api/src/game_builder.rs
@@ -199,20 +199,20 @@ impl GameBuilder {
 
     /// Creates a new game builder with custom server configuration
     #[cfg(feature = "unstable_api")]
-    pub fn from_args(args: &server_api::ServerArgs) -> Result<GameBuilder> {
+    pub fn from_args(args: &ServerArgs) -> Result<GameBuilder> {
         Self::new_with_builtins(ServerBuilder::from_args(args)?)
     }
 
     /// Borrows the ServerBuilder that can be used to directly register
     /// items, blocks, etc using the low-level unstable API.
     #[cfg(feature = "unstable_api")]
-    pub fn server_builder_mut(&mut self) -> &mut server_api::ServerBuilder {
+    pub fn server_builder_mut(&mut self) -> &mut ServerBuilder {
         &mut self.inner
     }
 
     /// Returns the ServerBuilder with everything built so far.
     #[cfg(feature = "unstable_api")]
-    pub fn into_server_builder(mut self) -> Result<server_api::ServerBuilder> {
+    pub fn into_server_builder(mut self) -> Result<ServerBuilder> {
         self.pre_build()?;
         Ok(self.inner)
     }
@@ -373,7 +373,7 @@ impl GameBuilder {
             .register_from_memory(&tex_name.into().0, data)
     }
 
-    pub fn data_dir(&self) -> &std::path::PathBuf {
+    pub fn data_dir(&self) -> &PathBuf {
         self.inner.data_dir()
     }
 
@@ -394,7 +394,7 @@ impl GameBuilder {
 
 /// Convenience helper for including a texture in the source tree into the game.
 ///
-/// file_name is looked up relative to the current file (see [std::include_bytes]).
+/// file_name is looked up relative to the current file (see [include_bytes]).
 ///
 /// This macro takes the following parameters:
 /// * Mutable reference to [GameBuilder]
diff --git a/perovskite_server/src/database/rocksdb.rs b/perovskite_server/src/database/rocksdb.rs
index b9bb913..a984587 100644
--- a/perovskite_server/src/database/rocksdb.rs
+++ b/perovskite_server/src/database/rocksdb.rs
@@ -23,7 +23,7 @@ use tracy_client::span;
 use super::database_engine::GameDatabase;
 
 pub(crate) struct RocksDbBackend {
-    db: rocksdb::DB,
+    db: DB,
 }
 impl RocksDbBackend {
     pub(crate) fn new<P: AsRef<Path>>(path: P) -> Result<RocksDbBackend> {
@@ -65,7 +65,7 @@ impl Drop for RocksDbBackend {
     }
 }
 impl GameDatabase for RocksDbBackend {
-    fn get(&self, key: &[u8]) -> anyhow::Result<Option<Vec<u8>>> {
+    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
         let _span = span!("db get");
         self.db.get(key).with_context(|| "RocksDB get failed")
     }
@@ -79,14 +79,14 @@ impl GameDatabase for RocksDbBackend {
             .with_context(|| "RocksDB get failed")
     }
 
-    fn put(&self, key: &[u8], value: &[u8]) -> anyhow::Result<()> {
+    fn put(&self, key: &[u8], value: &[u8]) -> Result<()> {
         let _span = span!("db put");
         self.db
             .put(key, value)
             .with_context(|| "RocksDB put failed")
     }
 
-    fn delete(&self, key: &[u8]) -> anyhow::Result<()> {
+    fn delete(&self, key: &[u8]) -> Result<()> {
         let _span = span!("db delete");
         self.db.delete(key).with_context(|| "RocksDB delete failed")
     }
diff --git a/perovskite_server/src/game_state/blocks.rs b/perovskite_server/src/game_state/blocks.rs
index c782c74..3b439b9 100644
--- a/perovskite_server/src/game_state/blocks.rs
+++ b/perovskite_server/src/game_state/blocks.rs
@@ -644,7 +644,7 @@ impl BlockTypeManager {
     ///
     /// Panics:
     /// This function will panic if called before the game starts up.
-    pub fn fast_block_group<'a>(&'a self, block_group: &str) -> Option<FastBlockGroup<'a>> {
+    pub fn fast_block_group(&self, block_group: &str) -> Option<FastBlockGroup> {
         self.fast_block_groups
             .get(block_group)
             .map(|x| FastBlockGroup { blocks: x })
@@ -760,7 +760,7 @@ fn make_unknown_block_serverside(
     id: BlockId,
     short_name: String,
 ) -> BlockType {
-    assert!(id.variant() == 0);
+    assert_eq!(id.variant(), 0);
 
     let air = manager.make_block_name(AIR.to_string());
 
diff --git a/perovskite_server/src/game_state/client_ui.rs b/perovskite_server/src/game_state/client_ui.rs
index e6aac19..90fe2c6 100644
--- a/perovskite_server/src/game_state/client_ui.rs
+++ b/perovskite_server/src/game_state/client_ui.rs
@@ -208,7 +208,7 @@ mod private {
         fn deref_to_popup(&mut self) -> &mut super::Popup;
     }
 }
-impl private::UiElementContainerPrivate for Popup {
+impl UiElementContainerPrivate for Popup {
     fn push_widget(&mut self, element: UiElement) {
         self.widgets.push(element);
     }
@@ -224,7 +224,7 @@ pub struct SideBySideLayoutBuilder<'a> {
     widgets: Vec<UiElement>,
 }
 
-impl private::UiElementContainerPrivate for SideBySideLayoutBuilder<'_> {
+impl UiElementContainerPrivate for SideBySideLayoutBuilder<'_> {
     fn push_widget(&mut self, element: UiElement) {
         self.widgets.push(element);
     }
diff --git a/perovskite_server/src/game_state/entities.rs b/perovskite_server/src/game_state/entities.rs
index de7596b..c3586a5 100644
--- a/perovskite_server/src/game_state/entities.rs
+++ b/perovskite_server/src/game_state/entities.rs
@@ -212,7 +212,7 @@ impl EntityManager {
         self.cancellation.cancel();
     }
     pub(crate) async fn await_shutdown(&self) -> Result<()> {
-        for handle in std::mem::replace(self.workers.lock().deref_mut(), vec![]).into_iter() {
+        for handle in std::mem::take(self.workers.lock().deref_mut()).into_iter() {
             handle.await??;
         }
         Ok(())
@@ -610,7 +610,7 @@ impl<'a> EntityCoroutineServices<'a> {
     ) {
         let ctx = HandlerContext {
             tick: self.game_state.tick(),
-            initiator: super::event::EventInitiator::Plugin("entity_coroutine".to_string()),
+            initiator: EventInitiator::Plugin("entity_coroutine".to_string()),
             game_state: self.game_state.clone(),
         };
         tokio::task::spawn(async move {
@@ -624,7 +624,6 @@ impl<'a> EntityCoroutineServices<'a> {
     /// For now, this is just spawned onto the tokio runtime, but this may change in the future.
     ///
     /// Warning: If the function hangs indefinitely, the game cannot exit.
-    #[must_use = "deferrable result does nothing unless returned to the entity scheduler"]
     pub fn spawn_async<T: Send + Sync + 'static, Fut>(
         &self,
         task: impl FnOnce(HandlerContext<'static>) -> Fut,
@@ -634,7 +633,7 @@ impl<'a> EntityCoroutineServices<'a> {
     {
         let ctx: HandlerContext<'static> = HandlerContext {
             tick: self.game_state.tick(),
-            initiator: super::event::EventInitiator::Plugin("entity_coroutine".to_string()),
+            initiator: EventInitiator::Plugin("entity_coroutine".to_string()),
             game_state: self.game_state.clone(),
         };
         let fut = task(ctx);
@@ -657,7 +656,6 @@ pub struct Deferral<T: Send + 'static, Residual: Debug + Send + 'static = ()> {
     deferred_call: Pin<Box<dyn Future<Output = (T, Residual)> + Send + 'static>>,
 }
 impl Deferral<Result<BlockId>, BlockCoordinate> {
-    #[must_use = "coroutine result does nothing unless returned to the entity scheduler"]
     /// Returns a CoroutineResult that will instruct the entity system to run the deferred computation,
     /// and then re-enter the coroutine by calling [EntityCoroutine::continuation] with a ContinuationResult
     /// containing a [ContinuationResultValue::GetBlock] with the result of the deferred computation
@@ -758,7 +756,6 @@ impl_deferral!(EntityMoveDecision, EntityDecision);
 impl_deferral!(HeapResult, HeapResult);
 
 impl Deferral<ContinuationResultValue, ()> {
-    #[must_use = "coroutine result does nothing unless returned to the entity scheduler"]
     pub fn defer_and_reinvoke(self, tag: u32) -> CoroutineResult {
         CoroutineResult::_DeferredReenterCoroutine(DeferredPrivate {
             deferred_call: Box::pin(async move {
@@ -771,7 +768,6 @@ impl Deferral<ContinuationResultValue, ()> {
 }
 
 impl<T: Send + Sync + 'static, Residual: Debug + Send + Sync + 'static> Deferral<T, Residual> {
-    #[must_use = "coroutine result does nothing unless returned to the entity scheduler"]
     pub fn and_then(
         self,
         f: impl FnOnce(T) -> EntityMoveDecision + Send + Sync + 'static,
@@ -805,7 +801,6 @@ impl<T: Send + Sync + 'static, Residual: Debug + Send + Sync + 'static> From<Def
 impl<T: Send + Sync + 'static, Residual: Debug + Send + Sync + 'static>
     DeferrableResult<T, Residual>
 {
-    #[must_use = "coroutine result does nothing unless returned to the entity scheduler"]
     pub fn map<U: Send + Sync + 'static>(
         self,
         f: impl FnOnce(T) -> U + Send + Sync + 'static,
@@ -818,7 +813,6 @@ impl<T: Send + Sync + 'static, Residual: Debug + Send + Sync + 'static>
 }
 
 impl<T: Send + Sync + 'static, Residual: Debug + Send + Sync + 'static> Deferral<T, Residual> {
-    #[must_use = "coroutine result does nothing unless returned to the entity scheduler"]
     pub fn map<U: Send + Sync + 'static>(
         self,
         f: impl FnOnce(T) -> U + Send + Sync + 'static,
@@ -939,8 +933,8 @@ const ID_MASK: u64 = (1 << 48) - 1;
 #[derive(Debug)]
 enum MoveQueue {
     SingleMove(Option<StoredMovement>),
-    Buffer8(Box<circular_buffer::CircularBuffer<8, StoredMovement>>),
-    Buffer64(Box<circular_buffer::CircularBuffer<64, StoredMovement>>),
+    Buffer8(Box<CircularBuffer<8, StoredMovement>>),
+    Buffer64(Box<CircularBuffer<64, StoredMovement>>),
 }
 
 impl MoveQueue {
@@ -1022,7 +1016,7 @@ impl MoveQueue {
 
     fn as_slice(&self) -> &[StoredMovement] {
         match self {
-            MoveQueue::SingleMove(x) => x.as_ref().map(|x| std::slice::from_ref(x)).unwrap_or(&[]),
+            MoveQueue::SingleMove(x) => x.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
             MoveQueue::Buffer8(x) => {
                 let (left_slice, right_slice) = x.as_slices();
                 if right_slice.is_empty() {
@@ -1164,9 +1158,7 @@ impl EntityCoreArray {
         const MAX_WAIT_TIME_TICKS: u64 = 10 * 1_000_000_000;
         let awakening_bound = update_tick + MAX_WAIT_TIME_TICKS;
 
-        let next_event = next_event.min(awakening_bound).max(update_tick);
-
-        next_event
+        next_event.min(awakening_bound).max(update_tick)
     }
 
     /// Run coroutines present on entities that need coroutine based recalcs.
@@ -1187,7 +1179,7 @@ impl EntityCoreArray {
     ) -> u64 {
         let _span = span!("entity_run_coroutines");
         self.check_preconditions();
-        let mut next_event = std::u64::MAX;
+        let mut next_event = u64::MAX;
         for i in 0..self.len {
             next_event =
                 next_event.min(self.run_coro_single(i, services, None, sender, game_state.tick()));
@@ -1337,10 +1329,8 @@ impl EntityCoreArray {
         };
         let queue = match entity_def.move_queue_type {
             MoveQueueType::SingleMove => MoveQueue::SingleMove(None),
-            MoveQueueType::Buffer8 => MoveQueue::Buffer8(circular_buffer::CircularBuffer::boxed()),
-            MoveQueueType::Buffer64 => {
-                MoveQueue::Buffer64(circular_buffer::CircularBuffer::boxed())
-            }
+            MoveQueueType::Buffer8 => MoveQueue::Buffer8(CircularBuffer::boxed()),
+            MoveQueueType::Buffer64 => MoveQueue::Buffer64(CircularBuffer::boxed()),
         };
         let i = match index {
             Some(i) => {
@@ -2070,7 +2060,7 @@ impl EntityCoreArray {
     /// Returns the elapsed time for the current entity, saturating to 0 if it's negative
     fn move_elapsed(&self, i: usize, tick: u64) -> f32 {
         // We need a saturating subtraction here because we don't want to go negative
-        (tick.saturating_sub(self.move_start_tick[i])) as f32 / 1_000_000_000.0
+        tick.saturating_sub(self.move_start_tick[i]) as f32 / 1_000_000_000.0
     }
 
     fn move_time_in_ticks(&self, i: usize) -> u64 {
@@ -2081,7 +2071,7 @@ impl EntityCoreArray {
         if approx_ticks >= u64::MAX as f32 {
             return u64::MAX;
         }
-        (approx_ticks) as u64
+        approx_ticks as u64
     }
 
     #[cold]
@@ -2223,7 +2213,6 @@ impl EntityShardWorker {
     }
 
     pub(crate) async fn run_loop(&self) -> Result<()> {
-        let last_iteration = self.game_state.tick();
         let mut rx_lock = self.entities.shards[self.shard_id]
             .pending_actions_rx
             .lock()
@@ -2370,7 +2359,7 @@ impl EntityShardWorker {
         }
         indices.sort();
         indices.dedup();
-        let mut next_event = std::u64::MAX;
+        let mut next_event = u64::MAX;
         for index in indices {
             if index != 0 {
                 //println!("post control message");
@@ -2391,7 +2380,7 @@ impl EntityShardWorker {
         let mut lock = self.entities.shards[self.shard_id].core.write();
         let completion_tx = &self.entities.shards[self.shard_id].completion_tx;
 
-        let mut next_event = std::u64::MAX;
+        let mut next_event = u64::MAX;
         for completion in completions.drain(..) {
             completion
                 .trace_buffer
@@ -2464,7 +2453,7 @@ pub enum MoveQueueType {
 pub struct EntityDef {
     pub move_queue_type: MoveQueueType,
     pub class_name: String,
-    pub client_info: perovskite_core::protocol::entities::EntityAppearance,
+    pub client_info: EntityAppearance,
 }
 
 pub struct EntityClass {
@@ -2550,7 +2539,7 @@ impl EntityTypeManager {
         }
 
         Ok(EntityTypeManager {
-            types: types,
+            types,
             by_name: FxHashMap::default(),
         })
     }
@@ -2636,7 +2625,7 @@ fn make_unknown_entity_appearance() -> Option<EntityDef> {
     Some(EntityDef {
         class_name: "builtin:unknown".to_string(),
         move_queue_type: MoveQueueType::SingleMove,
-        client_info: protocol::entities::EntityAppearance {
+        client_info: EntityAppearance {
             custom_mesh: vec![formats::load_obj_mesh(
                 UNKNOWN_ENTITITY_MESH,
                 FALLBACK_UNKNOWN_TEXTURE,
diff --git a/perovskite_server/src/game_state/game_map.rs b/perovskite_server/src/game_state/game_map.rs
index a1eb0f8..e61928f 100644
--- a/perovskite_server/src/game_state/game_map.rs
+++ b/perovskite_server/src/game_state/game_map.rs
@@ -249,7 +249,7 @@ impl MapChunk {
                             items: game_state.item_manager(),
                         };
 
-                        (serializer)(handler_context, x)?
+                        serializer(handler_context, x)?
                     }
                     None => {
                         if ext_data.custom_data.is_some() {
@@ -392,7 +392,7 @@ fn parse_v1(
     game_state: Arc<GameState>,
     storage: Arc<[AtomicU32; 4096]>,
     run_cold_load_postprocessors: bool,
-) -> std::result::Result<MapChunk, anyhow::Error> {
+) -> std::result::Result<MapChunk, Error> {
     let mut extended_data = FxHashMap::default();
     ensure!(
         chunk_data.block_ids.len() == 4096,
@@ -405,7 +405,7 @@ fn parse_v1(
                 bytemuck::cast_slice_mut(chunk_data.block_ids.as_mut_slice());
             let sized: &mut [BlockId; 4096] =
                 cast.try_into().expect("Failed to convert to BlockId array");
-            (processor)(sized);
+            processor(sized);
         }
     }
 
@@ -535,7 +535,7 @@ impl<'a> DerefMut for MapChunkInnerWriteGuard<'a> {
 
 enum HolderState {
     Empty,
-    Err(anyhow::Error),
+    Err(Error),
     Ok(MapChunk),
 }
 impl HolderState {
@@ -734,7 +734,7 @@ impl MapChunkHolder {
     }
 
     /// Set the chunk to an error, and notify any waiting threads so they can propagate the error
-    fn set_err(&self, err: anyhow::Error) {
+    fn set_err(&self, err: Error) {
         let mut guard = self.chunk.write();
         assert!(matches!(*guard, HolderState::Empty));
         *guard = HolderState::Err(err);
@@ -1243,7 +1243,7 @@ impl ServerGameMap {
         offset: ChunkOffset,
         mutator: F,
         game_map: &ServerGameMap,
-    ) -> anyhow::Result<(T, bool)>
+    ) -> Result<(T, bool)>
     where
         F: FnOnce(&mut BlockTypeHandle, &mut ExtendedDataHolder) -> Result<T>,
     {
@@ -1407,7 +1407,7 @@ impl ServerGameMap {
 
     // Gets a chunk, loading it from database/generating it if it is not in memory
     #[tracing::instrument(level = "trace", name = "get_chunk", skip(self))]
-    fn get_chunk<'a>(&'a self, coord: ChunkCoordinate) -> Result<MapChunkOuterGuard<'a>> {
+    fn get_chunk(&self, coord: ChunkCoordinate) -> Result<MapChunkOuterGuard> {
         log_trace("get_chunk starting");
         let writeback_permit = self.get_writeback_permit(shard_id(coord))?;
         log_trace("get_chunk acquired writeback permit");
@@ -1443,7 +1443,7 @@ impl ServerGameMap {
             log_trace("get_chunk acquired write lock");
             if write_guard.chunks.contains_key(&coord) {
                 // Someone raced with us. Try looping again.
-                log::info!("Race while upgrading in get_chunk; retrying two-phase lock");
+                info!("Race while upgrading in get_chunk; retrying two-phase lock");
                 log_trace("get_chunk race detected");
                 drop(write_guard);
                 continue;
@@ -1489,17 +1489,17 @@ impl ServerGameMap {
             }
         };
         if load_chunk_tries > 1 {
-            log::warn!("Took {load_chunk_tries} tries to load {coord:?}");
+            warn!("Took {load_chunk_tries} tries to load {coord:?}");
         }
         result
     }
 
     #[tracing::instrument(level = "trace", name = "try_get_chunk", skip(self))]
-    fn try_get_chunk<'a>(
-        &'a self,
+    fn try_get_chunk(
+        &self,
         coord: ChunkCoordinate,
         want_permit: bool,
-    ) -> Option<MapChunkOuterGuard<'a>> {
+    ) -> Option<MapChunkOuterGuard> {
         let shard = shard_id(coord);
         let mut permit = None;
         if want_permit {
@@ -1603,7 +1603,7 @@ impl ServerGameMap {
                     panic!("chunk unload got a chunk with an empty holder. This should never happen - please file a bug");
                 }
                 HolderState::Err(e) => {
-                    log::warn!("chunk unload trying to unload a chunk with an error: {e:?}");
+                    warn!("chunk unload trying to unload a chunk with an error: {e:?}");
                 }
                 HolderState::Ok(chunk) => {
                     if chunk.dirty {
@@ -1706,7 +1706,7 @@ impl ServerGameMap {
                 let cleanup_handle = self.cleanup_handles[i].lock().take();
                 cleanup_handle.unwrap().await??;
             }
-            Ok::<_, anyhow::Error>(())
+            Ok::<_, Error>(())
         };
         match tokio::time::timeout(Duration::from_secs(10), await_task).await {
             Ok(Ok(())) => {
@@ -1728,7 +1728,7 @@ impl ServerGameMap {
         for shard in 0..NUM_CHUNK_SHARDS {
             let mut lock = self.live_chunks[shard].write();
             let coords: Vec<_> = lock.chunks.keys().copied().collect();
-            log::info!(
+            info!(
                 "ServerGameMap shard {} being flushed: Writing back {} chunks",
                 shard,
                 coords.len()
@@ -1833,7 +1833,7 @@ impl MapCacheCleanup {
                 .skip(CACHE_CLEANUP_KEEP_N_RECENTLY_USED)
                 .take(CACHE_CLEANUP_RELOCK_EVERY_N)
             {
-                assert!(self.shard_id == shard_id(entry.1));
+                assert_eq!(self.shard_id, shard_id(entry.1));
                 self.map.unload_chunk_locked(&mut lock, entry.1)?;
             }
             if num_entries > (CACHE_CLEANUP_RELOCK_EVERY_N + CACHE_CLEANUP_KEEP_N_RECENTLY_USED) {
@@ -1868,7 +1868,7 @@ impl GameMapWriteback {
             tokio::task::block_in_place(|| self.do_writebacks(writebacks))?;
         }
 
-        log::info!("Map writeback exiting");
+        info!("Map writeback exiting");
         Ok(())
     }
 
@@ -1882,7 +1882,7 @@ impl GameMapWriteback {
             self.map.live_chunks[self.shard_id].read_recursive()
         };
         for coord in writebacks {
-            assert!(self.shard_id == shard_id(coord));
+            assert_eq!(self.shard_id, shard_id(coord));
             match lock.chunks.get(&coord) {
                 Some(chunk_holder) => {
                     if let Some(mut chunk) = chunk_holder.try_get_write()? {
@@ -2373,7 +2373,9 @@ impl GameMapTimer {
                         if !passed_block_presence {
                             continue;
                         }
-                        let last_update = (upper_chunk.last_written.get_acquire())
+                        let last_update = upper_chunk
+                            .last_written
+                            .get_acquire()
                             .max(lower_chunk.last_written.get_acquire());
                         let should_run = !self.settings.idle_chunk_after_unchanged
                             || last_update >= state.timer_state.prev_tick_time;
@@ -2963,7 +2965,7 @@ fn reconcile_after_bulk_handler(
 fn reacquire_writeback_permit<'a, 'b>(
     game_state: &'a Arc<GameState>,
     coarse_shard: usize,
-    read_lock: &mut parking_lot::RwLockReadGuard<'_, MapShard>,
+    read_lock: &mut RwLockReadGuard<'_, MapShard>,
 ) -> Result<mpsc::Permit<'b, WritebackReq>, Error>
 where
     'a: 'b,
diff --git a/perovskite_server/src/game_state/inventory.rs b/perovskite_server/src/game_state/inventory.rs
index e517d2d..57f4743 100644
--- a/perovskite_server/src/game_state/inventory.rs
+++ b/perovskite_server/src/game_state/inventory.rs
@@ -906,7 +906,7 @@ impl<T> InventoryView<T> {
                 borrows_from: BorrowLocation::VirtualOutput(self.id, slot, behavior),
                 borrowed_stack: x,
             })),
-            ViewBacking::VirtualInput(vi) => {
+            ViewBacking::VirtualInput(_vi) => {
                 bail!("Can't take from virtual input")
             }
             ViewBacking::Stored(key) => Ok(self
diff --git a/perovskite_server/src/game_state/player.rs b/perovskite_server/src/game_state/player.rs
index 378db14..8f5f21d 100644
--- a/perovskite_server/src/game_state/player.rs
+++ b/perovskite_server/src/game_state/player.rs
@@ -467,7 +467,7 @@ impl PlayerState {
                         let still_leftover =
                             source_view.put(action.source_slot as usize, leftover)?;
                         if let Some(still_leftover) = still_leftover {
-                            log::warn!("Still-leftover items were destroyed: {:?}", still_leftover);
+                            warn!("Still-leftover items were destroyed: {:?}", still_leftover);
                         }
                     }
                 }
@@ -743,7 +743,7 @@ impl PlayerManager {
     pub async fn for_all_connected_players_async<F, G>(&self, mut closure: F) -> Result<()>
     where
         F: FnMut(&Player) -> G,
-        G: Future<Output = anyhow::Result<()>>,
+        G: Future<Output = Result<()>>,
     {
         let lock = tokio::task::block_in_place(|| self.active_players.read());
         for (_, player) in lock.iter() {
diff --git a/perovskite_server/src/network_server/client_context.rs b/perovskite_server/src/network_server/client_context.rs
index 86a0f3f..e19083a 100644
--- a/perovskite_server/src/network_server/client_context.rs
+++ b/perovskite_server/src/network_server/client_context.rs
@@ -169,7 +169,7 @@ pub(crate) async fn make_client_contexts(
 
     let initial_position = PlayerPositionUpdate {
         position: player_context.last_position().position,
-        velocity: cgmath::Vector3::zero(),
+        velocity: Vector3::zero(),
         face_direction: (0., 0.),
     };
     let (pos_send, pos_recv) = watch::channel(PositionAndPacing {
@@ -284,9 +284,9 @@ async fn initialize_protocol_state(
     if !context.player_context.has_permission(permissions::LOG_IN) {
         context.cancellation.cancel();
         outbound_tx
-            .send(Ok(proto::StreamToClient {
+            .send(Ok(StreamToClient {
                 tick: context.game_state.tick(),
-                server_message: Some(proto::stream_to_client::ServerMessage::ShutdownMessage(
+                server_message: Some(ServerMessage::ShutdownMessage(
                     "You don't have permission to log in".to_string(),
                 )),
             }))
@@ -478,7 +478,7 @@ impl MapChunkSender {
                     // cancel the inbound context as well
                     self.context.cancellation.cancel();
                 }
-            };
+            }
         }
         Ok(())
     }
@@ -526,10 +526,9 @@ impl MapChunkSender {
         let player_block_coord: BlockCoordinate = match position.position.try_into() {
             Ok(x) => x,
             Err(e) => {
-                log::warn!(
+                warn!(
                     "Player had invalid position: {:?}, error {:?}",
-                    position.position,
-                    e
+                    position.position, e
                 );
                 let fixed_position = self.fix_position_and_notify(position.position).await?;
                 self.context
@@ -558,9 +557,9 @@ impl MapChunkSender {
             .filter(|&x| self.should_unload(player_chunk, *x))
             .cloned()
             .collect();
-        let message = proto::StreamToClient {
+        let message = StreamToClient {
             tick: self.context.game_state.tick(),
-            server_message: Some(proto::stream_to_client::ServerMessage::MapChunkUnsubscribe(
+            server_message: Some(ServerMessage::MapChunkUnsubscribe(
                 proto::MapChunkUnsubscribe {
                     chunk_coord: chunks_to_unsubscribe.iter().map(|&x| x.into()).collect(),
                 },
@@ -586,9 +585,11 @@ impl MapChunkSender {
             let finished_distance = MAX_INDEX_FOR_DISTANCE
                 .partition_point(|&x| x < self.processed_elements)
                 .saturating_sub(2);
-            let new_distance = (finished_distance.saturating_sub(moved_distance as usize)).max(0);
+            let new_distance = finished_distance
+                .saturating_sub(moved_distance as usize)
+                .max(0);
             self.processed_elements
-                .min(MIN_INDEX_FOR_DISTANCE[new_distance as usize])
+                .min(MIN_INDEX_FOR_DISTANCE[new_distance])
         };
 
         let start_time = Instant::now();
@@ -642,7 +643,7 @@ impl MapChunkSender {
             if i % 100 == 0 {
                 tokio::task::yield_now().await;
             }
-            let chunk_data = tokio::task::block_in_place(|| {
+            let chunk_data = block_in_place(|| {
                 self.context
                     .game_state
                     .game_map()
@@ -650,14 +651,12 @@ impl MapChunkSender {
             })?;
             if let Some(chunk_data) = chunk_data {
                 let chunk_bytes = self.snappy_encode(&chunk_data)?;
-                let message = proto::StreamToClient {
+                let message = StreamToClient {
                     tick: self.context.game_state.tick(),
-                    server_message: Some(proto::stream_to_client::ServerMessage::MapChunk(
-                        proto::MapChunk {
-                            chunk_coord: Some(coord.into()),
-                            snappy_encoded_bytes: chunk_bytes,
-                        },
-                    )),
+                    server_message: Some(ServerMessage::MapChunk(proto::MapChunk {
+                        chunk_coord: Some(coord.into()),
+                        snappy_encoded_bytes: chunk_bytes,
+                    })),
                 };
                 self.outbound_tx.send(Ok(message)).await.map_err(|_| {
                     self.context.cancellation.cancel();
@@ -732,7 +731,7 @@ pub(crate) struct BlockEventSender {
     context: Arc<SharedContext>,
 
     // RPC stream is sent via this channel
-    outbound_tx: mpsc::Sender<tonic::Result<proto::StreamToClient>>,
+    outbound_tx: mpsc::Sender<tonic::Result<StreamToClient>>,
 
     // All updates to the map from all sources, not yet filtered by location (BlockEventSender is
     // responsible for filtering)
@@ -761,7 +760,7 @@ impl BlockEventSender {
                     info!("Client outbound loop {} detected cancellation and shutting down", self.context.id)
                     // pass
                 }
-            };
+            }
         }
         Ok(())
     }
@@ -835,13 +834,11 @@ impl BlockEventSender {
         }
 
         if !update_protos.is_empty() {
-            let message = proto::StreamToClient {
+            let message = StreamToClient {
                 tick: self.context.game_state.tick(),
-                server_message: Some(proto::stream_to_client::ServerMessage::MapDeltaUpdate(
-                    MapDeltaUpdateBatch {
-                        updates: update_protos,
-                    },
-                )),
+                server_message: Some(ServerMessage::MapDeltaUpdate(MapDeltaUpdateBatch {
+                    updates: update_protos,
+                })),
             };
             self.outbound_tx
                 .send(Ok(message))
@@ -907,24 +904,20 @@ fn make_client_state_update_message(
         )?;
         StreamToClient {
             tick: ctx.game_state.tick(),
-            server_message: Some(proto::stream_to_client::ServerMessage::ClientState(
-                proto::SetClientState {
-                    position,
-                    hotbar_inventory_view: player_state.hotbar_inventory_view.id.0,
-                    inventory_popup: Some(
-                        player_state.inventory_popup.as_ref().unwrap().to_proto(),
-                    ),
-                    inventory_manipulation_view: player_state.inventory_manipulation_view.id.0,
-                    time_of_day: time_now,
-                    day_length_sec: day_len.as_secs_f64(),
-                    permission: player_state
-                        .effective_permissions(&ctx.game_state, ctx.player_context.name())
-                        .iter()
-                        .cloned()
-                        .collect(),
-                    attached_to_entity: player_state.attached_to_entity.unwrap_or(0),
-                },
-            )),
+            server_message: Some(ServerMessage::ClientState(proto::SetClientState {
+                position,
+                hotbar_inventory_view: player_state.hotbar_inventory_view.id.0,
+                inventory_popup: Some(player_state.inventory_popup.as_ref().unwrap().to_proto()),
+                inventory_manipulation_view: player_state.inventory_manipulation_view.id.0,
+                time_of_day: time_now,
+                day_length_sec: day_len.as_secs_f64(),
+                permission: player_state
+                    .effective_permissions(&ctx.game_state, ctx.player_context.name())
+                    .iter()
+                    .cloned()
+                    .collect(),
+                attached_to_entity: player_state.attached_to_entity.unwrap_or(0),
+            })),
         }
     };
 
@@ -934,7 +927,7 @@ fn make_client_state_update_message(
 pub(crate) struct InventoryEventSender {
     context: Arc<SharedContext>,
     // RPC stream is sent via this channel
-    outbound_tx: mpsc::Sender<tonic::Result<proto::StreamToClient>>,
+    outbound_tx: mpsc::Sender<tonic::Result<StreamToClient>>,
 
     // TODO consider delta updates for this
     inventory_events: broadcast::Receiver<UpdatedInventory>,
@@ -959,7 +952,7 @@ impl InventoryEventSender {
                     info!("Client outbound loop {} detected cancellation and shutting down", self.context.id)
                     // pass
                 }
-            };
+            }
         }
         Ok(())
     }
@@ -1005,7 +998,7 @@ impl InventoryEventSender {
             tracing::Level::TRACE,
             "filter and serialize inventory updates"
         )
-        .in_scope(|| -> anyhow::Result<_> {
+        .in_scope(|| -> Result<_> {
             let player_state = self.context.player_context.state.lock();
             let mut update_messages = vec![];
             for key in update_keys {
@@ -1058,7 +1051,7 @@ pub(crate) struct InboundWorker {
     // Note that there are some issues with race conditions here, especially around
     // handled_sequence messages getting sent befoe the outbound loop can send the actual
     // in-game effects for them. If this poses an issue, refactor later.
-    outbound_tx: mpsc::Sender<tonic::Result<proto::StreamToClient>>,
+    outbound_tx: mpsc::Sender<tonic::Result<StreamToClient>>,
     // The client's self-reported position
     own_positions: watch::Sender<PositionAndPacing>,
     next_pos_writeback: Instant,
@@ -1098,7 +1091,7 @@ impl InboundWorker {
                     info!("Client inbound context {} detected cancellation and shutting down", self.context.id)
                     // pass
                 }
-            };
+            }
         }
         Ok(())
     }
@@ -1243,7 +1236,7 @@ impl InboundWorker {
     where
         F: FnOnce(Option<&Item>) -> &items::BlockInteractionHandler,
     {
-        tokio::task::block_in_place(|| {
+        block_in_place(|| {
             self.map_handler_sync(selected_inv_slot, get_item_handler, coord, player_position)
         })
     }
@@ -1254,7 +1247,7 @@ impl InboundWorker {
         get_item_handler: F,
         coord: BlockCoordinate,
         player_position: PlayerPositionUpdate,
-    ) -> std::result::Result<(), anyhow::Error>
+    ) -> std::result::Result<(), Error>
     where
         F: FnOnce(Option<&Item>) -> &items::BlockInteractionHandler,
     {
@@ -1318,11 +1311,9 @@ impl InboundWorker {
         if sequence == 0 {
             return Ok(());
         };
-        let message = proto::StreamToClient {
+        let message = StreamToClient {
             tick: self.context.game_state.tick(),
-            server_message: Some(proto::stream_to_client::ServerMessage::HandledSequence(
-                sequence,
-            )),
+            server_message: Some(ServerMessage::HandledSequence(sequence)),
         };
 
         self.outbound_tx.send(Ok(message)).await.map_err(|_| {
@@ -1339,7 +1330,7 @@ impl InboundWorker {
                 let (az, el) = match &pos_update.face_direction {
                     Some(x) => (x.deg_azimuth, x.deg_elevation),
                     None => {
-                        log::warn!("No angles in update from client");
+                        warn!("No angles in update from client");
                         (0., 0.)
                     }
                 };
@@ -1403,7 +1394,7 @@ impl InboundWorker {
         ),
     )]
     async fn handle_place(&mut self, place_message: &proto::PlaceAction) -> Result<()> {
-        tokio::task::block_in_place(|| {
+        block_in_place(|| {
             log_trace("Running place handlers");
             let _span = span!("handle_place");
             self.context
@@ -1490,7 +1481,7 @@ impl InboundWorker {
         let updates =
             {
                 let mut views_to_send = HashSet::new();
-                block_in_place(|| -> anyhow::Result<Vec<StreamToClient>> {
+                block_in_place(|| -> Result<Vec<StreamToClient>> {
                     log_trace("Locking player for inventory action");
                     let mut player_state = self.context.player_context.player.state.lock();
                     log_trace("Locked player for inventory action");
@@ -1528,7 +1519,7 @@ impl InboundWorker {
                         &&player_state.inventory_manipulation_view,
                     )?);
                     log_trace("Done running inventory action");
-                    anyhow::Result::Ok(updates)
+                    Ok(updates)
                 })?
             };
         for update in updates {
@@ -1562,7 +1553,7 @@ impl InboundWorker {
         } else {
             PopupAction::ButtonClicked(action.clicked_button.clone())
         };
-        let updates = tokio::task::block_in_place(|| -> anyhow::Result<_> {
+        let updates = block_in_place(|| -> Result<_> {
             let _span = span!("handle_popup_response");
             let ctx = HandlerContext {
                 tick: self.context.game_state.tick(),
@@ -1677,7 +1668,7 @@ impl InboundWorker {
             }
             // drop before async calls
             drop(player_state);
-            anyhow::Result::Ok(updates)
+            Ok(updates)
         })?;
         for update in updates {
             self.outbound_tx
@@ -1701,9 +1692,8 @@ impl InboundWorker {
         &mut self,
         interact_message: &proto::InteractKeyAction,
     ) -> Result<()> {
-        let messages = tokio::task::block_in_place(|| -> anyhow::Result<_> {
-            self.handle_interact_key_sync(interact_message)
-        })?;
+        let messages =
+            block_in_place(|| -> Result<_> { self.handle_interact_key_sync(interact_message) })?;
         for message in messages {
             self.outbound_tx
                 .send(Ok(message))
diff --git a/perovskite_server/src/network_server/grpc_service.rs b/perovskite_server/src/network_server/grpc_service.rs
index 6cf6163..04a8c88 100644
--- a/perovskite_server/src/network_server/grpc_service.rs
+++ b/perovskite_server/src/network_server/grpc_service.rs
@@ -34,17 +34,17 @@ use crate::game_state::GameState;
 
 use super::client_context::make_client_contexts;
 
-fn get_metadata(req_metadata: &MetadataMap, key: &str) -> Result<String, tonic::Status> {
+fn get_metadata(req_metadata: &MetadataMap, key: &str) -> Result<String, Status> {
     match req_metadata.get(key).map(|x| x.to_str()) {
         Some(Ok(x)) => Ok(x.to_string()),
         Some(Err(e)) => {
             warn!("Could not decode string metadata for {}: {:?}", key, e);
-            Err(tonic::Status::invalid_argument(format!(
+            Err(Status::invalid_argument(format!(
                 "String decode error for {}",
                 key
             )))
         }
-        None => Err(tonic::Status::invalid_argument(format!("Missing {}", key))),
+        None => Err(Status::invalid_argument(format!("Missing {}", key))),
     }
 }
 
@@ -60,12 +60,11 @@ impl PerovskiteGameServerImpl {
 
 #[tonic::async_trait]
 impl PerovskiteGame for PerovskiteGameServerImpl {
-    type GameStreamStream =
-        Pin<Box<dyn Stream<Item = Result<proto::StreamToClient, Status>> + Send>>;
+    type GameStreamStream = Pin<Box<dyn Stream<Item = Result<StreamToClient, Status>> + Send>>;
 
     async fn game_stream(
         &self,
-        req: Request<Streaming<proto::StreamToServer>>,
+        req: Request<Streaming<StreamToServer>>,
     ) -> Result<Response<Self::GameStreamStream>> {
         info!("Stream established from {:?}", req.remote_addr());
         let (outbound_tx, outbound_rx) = mpsc::channel(4);
@@ -78,14 +77,14 @@ impl PerovskiteGame for PerovskiteGameServerImpl {
         })
         .map_err(|e| Status::internal(e.to_string()))?;
 
-        Result::Ok(Response::new(Box::pin(ReceiverStream::new(outbound_rx))))
+        Ok(Response::new(Box::pin(ReceiverStream::new(outbound_rx))))
     }
 
     async fn get_block_defs(
         &self,
         _req: Request<proto::GetBlockDefsRequest>,
     ) -> Result<Response<proto::GetBlockDefsResponse>> {
-        Result::Ok(Response::new(proto::GetBlockDefsResponse {
+        Ok(Response::new(proto::GetBlockDefsResponse {
             block_types: self
                 .game_state
                 .game_map()
@@ -98,7 +97,7 @@ impl PerovskiteGame for PerovskiteGameServerImpl {
         &self,
         _req: Request<proto::GetItemDefsRequest>,
     ) -> Result<Response<proto::GetItemDefsResponse>> {
-        Result::Ok(Response::new(proto::GetItemDefsResponse {
+        Ok(Response::new(proto::GetItemDefsResponse {
             item_defs: self
                 .game_state
                 .item_manager()
@@ -118,10 +117,10 @@ impl PerovskiteGame for PerovskiteGameServerImpl {
             .get(&req.get_ref().media_name)
         {
             Some(resource) => match resource.data() {
-                Ok(data) => Result::Ok(Response::new(proto::GetMediaResponse { media: data })),
-                Err(e) => Result::Err(Status::internal(e.to_string())),
+                Ok(data) => Ok(Response::new(proto::GetMediaResponse { media: data })),
+                Err(e) => Err(Status::internal(e.to_string())),
             },
-            None => Result::Err(Status::not_found(format!(
+            None => Err(Status::not_found(format!(
                 "{} unimplemented",
                 &req.get_ref().media_name
             ))),
@@ -132,7 +131,7 @@ impl PerovskiteGame for PerovskiteGameServerImpl {
         &self,
         _req: Request<proto::ListMediaRequest>,
     ) -> Result<Response<proto::ListMediaResponse>> {
-        Result::Ok(Response::new(proto::ListMediaResponse {
+        Ok(Response::new(proto::ListMediaResponse {
             media: self
                 .game_state
                 .media_resources()
@@ -149,7 +148,7 @@ impl PerovskiteGame for PerovskiteGameServerImpl {
         &self,
         _req: Request<proto::GetEntityDefsRequest>,
     ) -> Result<Response<proto::GetEntityDefsResponse>> {
-        Result::Ok(Response::new(proto::GetEntityDefsResponse {
+        Ok(Response::new(proto::GetEntityDefsResponse {
             entity_defs: self.game_state.entities().types().to_client_protos(),
         }))
     }
@@ -160,8 +159,8 @@ pub(crate) const SERVER_MAX_PROTOCOL_VERSION: u32 = 4;
 
 async fn game_stream_impl(
     game_state: Arc<GameState>,
-    mut inbound_rx: tonic::Streaming<StreamToServer>,
-    outbound_tx: mpsc::Sender<tonic::Result<StreamToClient>>,
+    mut inbound_rx: Streaming<StreamToServer>,
+    outbound_tx: mpsc::Sender<Result<StreamToClient>>,
 ) -> anyhow::Result<()> {
     let auth_outcome = match game_state
         .auth()
@@ -169,11 +168,9 @@ async fn game_stream_impl(
         .await
     {
         Ok(outcome) => {
-            log::info!(
+            info!(
                 "Player {} successfully authenticated, protocol range {}..={}",
-                outcome.username,
-                outcome.min_protocol_version,
-                outcome.max_protocol_version
+                outcome.username, outcome.min_protocol_version, outcome.max_protocol_version
             );
             outcome
         }
@@ -188,7 +185,7 @@ async fn game_stream_impl(
 
     if SERVER_MIN_PROTOCOL_VERSION > auth_outcome.max_protocol_version {
         outbound_tx
-            .send(Err(tonic::Status::unimplemented(format!(
+            .send(Err(Status::unimplemented(format!(
                 "Client is too old; minimum server protocol version is {}",
                 SERVER_MIN_PROTOCOL_VERSION
             ))))
@@ -197,7 +194,7 @@ async fn game_stream_impl(
     }
     if SERVER_MAX_PROTOCOL_VERSION < auth_outcome.min_protocol_version {
         outbound_tx
-            .send(Err(tonic::Status::unimplemented(format!(
+            .send(Err(Status::unimplemented(format!(
                 "Client is too new; maximum server protocol version is {}",
                 SERVER_MIN_PROTOCOL_VERSION
             ))))
@@ -244,13 +241,13 @@ async fn game_stream_impl(
             ..
         }) => {
             // all OK
-            log::info!(
+            info!(
                 "Client for {} reports ready; starting up client's context on server",
                 auth_outcome.username
             );
         }
         Some(_) => {
-            let err_response = Err(tonic::Status::invalid_argument(
+            let err_response = Err(Status::invalid_argument(
                 "Client did not send a ClientInitialReady as its first message after authenticating.",
             ));
             return outbound_tx.send(err_response).await.map_err(|_| {
@@ -258,7 +255,7 @@ async fn game_stream_impl(
             });
         }
         None => {
-            let err_response = Err(tonic::Status::unavailable(
+            let err_response = Err(Status::unavailable(
                 "Client did not send a ClientInitialReady and disconnected instead.",
             ));
             return outbound_tx
@@ -282,7 +279,7 @@ async fn game_stream_impl(
         Err(e) => {
             error!("Error setting up client context: {:?}", e);
             return outbound_tx
-                .send(Err(tonic::Status::internal(format!(
+                .send(Err(Status::internal(format!(
                     "Failure setting up client contexts: {e:?}"
                 ))))
                 .await
diff --git a/perovskite_server/src/server.rs b/perovskite_server/src/server.rs
index abc8b61..f2873ca 100644
--- a/perovskite_server/src/server.rs
+++ b/perovskite_server/src/server.rs
@@ -309,7 +309,7 @@ impl ServerBuilder {
     }
 
     /// Adds an extension to the server's game state.
-    /// This extension can be accessed through [crate::game_state::GameState::extension].
+    /// This extension can be accessed through [GameState::extension].
     pub fn add_extension<T: GameStateExtension>(&mut self, extension: T) {
         if self.extensions.contains::<T>() {
             panic!("Extension already added");