alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates client src app input.rs
use common::protocol::Packet;
use common::stats::ACHIEVEMENT;
use winit::event::MouseButton;
use winit::keyboard::KeyCode;
use winit::window::{CursorGrabMode, Fullscreen};
use super::Game;
use crate::interaction::Screen;
use crate::menu::Page;
use crate::settings::Binding;
impl Game {
pub(super) fn set_grab(&mut self, grab: bool) {
let grab = grab && self.focused && self.session.is_some();
if grab == self.view.grabbed {
return;
}
let window = &self.view.window;
let result = if grab {
window
.set_cursor_grab(CursorGrabMode::Locked)
.or_else(|_| window.set_cursor_grab(CursorGrabMode::Confined))
} else if !self.focused {
window.set_cursor_grab(CursorGrabMode::None)
} else {
// `Mouse.setGrabbed(false)` leaves the cursor in the middle of the window. winit only
// hints the position of a locked pointer on Wayland, which compositors may ignore, so
// the pointer is warped after the release where supported
let size = window.inner_size();
let center = [size.width as f32 / 2.0, size.height as f32 / 2.0];
let position = winit::dpi::PhysicalPosition::new(center[0], center[1]);
let hinted = window.set_cursor_position(position);
let result = window.set_cursor_grab(CursorGrabMode::None);
let warped = self.view.pointer_warp.as_mut().is_some_and(|warp| {
let position = position.to_logical(window.scale_factor());
warp.warp(position.x, position.y)
.inspect_err(|error| tracing::warn!("cannot warp the cursor: {error}"))
.unwrap_or(false)
});
match hinted {
Err(error) if !warped => tracing::warn!("cannot center the cursor: {error}"),
_ => {
self.view.cursor = center;
if let Some(session) = &mut self.session {
session.interaction.cursor = center;
}
}
}
result
};
if let Err(error) = result {
tracing::warn!("cannot change cursor grab: {error}");
return;
}
window.set_cursor_visible(!grab);
self.view.grabbed = grab;
if !grab && let Some(session) = &mut self.session {
session.player.keys = Default::default();
session.interaction.shift = false;
session.interaction.cancel(&session.connection);
}
}
pub(super) fn focus(&mut self) {
if let Some(session) = &self.session {
session.backend.set_activity(match self.menu.page {
Some(Page::Loading) => server::Activity::Preparing,
Some(_) => server::Activity::Paused,
None => server::Activity::Running,
});
if self.menu.page.is_some() || session.interaction.screen.is_some() {
self.set_grab(false);
}
} else {
self.set_grab(false);
}
let text = self.menu.field_rects(self.gui_space().0)[0].is_some();
if self.ime_enabled != text {
self.view.window.set_ime_allowed(text);
self.ime_enabled = text;
}
}
pub(super) fn mouse(&mut self, button: MouseButton, pressed: bool) {
if self.menu.page.is_some() {
if button != MouseButton::Left {
return;
}
if !pressed {
if self.menu.mouse_up() {
self.apply_options(true);
}
return;
}
let (size, cursor) = self.gui_space();
if let Some(action) = self.menu.click(cursor, size, &mut self.options) {
self.audio.click();
self.action(action);
}
return;
}
let (size, cursor) = self.gui_space();
let Some(session) = &mut self.session else {
return;
};
if let Some(screen) = session.interaction.screen {
if !pressed {
return;
}
if screen == Screen::Inventory {
if let Some(slot) =
crate::gui::slot_at(size, session.interaction.container.kind, cursor)
&& matches!(button, MouseButton::Left | MouseButton::Right)
{
let right = i8::from(button == MouseButton::Right);
session
.interaction
.click_slot(slot, right, &session.connection);
}
} else if screen == Screen::Death && button == MouseButton::Left {
match crate::gui::button_at(size, screen, cursor) {
Some(0) => {
session.connection.send(Packet::Respawn { dimension: 0 });
session.interaction.screen = None;
self.audio.click();
self.set_grab(true);
}
Some(1) => {
self.audio.click();
self.leave();
}
_ => {}
}
}
} else if !self.view.grabbed && pressed && button == MouseButton::Left {
self.set_grab(true);
} else if self.view.grabbed {
match button {
MouseButton::Left => session.interaction.attack_button(pressed),
MouseButton::Right => session.interaction.use_button(pressed),
MouseButton::Middle if pressed => session
.interaction
.pick_block(&session.world, &session.connection),
_ => {}
}
}
}
pub(super) fn key(&mut self, code: KeyCode, pressed: bool, repeat: bool, text: Option<&str>) {
// Shortcuts that do not consume the key: in game it may also be bound to movement
if pressed
&& !repeat
&& matches!(code, KeyCode::F2 | KeyCode::F11)
&& self.menu.binding.is_none()
{
if code == KeyCode::F2 {
self.view.renderer.request_screenshot();
} else {
let window = &self.view.window;
let fullscreen = Fullscreen::Borderless(None);
window.set_fullscreen(window.fullscreen().is_none().then_some(fullscreen));
}
if self.menu.page.is_some() {
return;
}
}
if self.menu.page.is_some() {
if !pressed
|| (repeat
&& (self.menu.binding.is_some()
|| matches!(
code,
KeyCode::Enter | KeyCode::NumpadEnter | KeyCode::Escape | KeyCode::Tab
)))
{
return;
}
if self.modifiers.control_key()
&& self.menu.binding.is_none()
&& let Some(field) = self.menu.fields.iter_mut().find(|field| field.focused)
&& matches!(code, KeyCode::KeyV | KeyCode::KeyC | KeyCode::KeyX)
{
let clipboard = self.clipboard.as_mut();
if code == KeyCode::KeyV {
if let Some(Ok(text)) = clipboard.map(arboard::Clipboard::get_text) {
field.insert(&text);
}
} else {
if let Some(clipboard) = clipboard {
let _ = clipboard.set_text(field.text[field.selection()].to_owned());
}
if code == KeyCode::KeyX {
field.insert("");
}
}
self.update_folder();
return;
}
let binding = self.menu.binding.is_some();
let action = self.menu.key(
code,
text,
self.modifiers.control_key(),
self.modifiers.shift_key(),
self.gui_space().0,
&mut self.options,
);
if binding {
self.apply_options(true);
}
self.update_folder();
if let Some(action) = action {
self.audio.click();
self.action(action);
}
return;
}
if repeat {
return;
}
let Some(session) = &mut self.session else {
return;
};
if matches!(code, KeyCode::ShiftLeft | KeyCode::ShiftRight) {
self.shift_keys[usize::from(code == KeyCode::ShiftRight)] = pressed;
session.interaction.shift = self.shift_keys.contains(&true);
}
if pressed && (code == KeyCode::Escape || self.options.matches(Binding::Inventory, code)) {
match session.interaction.screen {
Some(Screen::Death) => {}
Some(_) => {
session.interaction.close(&session.connection);
self.set_grab(true);
}
None if code == KeyCode::Escape => self.open(Page::Pause),
None => {
session.interaction.screen = Some(Screen::Inventory);
self.statistics.stats.lock().unwrap().add(ACHIEVEMENT, 1);
self.focus();
}
}
return;
}
if !self.view.grabbed || session.interaction.screen.is_some() {
return;
}
if pressed {
match code {
KeyCode::F1 => session.hide_gui = !session.hide_gui,
KeyCode::F3 => session.show_debug = !session.show_debug,
KeyCode::F5 => session.player.third_person = !session.player.third_person,
KeyCode::F8 => session.smooth_camera = !session.smooth_camera,
_ => {}
}
// The LWJGL codes of the keys 1 to 9
if let Some(key @ 2..=10) = crate::settings::key_code(code) {
session
.interaction
.select(key as usize - 2, &session.connection);
}
if self.options.matches(Binding::Drop, code) {
session.interaction.drop_selected(&session.connection);
}
// `GuiChat` only exists in multiplayer
if self.options.matches(Binding::Chat, code) && !session.backend.local() {
self.open(Page::Chat);
return;
}
}
let keys = &mut session.player.keys;
for (binding, key) in [
(Binding::Forward, &mut keys.forward),
(Binding::Back, &mut keys.back),
(Binding::Left, &mut keys.left),
(Binding::Right, &mut keys.right),
(Binding::Jump, &mut keys.jump),
(Binding::Sneak, &mut keys.sneak),
] {
if self.options.matches(binding, code) {
*key = pressed;
}
}
if pressed && self.options.matches(Binding::Fog, code) {
let distance = self.options.render_distance;
self.options.render_distance = if self.modifiers.shift_key() {
distance.farther()
} else {
distance.shorter()
};
self.apply_options(true);
}
}
}