Merge branch 'zesterer/winit-20' into 'master'

Upgrade winit to 0.22.2

See merge request veloren/veloren!788
This commit is contained in:
Imbris 2020-07-18 03:26:59 +00:00
commit 23e1378c78
18 changed files with 1195 additions and 962 deletions

View File

@ -71,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Animals are more effective in combat
- Pathfinding is much smoother and pets are cleverer
- Animals run/turn at different speeds
- Updated windowing library (winit 0.19 -> 0.22)
### Removed

357
Cargo.lock generated
View File

@ -63,6 +63,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"
[[package]]
name = "android_log-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8052e2d8aabbb8d556d6abbcce2a22b9590996c5f849b9c7ce4544a2e3b984e"
[[package]]
name = "ansi_term"
version = "0.11.0"
@ -428,6 +434,17 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "calloop"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160"
dependencies = [
"mio",
"mio-extras",
"nix 0.14.1",
]
[[package]]
name = "cast"
version = "0.2.3"
@ -463,11 +480,10 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cgl"
version = "0.2.3"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49"
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
dependencies = [
"gleam",
"libc",
]
@ -530,14 +546,14 @@ dependencies = [
[[package]]
name = "cocoa"
version = "0.18.5"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54"
checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400"
dependencies = [
"bitflags",
"block",
"core-foundation",
"core-graphics",
"core-foundation 0.6.4",
"core-graphics 0.17.3",
"foreign-types",
"libc",
"objc",
@ -545,14 +561,14 @@ dependencies = [
[[package]]
name = "cocoa"
version = "0.19.1"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400"
checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8"
dependencies = [
"bitflags",
"block",
"core-foundation",
"core-graphics",
"core-foundation 0.7.0",
"core-graphics 0.19.2",
"foreign-types",
"libc",
"objc",
@ -561,7 +577,7 @@ dependencies = [
[[package]]
name = "conrod_core"
version = "0.63.0"
source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927"
source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30"
dependencies = [
"conrod_derive",
"copypasta",
@ -576,7 +592,7 @@ dependencies = [
[[package]]
name = "conrod_derive"
version = "0.63.0"
source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927"
source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
@ -586,7 +602,7 @@ dependencies = [
[[package]]
name = "conrod_winit"
version = "0.63.0"
source = "git+https://gitlab.com/veloren/conrod.git?branch=pre-winit-20#46b374edc9537300e5278905ebd14dff45cfd927"
source = "git+https://gitlab.com/veloren/conrod.git#1ab6eccf94b16a8977a3274b31d4dbfef9cf9a30"
[[package]]
name = "const-random"
@ -663,7 +679,7 @@ dependencies = [
"objc-foundation",
"objc_id",
"smithay-clipboard",
"wayland-client 0.23.6",
"wayland-client",
"x11-clipboard",
]
@ -673,7 +689,17 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
dependencies = [
"core-foundation-sys",
"core-foundation-sys 0.6.2",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
dependencies = [
"core-foundation-sys 0.7.0",
"libc",
]
@ -683,6 +709,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
[[package]]
name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
[[package]]
name = "core-graphics"
version = "0.17.3"
@ -690,11 +722,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation 0.6.4",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923"
dependencies = [
"bitflags",
"core-foundation 0.7.0",
"foreign-types",
"libc",
]
[[package]]
name = "core-video-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
dependencies = [
"cfg-if",
"core-foundation-sys 0.7.0",
"core-graphics 0.19.2",
"libc",
"objc",
]
[[package]]
name = "coreaudio-rs"
version = "0.9.1"
@ -721,7 +778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b55d55d69f403f62a95bd3c04b431e0aedf5120c70f15d07a8edd234443dd59"
dependencies = [
"alsa-sys",
"core-foundation-sys",
"core-foundation-sys 0.6.2",
"coreaudio-rs",
"lazy_static",
"libc",
@ -992,6 +1049,17 @@ dependencies = [
"byteorder 1.3.4",
]
[[package]]
name = "derivative"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.33",
]
[[package]]
name = "deunicode"
version = "1.1.1"
@ -1063,6 +1131,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e93ca78226c51902d7aa8c12c988338aadd9e85ed9c6be8aaac39192ff3605"
[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dlib"
version = "0.4.2"
@ -1505,17 +1579,6 @@ dependencies = [
"gl_generator 0.14.0",
]
[[package]]
name = "gfx_window_glutin"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310ff66f08b5a55854b18fea2f48bdbb75c94458207ba574c9723be78e97a646"
dependencies = [
"gfx_core",
"gfx_device_gl",
"glutin",
]
[[package]]
name = "gilrs"
version = "0.7.4"
@ -1537,7 +1600,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43c758daf46af26d6872fe55507e3b2339779a160a06ad7a9b2a082f221209cd"
dependencies = [
"core-foundation",
"core-foundation 0.6.4",
"io-kit-sys",
"libc",
"libudev-sys",
@ -1621,15 +1684,6 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "gleam"
version = "0.6.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5"
dependencies = [
"gl_generator 0.13.1",
]
[[package]]
name = "glib"
version = "0.5.0"
@ -1672,15 +1726,15 @@ dependencies = [
[[package]]
name = "glutin"
version = "0.21.2"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd"
checksum = "9a9666c8fd9afd008f6559e2468c35e11aad1d110d525bb3b354e4138ec0e20f"
dependencies = [
"android_glue",
"cgl",
"cocoa 0.18.5",
"core-foundation",
"core-graphics",
"cocoa 0.20.2",
"core-foundation 0.7.0",
"core-graphics 0.19.2",
"glutin_egl_sys",
"glutin_emscripten_sys",
"glutin_gles2_sys",
@ -1688,10 +1742,11 @@ dependencies = [
"glutin_wgl_sys",
"lazy_static",
"libloading 0.5.2",
"log",
"objc",
"osmesa-sys",
"parking_lot 0.9.0",
"wayland-client 0.21.13",
"parking_lot 0.10.2",
"wayland-client",
"winapi 0.3.8",
"winit",
]
@ -2048,7 +2103,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0"
dependencies = [
"core-foundation-sys",
"core-foundation-sys 0.6.2",
"mach",
]
@ -2076,6 +2131,12 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.21"
@ -2448,6 +2509,37 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "ndk"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a356cafe20aee088789830bfea3a61336e84ded9e545e00d3869ce95dcb80c"
dependencies = [
"jni-sys",
"ndk-sys",
"num_enum",
]
[[package]]
name = "ndk-glue"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1730ee2e3de41c3321160a6da815f008c4006d71b095880ea50e17cf52332b8"
dependencies = [
"android_log-sys",
"lazy_static",
"libc",
"log",
"ndk",
"ndk-sys",
]
[[package]]
name = "ndk-sys"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2820aca934aba5ed91c79acc72b6a44048ceacc5d36c035ed4e051f12d887d"
[[package]]
name = "net2"
version = "0.2.34"
@ -2638,6 +2730,28 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4"
dependencies = [
"derivative",
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d"
dependencies = [
"proc-macro-crate",
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.33",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -2682,6 +2796,17 @@ dependencies = [
"byteorder 1.3.4",
]
[[package]]
name = "old_school_gfx_glutin_ext"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0557cea37cc48d238c938ded2873a6cc772704ee1eb01e832b43c2dd99624bc"
dependencies = [
"gfx_core",
"gfx_device_gl",
"glutin",
]
[[package]]
name = "once_cell"
version = "1.4.0"
@ -2957,6 +3082,15 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "0.4.12"
@ -3671,23 +3805,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "smithay-client-toolkit"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa"
dependencies = [
"andrew",
"bitflags",
"dlib",
"lazy_static",
"memmap",
"nix 0.14.1",
"wayland-client 0.21.13",
"wayland-commons 0.21.13",
"wayland-protocols 0.21.13",
]
[[package]]
name = "smithay-client-toolkit"
version = "0.6.6"
@ -3700,8 +3817,8 @@ dependencies = [
"lazy_static",
"memmap",
"nix 0.14.1",
"wayland-client 0.23.6",
"wayland-protocols 0.23.6",
"wayland-client",
"wayland-protocols",
]
[[package]]
@ -3711,7 +3828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917e8ec7f535cd1a6cbf749c8866c24d67c548a80ac48c8e88a182eab5c07bd1"
dependencies = [
"nix 0.14.1",
"smithay-client-toolkit 0.6.6",
"smithay-client-toolkit",
]
[[package]]
@ -4506,13 +4623,12 @@ dependencies = [
"crossbeam",
"deunicode",
"directories-next",
"dispatch",
"dispatch 0.1.4",
"dot_vox",
"euc",
"failure",
"gfx",
"gfx_device_gl",
"gfx_window_glutin",
"gilrs",
"git2",
"glsl-include",
@ -4522,6 +4638,7 @@ dependencies = [
"image",
"msgbox",
"num 0.2.1",
"old_school_gfx_glutin_ext",
"rand 0.7.3",
"rodio",
"ron",
@ -4706,21 +4823,6 @@ version = "0.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
[[package]]
name = "wayland-client"
version = "0.21.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713"
dependencies = [
"bitflags",
"downcast-rs",
"libc",
"nix 0.14.1",
"wayland-commons 0.21.13",
"wayland-scanner 0.21.13",
"wayland-sys 0.21.13",
]
[[package]]
name = "wayland-client"
version = "0.23.6"
@ -4728,22 +4830,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda"
dependencies = [
"bitflags",
"calloop",
"downcast-rs",
"libc",
"mio",
"nix 0.14.1",
"wayland-commons 0.23.6",
"wayland-scanner 0.23.6",
"wayland-sys 0.23.6",
]
[[package]]
name = "wayland-commons"
version = "0.21.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec"
dependencies = [
"nix 0.14.1",
"wayland-sys 0.21.13",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
]
[[package]]
@ -4753,20 +4847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb"
dependencies = [
"nix 0.14.1",
"wayland-sys 0.23.6",
]
[[package]]
name = "wayland-protocols"
version = "0.21.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145"
dependencies = [
"bitflags",
"wayland-client 0.21.13",
"wayland-commons 0.21.13",
"wayland-scanner 0.21.13",
"wayland-sys 0.21.13",
"wayland-sys",
]
[[package]]
@ -4776,20 +4857,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9"
dependencies = [
"bitflags",
"wayland-client 0.23.6",
"wayland-commons 0.23.6",
"wayland-scanner 0.23.6",
]
[[package]]
name = "wayland-scanner"
version = "0.21.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
"xml-rs",
"wayland-client",
"wayland-commons",
"wayland-scanner",
]
[[package]]
@ -4803,16 +4873,6 @@ dependencies = [
"xml-rs",
]
[[package]]
name = "wayland-sys"
version = "0.21.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628"
dependencies = [
"dlib",
"lazy_static",
]
[[package]]
name = "wayland-sys"
version = "0.23.6"
@ -4897,26 +4957,31 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winit"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8"
version = "0.22.2"
source = "git+https://github.com/Imberflur/winit.git?branch=macos-test#e98133adf2abbfc4368f6c069d0beb2b8b688b42"
dependencies = [
"android_glue",
"backtrace",
"bitflags",
"cocoa 0.18.5",
"core-foundation",
"core-graphics",
"cocoa 0.20.2",
"core-foundation 0.7.0",
"core-graphics 0.19.2",
"core-video-sys",
"dispatch 0.2.0",
"instant",
"lazy_static",
"libc",
"log",
"mio",
"mio-extras",
"ndk",
"ndk-glue",
"ndk-sys",
"objc",
"parking_lot 0.9.0",
"parking_lot 0.10.2",
"percent-encoding 2.1.0",
"raw-window-handle",
"serde",
"smithay-client-toolkit 0.4.6",
"wayland-client 0.21.13",
"smithay-client-toolkit",
"wayland-client",
"winapi 0.3.8",
"x11-dl",
]

View File

@ -72,3 +72,7 @@ debug = false
[profile.releasedebuginfo]
inherits = 'release'
debug = 1
# cpal conflict fix isn't released yet
[patch.crates-io]
winit = { git = "https://github.com/Imberflur/winit.git", branch = "macos-test" }

View File

@ -249,15 +249,11 @@ impl State {
/// Removes every chunk of the terrain.
pub fn clear_terrain(&mut self) {
let keys = self
.terrain_mut()
.drain()
.map(|(key, _)| key)
.collect::<Vec<_>>();
let removed_chunks = &mut self.ecs.write_resource::<TerrainChanges>().removed_chunks;
for key in keys {
self.remove_chunk(key);
}
self.terrain_mut().drain().for_each(|(key, _)| {
removed_chunks.insert(key);
});
}
/// Insert the provided chunk into this state's terrain.

View File

@ -25,11 +25,11 @@ anim = { package = "veloren-voxygen-anim", path = "src/anim", default-features =
# Graphics
gfx = "0.18.2"
gfx_device_gl = { version = "0.16.2", optional = true }
gfx_window_glutin = "0.31.0"
glutin = "0.21.1"
winit = { version = "0.19.4", features = ["serde"] }
conrod_core = { git = "https://gitlab.com/veloren/conrod.git", branch = "pre-winit-20" }
conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "pre-winit-20" }
old_school_gfx_glutin_ext = "0.24"
glutin = "0.24.1"
winit = { version = "0.22.2", features = ["serde"] }
conrod_core = { git = "https://gitlab.com/veloren/conrod.git" }
conrod_winit = { git = "https://gitlab.com/veloren/conrod.git" }
euc = { git = "https://github.com/zesterer/euc.git" }
# ECS
@ -87,7 +87,6 @@ winres = "0.1"
criterion = "0.3"
git2 = "0.13"
world = { package = "veloren-world", path = "../world" }
gfx_window_glutin = { version = "0.31.0", features = ["headless"] }
[[bench]]
name = "meshing_benchmark"

View File

@ -1,9 +1,16 @@
use common::comp;
// TODO: Fix this example when we switch to actively maintained rendering
// backend. Right now we would have to update `gfx_window_glutin` to work with
// the latest version of glutin or we would need to add headless support to
// `old_school_gfx_glutin_ext`.
fn main() {
println!("Example temporarily disabled, see the TODO comment for details");
}
/*use common::comp;
use gfx_window_glutin::init_headless;
use vek::*;
use veloren_voxygen::{render, scene::simple as scene};
#[allow(clippy::clone_on_copy)] // TODO: Pending review in #587
fn main() {
// Setup renderer
let dim = (200u16, 300u16, 1, gfx::texture::AaMode::Single);
@ -72,4 +79,4 @@ fn main() {
// Get image
let img = renderer.create_screenshot().unwrap();
img.save("character.png").unwrap();
}
}*/

View File

@ -465,17 +465,15 @@ impl Show {
self.want_grab = true;
// Unpause the game if we are on singleplayer
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(false);
};
#[cfg(feature = "singleplayer")]
global_state.unpause();
} else {
self.esc_menu = true;
self.want_grab = false;
// Pause the game if we are on singleplayer
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(true);
};
#[cfg(feature = "singleplayer")]
global_state.pause();
}
}
@ -1721,9 +1719,8 @@ impl Hud {
settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab),
settings_window::Event::Close => {
// Unpause the game if we are on singleplayer so that we can logout
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(false);
};
#[cfg(feature = "singleplayer")]
global_state.unpause();
self.show.settings(false)
},
@ -1908,24 +1905,21 @@ impl Hud {
self.force_ungrab = false;
// Unpause the game if we are on singleplayer
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(false);
};
#[cfg(feature = "singleplayer")]
global_state.unpause();
},
Some(esc_menu::Event::Logout) => {
// Unpause the game if we are on singleplayer so that we can logout
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(false);
};
#[cfg(feature = "singleplayer")]
global_state.unpause();
events.push(Event::Logout);
},
Some(esc_menu::Event::Quit) => events.push(Event::Quit),
Some(esc_menu::Event::CharacterSelection) => {
// Unpause the game if we are on singleplayer so that we can logout
if let Some(singleplayer) = global_state.singleplayer.as_ref() {
singleplayer.pause(false);
};
#[cfg(feature = "singleplayer")]
global_state.unpause();
events.push(Event::CharacterSelection)
},

View File

@ -17,6 +17,7 @@ pub mod menu;
pub mod mesh;
pub mod profile;
pub mod render;
pub mod run;
pub mod scene;
pub mod session;
pub mod settings;
@ -27,10 +28,16 @@ pub mod window;
// Reexports
pub use crate::error::Error;
#[cfg(feature = "singleplayer")]
use crate::singleplayer::Singleplayer;
use crate::{
audio::AudioFrontend, profile::Profile, settings::Settings, singleplayer::Singleplayer,
window::Window,
audio::AudioFrontend,
profile::Profile,
render::Renderer,
settings::Settings,
window::{Event, Window},
};
use common::{assets::watch, clock::Clock};
/// A type used to store state that is shared between all play states.
pub struct GlobalState {
@ -39,7 +46,11 @@ pub struct GlobalState {
pub window: Window,
pub audio: AudioFrontend,
pub info_message: Option<String>,
pub clock: Clock,
#[cfg(feature = "singleplayer")]
pub singleplayer: Option<Singleplayer>,
// TODO: redo this so that the watcher doesn't have to exist for reloading to occur
pub localization_watcher: watch::ReloadIndicator,
}
impl GlobalState {
@ -51,8 +62,25 @@ impl GlobalState {
}
pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); }
#[cfg(feature = "singleplayer")]
pub fn paused(&self) -> bool {
self.singleplayer
.as_ref()
.map_or(false, Singleplayer::is_paused)
}
#[cfg(not(feature = "singleplayer"))]
pub fn paused(&self) -> bool { false }
#[cfg(feature = "singleplayer")]
pub fn unpause(&self) { self.singleplayer.as_ref().map(|s| s.pause(false)); }
#[cfg(feature = "singleplayer")]
pub fn pause(&self) { self.singleplayer.as_ref().map(|s| s.pause(true)); }
}
// TODO: appears to be currently unused by playstates
pub enum Direction {
Forwards,
Backwards,
@ -61,6 +89,8 @@ pub enum Direction {
/// States can either close (and revert to a previous state), push a new state
/// on top of themselves, or switch to a totally different state.
pub enum PlayStateResult {
/// Keep running this play state.
Continue,
/// Pop all play states in reverse order and shut down the program.
Shutdown,
/// Close the current play state and pop it from the play state stack.
@ -74,10 +104,15 @@ pub enum PlayStateResult {
/// A trait representing a playable game state. This may be a menu, a game
/// session, the title screen, etc.
pub trait PlayState {
/// Play the state until some change of state is required (i.e: a menu is
/// opened or the game is closed).
fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult;
/// Called when entering this play state from another
fn enter(&mut self, global_state: &mut GlobalState, direction: Direction);
/// Tick the play state
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult;
/// Get a descriptive name for this state type.
fn name(&self) -> &'static str;
/// Draw the play state.
fn render(&mut self, renderer: &mut Renderer, settings: &Settings);
}

View File

@ -7,16 +7,19 @@ use veloren_voxygen::{
audio::{self, AudioFrontend},
i18n::{self, i18n_asset_key, VoxygenLocalization},
logging,
menu::main::MainMenuState,
profile::Profile,
run,
settings::{AudioOutput, Settings},
window::Window,
Direction, GlobalState, PlayState, PlayStateResult,
GlobalState,
};
use common::assets::{load, load_expect};
use std::{mem, panic};
use tracing::{debug, error, warn};
use common::{
assets::{load_watched, watch},
clock::Clock,
};
use std::panic;
use tracing::{error, warn};
fn main() {
#[cfg(feature = "tweak")]
@ -26,57 +29,14 @@ fn main() {
// Note: This won't log anything due to it being called before
// `logging::init`. The issue is we need to read a setting to decide
// whether we create a log file or not.
let settings = Settings::load();
// Init logging and hold the guards.
let _guards = logging::init(&settings);
// Save settings to add new fields or create the file if it is not already
// there.
let mut settings = Settings::load();
// Save settings to add new fields or create the file if it is not already there
if let Err(err) = settings.save_to_file() {
panic!("Failed to save settings: {:?}", err);
}
let mut audio = match settings.audio.output {
AudioOutput::Off => None,
AudioOutput::Automatic => audio::get_default_device(),
AudioOutput::Device(ref dev) => Some(dev.clone()),
}
.map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels))
.unwrap_or_else(AudioFrontend::no_audio);
audio.set_music_volume(settings.audio.music_volume);
audio.set_sfx_volume(settings.audio.sfx_volume);
// Load the profile.
let profile = Profile::load();
let mut global_state = GlobalState {
audio,
profile,
window: Window::new(&settings).expect("Failed to create window!"),
settings,
info_message: None,
singleplayer: None,
};
// Try to load the localization and log missing entries
let localized_strings = load::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
))
.unwrap_or_else(|e| {
let preferred_language = &global_state.settings.language.selected_language;
warn!(
?e,
?preferred_language,
"Impossible to load language: change to the default language (English) instead.",
);
global_state.settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
))
});
localized_strings.log_missing_entries();
// Init logging and hold the guards.
let _guards = logging::init(&settings);
// Set up panic handler to relay swish panic messages to the user
let default_hook = panic::take_hook();
@ -159,68 +119,56 @@ fn main() {
#[cfg(feature = "hot-anim")]
anim::init();
// Set up the initial play state.
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
states.last().map(|current_state| {
let current_state = current_state.name();
debug!(?current_state, "Started game with state")
});
// What's going on here?
// ---------------------
// The state system used by Voxygen allows for the easy development of
// stack-based menus. For example, you may want a "title" state that can
// push a "main menu" state on top of it, which can in turn push a
// "settings" state or a "game session" state on top of it. The code below
// manages the state transfer logic automatically so that we don't have to
// re-engineer it for each menu we decide to add to the game.
let mut direction = Direction::Forwards;
while let Some(state_result) = states
.last_mut()
.map(|last| last.play(direction, &mut global_state))
{
// Implement state transfer logic.
match state_result {
PlayStateResult::Shutdown => {
direction = Direction::Backwards;
debug!("Shutting down all states...");
while states.last().is_some() {
states.pop().map(|old_state| {
let old_state = old_state.name();
debug!(?old_state, "Popped state");
global_state.on_play_state_changed();
});
}
},
PlayStateResult::Pop => {
direction = Direction::Backwards;
states.pop().map(|old_state| {
let old_state = old_state.name();
debug!(?old_state, "Popped state");
global_state.on_play_state_changed();
});
},
PlayStateResult::Push(new_state) => {
direction = Direction::Forwards;
debug!("Pushed state '{}'.", new_state.name());
states.push(new_state);
global_state.on_play_state_changed();
},
PlayStateResult::Switch(mut new_state_box) => {
direction = Direction::Forwards;
states.last_mut().map(|old_state_box| {
let old_state = old_state_box.name();
let new_state = new_state_box.name();
debug!(?old_state, ?new_state, "Switching to states",);
mem::swap(old_state_box, &mut new_state_box);
global_state.on_play_state_changed();
});
},
}
// Setup audio
let mut audio = match settings.audio.output {
AudioOutput::Off => None,
AudioOutput::Automatic => audio::get_default_device(),
AudioOutput::Device(ref dev) => Some(dev.clone()),
}
.map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels))
.unwrap_or_else(AudioFrontend::no_audio);
// Save any unsaved changes to profile.
global_state.profile.save_to_file_warn();
// Save any unsaved changes to settings.
global_state.settings.save_to_file_warn();
audio.set_music_volume(settings.audio.music_volume);
audio.set_sfx_volume(settings.audio.sfx_volume);
// Load the profile.
let profile = Profile::load();
let mut localization_watcher = watch::ReloadIndicator::new();
let localized_strings = load_watched::<VoxygenLocalization>(
&i18n_asset_key(&settings.language.selected_language),
&mut localization_watcher,
)
.unwrap_or_else(|error| {
let selected_language = &settings.language.selected_language;
warn!(
?error,
?selected_language,
"Impossible to load language: change to the default language (English) instead.",
);
settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
load_watched::<VoxygenLocalization>(
&i18n_asset_key(&settings.language.selected_language),
&mut localization_watcher,
)
.unwrap()
});
localized_strings.log_missing_entries();
// Create window
let (window, event_loop) = Window::new(&settings).expect("Failed to create window!");
let global_state = GlobalState {
audio,
profile,
window,
settings,
clock: Clock::start(),
info_message: None,
#[cfg(feature = "singleplayer")]
singleplayer: None,
localization_watcher,
};
run::run(global_state, event_loop);
}

View File

@ -2,15 +2,17 @@ mod ui;
use crate::{
i18n::{i18n_asset_key, VoxygenLocalization},
render::Renderer,
scene::simple::{self as scene, Scene},
session::SessionState,
settings::Settings,
window::Event as WinEvent,
Direction, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime};
use common::{assets, comp, msg::ClientState, state::DeltaTime};
use specs::WorldExt;
use std::{cell::RefCell, rc::Rc, time::Duration};
use std::{cell::RefCell, rc::Rc};
use tracing::error;
use ui::CharSelectionUi;
@ -32,20 +34,34 @@ impl CharSelectionState {
),
}
}
fn get_humanoid_body(&self) -> Option<comp::humanoid::Body> {
self.char_selection_ui
.get_character_list()
.and_then(|data| {
if let Some(character) = data.get(self.char_selection_ui.selected_character) {
match character.body {
comp::Body::Humanoid(body) => Some(body),
_ => None,
}
} else {
None
}
})
}
}
impl PlayState for CharSelectionState {
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
// Set up an fps clock.
let mut clock = Clock::start();
fn enter(&mut self, _: &mut GlobalState, _: Direction) {
// Load the player's character list
self.client.borrow_mut().load_character_list();
}
let mut current_client_state = self.client.borrow().get_client_state();
while let ClientState::Pending | ClientState::Registered = current_client_state {
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
let client_state = self.client.borrow().get_client_state();
if let ClientState::Pending | ClientState::Registered = client_state {
// Handle window events
for event in global_state.window.fetch_events(&mut global_state.settings) {
for event in events {
if self.char_selection_ui.handle_event(event.clone()) {
continue;
}
@ -60,8 +76,6 @@ impl PlayState for CharSelectionState {
}
}
global_state.window.renderer_mut().clear();
// Maintain the UI.
let events = self
.char_selection_ui
@ -100,23 +114,7 @@ impl PlayState for CharSelectionState {
}
}
// Maintain global state.
global_state.maintain(clock.get_last_delta().as_secs_f32());
let humanoid_body = self
.char_selection_ui
.get_character_list()
.and_then(|data| {
if let Some(character) = data.get(self.char_selection_ui.selected_character) {
match character.body {
comp::Body::Humanoid(body) => Some(body),
_ => None,
}
} else {
None
}
});
let humanoid_body = self.get_humanoid_body();
let loadout = self.char_selection_ui.get_loadout();
// Maintain the scene.
@ -141,17 +139,6 @@ impl PlayState for CharSelectionState {
loadout.as_ref(),
);
}
// Render the scene.
self.scene.render(
global_state.window.renderer_mut(),
self.client.borrow().get_tick(),
humanoid_body,
loadout.as_ref(),
);
// Draw the UI to the screen.
self.char_selection_ui
.render(global_state.window.renderer_mut(), self.scene.globals());
// Tick the client (currently only to keep the connection alive).
let localized_strings = assets::load_expect::<VoxygenLocalization>(&i18n_asset_key(
@ -160,7 +147,7 @@ impl PlayState for CharSelectionState {
match self.client.borrow_mut().tick(
comp::ControllerInputs::default(),
clock.get_last_delta(),
global_state.clock.get_last_delta(),
|_| {},
) {
Ok(events) => {
@ -190,25 +177,33 @@ impl PlayState for CharSelectionState {
},
}
// TODO: make sure rendering is not relying on cleaned up stuff
self.client.borrow_mut().cleanup();
// Finish the frame.
global_state.window.renderer_mut().flush();
global_state
.window
.swap_buffers()
.expect("Failed to swap window buffers");
// Wait for the next tick.
clock.tick(Duration::from_millis(
1000 / (global_state.settings.graphics.max_fps as u64),
));
current_client_state = self.client.borrow().get_client_state();
PlayStateResult::Continue
} else {
error!("Client not in pending or registered state. Popping char selection play state");
// TODO set global_state.info_message
PlayStateResult::Pop
}
PlayStateResult::Pop
}
fn name(&self) -> &'static str { "Title" }
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
let humanoid_body = self.get_humanoid_body();
let loadout = self.char_selection_ui.get_loadout();
// Render the scene.
self.scene.render(
renderer,
self.client.borrow().get_tick(),
humanoid_body,
loadout.as_ref(),
);
// Draw the UI to the screen.
self.char_selection_ui
.render(renderer, self.scene.globals());
}
}

View File

@ -1,19 +1,22 @@
mod client_init;
#[cfg(feature = "singleplayer")] mod ui;
mod ui;
use super::char_selection::CharSelectionState;
#[cfg(feature = "singleplayer")]
use crate::singleplayer::Singleplayer;
use crate::{
singleplayer::Singleplayer, window::Event, Direction, GlobalState, PlayState, PlayStateResult,
render::Renderer, settings::Settings, window::Event, Direction, GlobalState, PlayState,
PlayStateResult,
};
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::{assets::load_expect, clock::Clock, comp};
#[cfg(feature = "singleplayer")]
use std::time::Duration;
use common::{assets::load_expect, comp};
use tracing::{error, warn};
use ui::{Event as MainMenuEvent, MainMenuUi};
pub struct MainMenuState {
main_menu_ui: MainMenuUi,
// Used for client creation.
client_init: Option<ClientInit>,
}
impl MainMenuState {
@ -21,6 +24,7 @@ impl MainMenuState {
pub fn new(global_state: &mut GlobalState) -> Self {
Self {
main_menu_ui: MainMenuUi::new(global_state),
client_init: None,
}
}
}
@ -28,237 +32,226 @@ impl MainMenuState {
const DEFAULT_PORT: u16 = 14004;
impl PlayState for MainMenuState {
#[allow(clippy::useless_format)] // TODO: Pending review in #587
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
// Set up an fps clock.
let mut clock = Clock::start();
// Used for client creation.
let mut client_init: Option<ClientInit> = None;
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
// Kick off title music
if global_state.settings.audio.output.is_enabled() && global_state.audio.music_enabled() {
global_state.audio.play_title_music();
}
// Reset singleplayer server if it was running already
global_state.singleplayer = None;
#[cfg(feature = "singleplayer")]
{
global_state.singleplayer = None;
}
}
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
let localized_strings = load_expect::<crate::i18n::VoxygenLocalization>(
&crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language),
);
loop {
// Handle window events.
for event in global_state.window.fetch_events(&mut global_state.settings) {
match event {
Event::Close => return PlayStateResult::Shutdown,
// Pass events to ui.
Event::Ui(event) => {
self.main_menu_ui.handle_event(event);
},
// Ignore all other events.
_ => {},
}
}
global_state.window.renderer_mut().clear();
// Poll client creation.
match client_init.as_ref().and_then(|init| init.poll()) {
Some(InitMsg::Done(Ok(mut client))) => {
self.main_menu_ui.connected();
// Register voxygen components / resources
crate::ecs::init(client.state_mut().ecs_mut());
return PlayStateResult::Push(Box::new(CharSelectionState::new(
global_state,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)));
// Handle window events.
for event in events {
match event {
Event::Close => return PlayStateResult::Shutdown,
// Pass events to ui.
Event::Ui(event) => {
self.main_menu_ui.handle_event(event);
},
Some(InitMsg::Done(Err(e))) => {
client_init = None;
global_state.info_message = Some({
let err = match e {
InitError::BadAddress(_) | InitError::NoAddress => {
localized_strings.get("main.login.server_not_found").into()
// Ignore all other events.
_ => {},
}
}
// Poll client creation.
match self.client_init.as_ref().and_then(|init| init.poll()) {
Some(InitMsg::Done(Ok(mut client))) => {
self.client_init = None;
self.main_menu_ui.connected();
// Register voxygen components / resources
crate::ecs::init(client.state_mut().ecs_mut());
return PlayStateResult::Push(Box::new(CharSelectionState::new(
global_state,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)));
},
Some(InitMsg::Done(Err(err))) => {
self.client_init = None;
global_state.info_message = Some({
let err = match err {
InitError::BadAddress(_) | InitError::NoAddress => {
localized_strings.get("main.login.server_not_found").into()
},
InitError::ClientError(err) => match err {
client::Error::AuthErr(e) => format!(
"{}: {}",
localized_strings.get("main.login.authentication_error"),
e
),
client::Error::TooManyPlayers => {
localized_strings.get("main.login.server_full").into()
},
InitError::ClientError(err) => match err {
client::Error::AuthErr(e) => format!(
client::Error::AuthServerNotTrusted => localized_strings
.get("main.login.untrusted_auth_server")
.into(),
client::Error::ServerWentMad => localized_strings
.get("main.login.outdated_client_or_server")
.into(),
client::Error::ServerTimeout => {
localized_strings.get("main.login.timeout").into()
},
client::Error::ServerShutdown => {
localized_strings.get("main.login.server_shut_down").into()
},
client::Error::AlreadyLoggedIn => {
localized_strings.get("main.login.already_logged_in").into()
},
client::Error::NotOnWhitelist => {
localized_strings.get("main.login.not_on_whitelist").into()
},
client::Error::InvalidCharacter => {
localized_strings.get("main.login.invalid_character").into()
},
client::Error::NetworkErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::ParticipantErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::StreamErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::Other(e) => {
format!("{}: {}", localized_strings.get("common.error"), e)
},
client::Error::AuthClientError(e) => match e {
client::AuthClientError::JsonError(e) => format!(
"{}: {}",
localized_strings.get("main.login.authentication_error"),
localized_strings.get("common.fatal_error"),
e
),
client::Error::TooManyPlayers => {
localized_strings.get("main.login.server_full").into()
},
client::Error::AuthServerNotTrusted => localized_strings
.get("main.login.untrusted_auth_server")
.into(),
client::Error::ServerWentMad => localized_strings
.get("main.login.outdated_client_or_server")
.into(),
client::Error::ServerTimeout => {
localized_strings.get("main.login.timeout").into()
},
client::Error::ServerShutdown => {
localized_strings.get("main.login.server_shut_down").into()
},
client::Error::AlreadyLoggedIn => {
localized_strings.get("main.login.already_logged_in").into()
},
client::Error::NotOnWhitelist => {
localized_strings.get("main.login.not_on_whitelist").into()
},
client::Error::NetworkErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::ParticipantErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::StreamErr(e) => format!(
"{}: {:?}",
localized_strings.get("main.login.network_error"),
e
),
client::Error::Other(e) => {
format!("{}: {}", localized_strings.get("common.error"), e)
},
client::Error::AuthClientError(e) => match e {
client::AuthClientError::JsonError(e) => format!(
"{}: {}",
localized_strings.get("common.fatal_error"),
e
),
client::AuthClientError::RequestError() => format!(
"{}",
localized_strings.get("main.login.failed_sending_request")
),
client::AuthClientError::ServerError(_, e) => format!("{}", e),
},
client::Error::InvalidCharacter => {
localized_strings.get("main.login.invalid_character").into()
},
// TODO: remove parentheses
client::AuthClientError::RequestError() => localized_strings
.get("main.login.failed_sending_request")
.to_owned(),
client::AuthClientError::ServerError(_, e) => e,
},
InitError::ClientCrashed => {
localized_strings.get("main.login.client_crashed").into()
},
};
// Log error for possible additional use later or incase that the error
// displayed is cut of.
error!("{}", err);
err
});
},
Some(InitMsg::IsAuthTrusted(auth_server)) => {
if global_state
.settings
.networking
.trusted_auth_servers
.contains(&auth_server)
{
// Can't fail since we just polled it, it must be Some
client_init.as_ref().unwrap().auth_trust(auth_server, true);
} else {
// Show warning that auth server is not trusted and prompt for approval
self.main_menu_ui.auth_trust_prompt(auth_server);
}
},
None => {},
}
},
InitError::ClientCrashed => {
localized_strings.get("main.login.client_crashed").into()
},
};
// Log error for possible additional use later or incase that the error
// displayed is cut of.
error!("{}", err);
err
});
},
Some(InitMsg::IsAuthTrusted(auth_server)) => {
if global_state
.settings
.networking
.trusted_auth_servers
.contains(&auth_server)
{
// Can't fail since we just polled it, it must be Some
self.client_init
.as_ref()
.unwrap()
.auth_trust(auth_server, true);
} else {
// Show warning that auth server is not trusted and prompt for approval
self.main_menu_ui.auth_trust_prompt(auth_server);
}
},
None => {},
}
// Maintain global_state
global_state.maintain(clock.get_last_delta().as_secs_f32());
// Maintain the UI.
for event in self
.main_menu_ui
.maintain(global_state, clock.get_last_delta())
{
match event {
MainMenuEvent::LoginAttempt {
// Maintain the UI.
for event in self
.main_menu_ui
.maintain(global_state, global_state.clock.get_last_delta())
{
match event {
MainMenuEvent::LoginAttempt {
username,
password,
server_address,
} => {
attempt_login(
global_state,
username,
password,
server_address,
} => {
attempt_login(
global_state,
username,
password,
server_address,
DEFAULT_PORT,
&mut client_init,
);
},
MainMenuEvent::CancelLoginAttempt => {
// client_init contains Some(ClientInit), which spawns a thread which
// contains a TcpStream::connect() call This call is
// blocking TODO fix when the network rework happens
global_state.singleplayer = None;
client_init = None;
self.main_menu_ui.cancel_connection();
},
DEFAULT_PORT,
&mut self.client_init,
);
},
MainMenuEvent::CancelLoginAttempt => {
// client_init contains Some(ClientInit), which spawns a thread which contains a
// TcpStream::connect() call This call is blocking
// TODO fix when the network rework happens
#[cfg(feature = "singleplayer")]
MainMenuEvent::StartSingleplayer => {
let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool
{
global_state.singleplayer = None;
}
self.client_init = None;
self.main_menu_ui.cancel_connection();
},
#[cfg(feature = "singleplayer")]
MainMenuEvent::StartSingleplayer => {
let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool
global_state.singleplayer = Some(singleplayer);
global_state.singleplayer = Some(singleplayer);
attempt_login(
global_state,
"singleplayer".to_owned(),
"".to_owned(),
server_settings.gameserver_address.ip().to_string(),
server_settings.gameserver_address.port(),
&mut client_init,
);
},
MainMenuEvent::Settings => {}, // TODO
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
/*MainMenuEvent::DisclaimerClosed => {
global_state.settings.show_disclaimer = false
},*/
MainMenuEvent::AuthServerTrust(auth_server, trust) => {
if trust {
global_state
.settings
.networking
.trusted_auth_servers
.insert(auth_server.clone());
global_state.settings.save_to_file_warn();
}
client_init
.as_ref()
.map(|init| init.auth_trust(auth_server, trust));
},
}
attempt_login(
global_state,
"singleplayer".to_owned(),
"".to_owned(),
server_settings.gameserver_address.ip().to_string(),
server_settings.gameserver_address.port(),
&mut self.client_init,
);
},
MainMenuEvent::Settings => {}, // TODO
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
/*MainMenuEvent::DisclaimerClosed => {
global_state.settings.show_disclaimer = false
},*/
MainMenuEvent::AuthServerTrust(auth_server, trust) => {
if trust {
global_state
.settings
.networking
.trusted_auth_servers
.insert(auth_server.clone());
global_state.settings.save_to_file_warn();
}
self.client_init
.as_ref()
.map(|init| init.auth_trust(auth_server, trust));
},
}
if let Some(info) = global_state.info_message.take() {
self.main_menu_ui.show_info(info);
}
// Draw the UI to the screen.
self.main_menu_ui.render(global_state.window.renderer_mut());
// Finish the frame.
global_state.window.renderer_mut().flush();
global_state
.window
.swap_buffers()
.expect("Failed to swap window buffers!");
// Wait for the next tick
clock.tick(Duration::from_millis(
1000 / (global_state.settings.graphics.max_fps as u64),
));
}
if let Some(info) = global_state.info_message.take() {
self.main_menu_ui.show_info(info);
}
PlayStateResult::Continue
}
fn name(&self) -> &'static str { "Title" }
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
// Draw the UI to the screen.
self.main_menu_ui.render(renderer);
}
}
fn attempt_login(

145
voxygen/src/run.rs Normal file
View File

@ -0,0 +1,145 @@
use crate::{
menu::main::MainMenuState,
ui,
window::{Event, EventLoop},
Direction, GlobalState, PlayState, PlayStateResult,
};
use std::{mem, time::Duration};
use tracing::debug;
pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
// Set up the initial play state.
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
states.last_mut().map(|current_state| {
current_state.enter(&mut global_state, Direction::Forwards);
let current_state = current_state.name();
debug!(?current_state, "Started game with state");
});
// Used to ignore every other `MainEventsCleared`
// This is a workaround for a bug on macos in which mouse motion events are only
// reported every other cycle of the event loop
// See: https://github.com/rust-windowing/winit/issues/1418
let mut polled_twice = false;
event_loop.run(move |event, _, control_flow| {
// Continously run loop since we handle sleeping
*control_flow = winit::event_loop::ControlFlow::Poll;
// Get events for the ui.
if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) {
global_state.window.send_event(Event::Ui(event));
}
match event {
winit::event::Event::MainEventsCleared => {
if polled_twice {
handle_main_events_cleared(&mut states, control_flow, &mut global_state);
}
polled_twice = !polled_twice;
},
winit::event::Event::WindowEvent { event, .. } => global_state
.window
.handle_window_event(event, &mut global_state.settings),
winit::event::Event::DeviceEvent { event, .. } => {
global_state.window.handle_device_event(event)
},
winit::event::Event::LoopDestroyed => {
// Save any unsaved changes to settings and profile
global_state.settings.save_to_file_warn();
global_state.profile.save_to_file_warn();
},
_ => {},
}
});
}
fn handle_main_events_cleared(
states: &mut Vec<Box<dyn PlayState>>,
control_flow: &mut winit::event_loop::ControlFlow,
global_state: &mut GlobalState,
) {
// Run tick here
// What's going on here?
// ---------------------
// The state system used by Voxygen allows for the easy development of
// stack-based menus. For example, you may want a "title" state
// that can push a "main menu" state on top of it, which can in
// turn push a "settings" state or a "game session" state on top of it.
// The code below manages the state transfer logic automatically so that we
// don't have to re-engineer it for each menu we decide to add
// to the game.
let mut exit = true;
while let Some(state_result) = states.last_mut().map(|last| {
let events = global_state.window.fetch_events();
last.tick(global_state, events)
}) {
// Implement state transfer logic.
match state_result {
PlayStateResult::Continue => {
// Wait for the next tick.
global_state.clock.tick(Duration::from_millis(
1000 / global_state.settings.graphics.max_fps as u64,
));
// Maintain global state.
global_state.maintain(global_state.clock.get_last_delta().as_secs_f32());
exit = false;
break;
},
PlayStateResult::Shutdown => {
debug!("Shutting down all states...");
while states.last().is_some() {
states.pop().map(|old_state| {
debug!("Popped state '{}'.", old_state.name());
global_state.on_play_state_changed();
});
}
},
PlayStateResult::Pop => {
states.pop().map(|old_state| {
debug!("Popped state '{}'.", old_state.name());
global_state.on_play_state_changed();
});
states.last_mut().map(|new_state| {
new_state.enter(global_state, Direction::Backwards);
});
},
PlayStateResult::Push(mut new_state) => {
new_state.enter(global_state, Direction::Forwards);
debug!("Pushed state '{}'.", new_state.name());
states.push(new_state);
global_state.on_play_state_changed();
},
PlayStateResult::Switch(mut new_state) => {
new_state.enter(global_state, Direction::Forwards);
states.last_mut().map(|old_state| {
debug!(
"Switching to state '{}' from state '{}'.",
new_state.name(),
old_state.name()
);
mem::swap(old_state, &mut new_state);
global_state.on_play_state_changed();
});
},
}
}
if exit {
*control_flow = winit::event_loop::ControlFlow::Exit;
}
if let Some(last) = states.last_mut() {
global_state.window.renderer_mut().clear();
last.render(global_state.window.renderer_mut(), &global_state.settings);
// Finish the frame.
global_state.window.renderer_mut().flush();
global_state
.window
.swap_buffers()
.expect("Failed to swap window buffers!");
}
}

View File

@ -5,15 +5,15 @@ use crate::{
i18n::{i18n_asset_key, VoxygenLocalization},
key_state::KeyState,
menu::char_selection::CharSelectionState,
render::Renderer,
scene::{camera, Scene, SceneData},
settings::{AudioOutput, ControlSettings},
settings::{AudioOutput, ControlSettings, Settings},
window::{AnalogGameInput, Event, GameInput},
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{
assets::{load_expect, load_watched, watch},
clock::Clock,
assets::{load_expect, load_watched},
comp,
comp::{
ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel, MAX_MOUNT_RANGE_SQR,
@ -46,6 +46,12 @@ pub struct SessionState {
inputs: comp::ControllerInputs,
selected_block: Block,
voxygen_i18n: std::sync::Arc<VoxygenLocalization>,
walk_forward_dir: Vec2<f32>,
walk_right_dir: Vec2<f32>,
freefly_vel: Vec3<f32>,
free_look: bool,
auto_walk: bool,
is_aiming: bool,
}
/// Represents an active game session (i.e., the one being played).
@ -59,14 +65,12 @@ impl SessionState {
.camera_mut()
.set_fov_deg(global_state.settings.graphics.fov);
let hud = Hud::new(global_state, &client.borrow());
{
let mut client = client.borrow_mut();
let my_entity = client.entity();
client.state_mut().ecs_mut().insert(MyEntity(my_entity));
}
let voxygen_i18n = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
let walk_forward_dir = scene.camera().forward_xy();
let walk_right_dir = scene.camera().right_xy();
Self {
scene,
client,
@ -75,8 +79,20 @@ impl SessionState {
hud,
selected_block: Block::new(BlockKind::Normal, Rgb::broadcast(255)),
voxygen_i18n,
walk_forward_dir,
walk_right_dir,
freefly_vel: Vec3::zero(),
free_look: false,
auto_walk: false,
is_aiming: false,
}
}
fn stop_auto_walk(&mut self) {
self.auto_walk = false;
self.hud.auto_walk(false);
self.key_state.auto_walk = false;
}
}
impl SessionState {
@ -86,9 +102,6 @@ impl SessionState {
let mut client = self.client.borrow_mut();
for event in client.tick(self.inputs.clone(), dt, crate::ecs::sys::add_local_systems)? {
self.voxygen_i18n = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
match event {
client::Event::Chat(m) => {
self.hud.new_message(m);
@ -153,12 +166,10 @@ impl SessionState {
}
impl PlayState for SessionState {
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
// Trap the cursor.
global_state.window.grab_cursor(true);
// Set up an fps clock.
let mut clock = Clock::start();
self.client.borrow_mut().clear_terrain();
// Send startup commands to the server
@ -167,30 +178,28 @@ impl PlayState for SessionState {
self.client.borrow_mut().send_chat(cmd.to_string());
}
}
}
// Keep a watcher on the language
let mut localization_watcher = watch::ReloadIndicator::new();
let mut localized_strings = load_watched::<VoxygenLocalization>(
&i18n_asset_key(&global_state.settings.language.selected_language),
&mut localization_watcher,
)
.unwrap();
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
self.voxygen_i18n = load_expect::<VoxygenLocalization>(&i18n_asset_key(
&global_state.settings.language.selected_language,
));
let mut walk_forward_dir = self.scene.camera().forward_xy();
let mut walk_right_dir = self.scene.camera().right_xy();
let mut freefly_vel = Vec3::zero();
let mut free_look = false;
let mut auto_walk = false;
// TODO: can this be a method on the session or are there borrowcheck issues?
fn stop_auto_walk(auto_walk: &mut bool, key_state: &mut KeyState, hud: &mut Hud) {
*auto_walk = false;
hud.auto_walk(false);
key_state.auto_walk = false;
}
// Game loop
let mut current_client_state = self.client.borrow().get_client_state();
while let ClientState::Pending | ClientState::Character = current_client_state {
let client_state = self.client.borrow().get_client_state();
if let ClientState::Pending | ClientState::Character = client_state {
// Update MyEntity
// Note: Alternatively, the client could emit an event when the entity changes
// which may or may not be more elegant
{
let my_entity = self.client.borrow().entity();
self.client
.borrow_mut()
.state_mut()
.ecs_mut()
.insert(MyEntity(my_entity));
}
// Compute camera data
self.scene
.camera_mut()
@ -229,6 +238,7 @@ impl PlayState for SessionState {
},
)
};
self.is_aiming = is_aiming;
let cam_dir: Vec3<f32> = Vec3::from(view_mat.inverted() * -Vec4::unit_z());
@ -278,7 +288,7 @@ impl PlayState for SessionState {
}));
// Handle window events.
for event in global_state.window.fetch_events(&mut global_state.settings) {
for event in events {
// Pass all events to the ui first.
if self.hud.handle_event(event.clone(), global_state) {
continue;
@ -331,7 +341,7 @@ impl PlayState for SessionState {
Event::InputUpdate(GameInput::Respawn, state)
if state != self.key_state.respawn =>
{
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
self.key_state.respawn = state;
if state {
self.client.borrow_mut().respawn();
@ -348,7 +358,7 @@ impl PlayState for SessionState {
{
self.key_state.toggle_sit = state;
if state {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
self.client.borrow_mut().toggle_sit();
}
}
@ -357,31 +367,31 @@ impl PlayState for SessionState {
{
self.key_state.toggle_dance = state;
if state {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
self.client.borrow_mut().toggle_dance();
}
}
Event::InputUpdate(GameInput::MoveForward, state) => {
if state && global_state.settings.gameplay.stop_auto_walk_on_input {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
}
self.key_state.up = state
},
Event::InputUpdate(GameInput::MoveBack, state) => {
if state && global_state.settings.gameplay.stop_auto_walk_on_input {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
}
self.key_state.down = state
},
Event::InputUpdate(GameInput::MoveLeft, state) => {
if state && global_state.settings.gameplay.stop_auto_walk_on_input {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
}
self.key_state.left = state
},
Event::InputUpdate(GameInput::MoveRight, state) => {
if state && global_state.settings.gameplay.stop_auto_walk_on_input {
stop_auto_walk(&mut auto_walk, &mut self.key_state, &mut self.hud);
self.stop_auto_walk();
}
self.key_state.right = state
},
@ -509,12 +519,12 @@ impl PlayState for SessionState {
Event::InputUpdate(GameInput::FreeLook, state) => {
match (global_state.settings.gameplay.free_look_behavior, state) {
(PressBehavior::Toggle, true) => {
free_look = !free_look;
self.hud.free_look(free_look);
self.free_look = !self.free_look;
self.hud.free_look(self.free_look);
},
(PressBehavior::Hold, state) => {
free_look = state;
self.hud.free_look(free_look);
self.free_look = state;
self.hud.free_look(self.free_look);
},
_ => {},
};
@ -522,14 +532,14 @@ impl PlayState for SessionState {
Event::InputUpdate(GameInput::AutoWalk, state) => {
match (global_state.settings.gameplay.auto_walk_behavior, state) {
(PressBehavior::Toggle, true) => {
auto_walk = !auto_walk;
self.key_state.auto_walk = auto_walk;
self.hud.auto_walk(auto_walk);
self.auto_walk = !self.auto_walk;
self.key_state.auto_walk = self.auto_walk;
self.hud.auto_walk(self.auto_walk);
},
(PressBehavior::Hold, state) => {
auto_walk = state;
self.key_state.auto_walk = auto_walk;
self.hud.auto_walk(auto_walk);
self.auto_walk = state;
self.key_state.auto_walk = self.auto_walk;
self.hud.auto_walk(self.auto_walk);
},
_ => {},
}
@ -567,9 +577,9 @@ impl PlayState for SessionState {
}
}
if !free_look {
walk_forward_dir = self.scene.camera().forward_xy();
walk_right_dir = self.scene.camera().right_xy();
if !self.free_look {
self.walk_forward_dir = self.scene.camera().forward_xy();
self.walk_right_dir = self.scene.camera().right_xy();
self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
}
@ -581,8 +591,9 @@ impl PlayState for SessionState {
camera::CameraMode::FirstPerson | camera::CameraMode::ThirdPerson => {
// Move the player character based on their walking direction.
// This could be different from the camera direction if free look is enabled.
self.inputs.move_dir = walk_right_dir * axis_right + walk_forward_dir * axis_up;
freefly_vel = Vec3::zero();
self.inputs.move_dir =
self.walk_right_dir * axis_right + self.walk_forward_dir * axis_up;
self.freefly_vel = Vec3::zero();
},
camera::CameraMode::Freefly => {
@ -596,27 +607,27 @@ impl PlayState for SessionState {
let right = self.scene.camera().right();
let dir = right * axis_right + forward * axis_up;
let dt = clock.get_last_delta().as_secs_f32();
if freefly_vel.magnitude_squared() > 0.01 {
let new_vel =
freefly_vel - freefly_vel.normalized() * (FREEFLY_DAMPING * dt);
if freefly_vel.dot(new_vel) > 0.0 {
freefly_vel = new_vel;
let dt = global_state.clock.get_last_delta().as_secs_f32();
if self.freefly_vel.magnitude_squared() > 0.01 {
let new_vel = self.freefly_vel
- self.freefly_vel.normalized() * (FREEFLY_DAMPING * dt);
if self.freefly_vel.dot(new_vel) > 0.0 {
self.freefly_vel = new_vel;
} else {
freefly_vel = Vec3::zero();
self.freefly_vel = Vec3::zero();
}
}
if dir.magnitude_squared() > 0.01 {
freefly_vel += dir * (FREEFLY_ACCEL * dt);
if freefly_vel.magnitude() > FREEFLY_MAX_SPEED {
freefly_vel = freefly_vel.normalized() * FREEFLY_MAX_SPEED;
self.freefly_vel += dir * (FREEFLY_ACCEL * dt);
if self.freefly_vel.magnitude() > FREEFLY_MAX_SPEED {
self.freefly_vel = self.freefly_vel.normalized() * FREEFLY_MAX_SPEED;
}
}
let pos = self.scene.camera().get_focus_pos();
self.scene
.camera_mut()
.set_focus_pos(pos + freefly_vel * dt);
.set_focus_pos(pos + self.freefly_vel * dt);
// Do not apply any movement to the player character
self.inputs.move_dir = Vec2::zero();
@ -626,16 +637,14 @@ impl PlayState for SessionState {
self.inputs.climb = self.key_state.climb();
// Runs if either in a multiplayer server or the singleplayer server is unpaused
if global_state.singleplayer.is_none()
|| !global_state.singleplayer.as_ref().unwrap().is_paused()
{
if !global_state.paused() {
// Perform an in-game tick.
match self.tick(clock.get_avg_delta(), global_state) {
match self.tick(global_state.clock.get_avg_delta(), global_state) {
Ok(TickAction::Continue) => {}, // Do nothing
Ok(TickAction::Disconnect) => return PlayStateResult::Pop, // Go to main menu
Err(err) => {
global_state.info_message =
Some(localized_strings.get("common.connection_lost").to_owned());
Some(self.voxygen_i18n.get("common.connection_lost").to_owned());
error!("[session] Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;
@ -643,19 +652,17 @@ impl PlayState for SessionState {
}
}
// Maintain global state.
global_state.maintain(clock.get_last_delta().as_secs_f32());
// Recompute dependents just in case some input modified the camera
self.scene
.camera_mut()
.compute_dependents(&*self.client.borrow().state().terrain());
// Extract HUD events ensuring the client borrow gets dropped.
let mut hud_events = self.hud.maintain(
&self.client.borrow(),
global_state,
DebugInfo {
tps: clock.get_tps(),
tps: global_state.clock.get_tps(),
ping_ms: self.client.borrow().get_ping_ms_rolling_avg(),
coordinates: self
.client
@ -687,7 +694,7 @@ impl PlayState for SessionState {
num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32,
},
&self.scene.camera(),
clock.get_last_delta(),
global_state.clock.get_last_delta(),
HudInfo {
is_aiming,
is_first_person: matches!(
@ -698,8 +705,8 @@ impl PlayState for SessionState {
);
// Look for changes in the localization files
if localization_watcher.reloaded() {
hud_events.push(HudEvent::ChangeLanguage(localized_strings.metadata.clone()));
if global_state.localization_watcher.reloaded() {
hud_events.push(HudEvent::ChangeLanguage(self.voxygen_i18n.metadata.clone()));
}
// Maintain the UI.
@ -916,13 +923,13 @@ impl PlayState for SessionState {
HudEvent::ChangeLanguage(new_language) => {
global_state.settings.language.selected_language =
new_language.language_identifier;
localized_strings = load_watched::<VoxygenLocalization>(
self.voxygen_i18n = load_watched::<VoxygenLocalization>(
&i18n_asset_key(&global_state.settings.language.selected_language),
&mut localization_watcher,
&mut global_state.localization_watcher,
)
.unwrap();
localized_strings.log_missing_entries();
self.hud.update_language(localized_strings.clone());
self.voxygen_i18n.log_missing_entries();
self.hud.update_language(self.voxygen_i18n.clone());
},
HudEvent::ToggleFullscreen => {
global_state
@ -978,59 +985,62 @@ impl PlayState for SessionState {
};
// Runs if either in a multiplayer server or the singleplayer server is unpaused
if global_state.singleplayer.is_none()
|| !global_state.singleplayer.as_ref().unwrap().is_paused()
{
if !global_state.paused() {
self.scene.maintain(
global_state.window.renderer_mut(),
&mut global_state.audio,
&scene_data,
);
}
let renderer = global_state.window.renderer_mut();
// Clear the screen
renderer.clear();
// Render the screen using the global renderer
self.scene.render(
renderer,
client.state(),
client.entity(),
client.get_tick(),
&scene_data,
);
// Draw the UI to the screen
self.hud.render(renderer, self.scene.globals());
// Finish the frame
renderer.flush();
}
// Display the frame on the window.
global_state
.window
.swap_buffers()
.expect("Failed to swap window buffers!");
// Wait for the next tick.
clock.tick(Duration::from_millis(
1000 / global_state.settings.graphics.max_fps as u64,
));
// Clean things up after the tick.
self.cleanup();
current_client_state = self.client.borrow().get_client_state();
}
if let ClientState::Registered = current_client_state {
return PlayStateResult::Switch(Box::new(CharSelectionState::new(
PlayStateResult::Continue
} else if let ClientState::Registered = client_state {
PlayStateResult::Switch(Box::new(CharSelectionState::new(
global_state,
self.client.clone(),
)));
)))
} else {
error!("Client not in the expected state, exiting session play state");
PlayStateResult::Pop
}
PlayStateResult::Pop
}
fn name(&self) -> &'static str { "Session" }
/// Render the session to the screen.
///
/// This method should be called once per frame.
fn render(&mut self, renderer: &mut Renderer, settings: &Settings) {
// Render the screen using the global renderer
{
let client = self.client.borrow();
let scene_data = SceneData {
state: client.state(),
player_entity: client.entity(),
loaded_distance: client.loaded_distance(),
view_distance: client.view_distance().unwrap_or(1),
tick: client.get_tick(),
thread_pool: client.thread_pool(),
gamma: settings.graphics.gamma,
mouse_smoothing: settings.gameplay.smooth_pan_enable,
sprite_render_distance: settings.graphics.sprite_render_distance as f32,
figure_lod_render_distance: settings.graphics.figure_lod_render_distance as f32,
is_aiming: self.is_aiming,
};
self.scene.render(
renderer,
client.state(),
client.entity(),
client.get_tick(),
&scene_data,
);
}
// Draw the UI to the screen
self.hud.render(renderer, self.scene.globals());
}
}

View File

@ -6,11 +6,11 @@ use crate::{
window::{GameInput, KeyMouse},
};
use directories_next::{ProjectDirs, UserDirs};
use glutin::{MouseButton, VirtualKeyCode};
use hashbrown::{HashMap, HashSet};
use serde_derive::{Deserialize, Serialize};
use std::{fs, io::prelude::*, path::PathBuf};
use tracing::warn;
use winit::event::{MouseButton, VirtualKeyCode};
// ControlSetting-like struct used by Serde, to handle not serializing/building
// post-deserializing the inverse_keybindings hashmap

View File

@ -1,26 +1,30 @@
use conrod_core::{event::Input, input::Button};
use vek::*;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Event(pub Input);
impl Event {
pub fn try_from(
event: glutin::Event,
window: &glutin::ContextWrapper<glutin::PossiblyCurrent, winit::Window>,
event: &winit::event::Event<()>,
window: &glutin::ContextWrapper<glutin::PossiblyCurrent, winit::window::Window>,
) -> Option<Self> {
use conrod_winit::*;
// A wrapper around the winit window that allows us to implement the trait
// necessary for enabling the winit <-> conrod conversion functions.
struct WindowRef<'a>(&'a winit::Window);
struct WindowRef<'a>(&'a winit::window::Window);
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating
// compatible conversion functions.
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
fn get_inner_size(&self) -> Option<(u32, u32)> {
winit::Window::get_inner_size(&self.0).map(Into::into)
Some(
winit::window::Window::inner_size(&self.0)
.to_logical::<u32>(self.hidpi_factor())
.into(),
)
}
fn hidpi_factor(&self) -> f32 { winit::Window::get_hidpi_factor(&self.0) as _ }
fn hidpi_factor(&self) -> f64 { winit::window::Window::scale_factor(&self.0) }
}
convert_event!(event, &WindowRef(window.window())).map(Self)
}

View File

@ -19,7 +19,7 @@ pub enum ScaleMode {
pub struct Scale {
mode: ScaleMode,
// Current dpi factor
dpi_factor: f64,
scale_factor: f64,
// Current logical window size
window_dims: Vec2<f64>,
}
@ -27,10 +27,10 @@ pub struct Scale {
impl Scale {
pub fn new(window: &Window, mode: ScaleMode) -> Self {
let window_dims = window.logical_size();
let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
let scale_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
Scale {
mode,
dpi_factor,
scale_factor,
window_dims,
}
}
@ -54,7 +54,7 @@ impl Scale {
// coordinates.
pub fn scale_factor_logical(&self) -> f64 {
match self.mode {
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
ScaleMode::Absolute(scale) => scale / self.scale_factor,
ScaleMode::DpiFactor => 1.0,
ScaleMode::RelativeToWindow(dims) => {
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
@ -64,11 +64,11 @@ impl Scale {
// Calculate factor to transform between physical coordinates and our scaled
// coordinates.
pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.dpi_factor }
pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor }
// Updates internal window size (and/or dpi_factor).
// Updates internal window size (and/or scale_factor).
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
self.scale_factor = renderer.get_resolution().x as f64 / new_dims.x;
self.window_dims = new_dims;
}

View File

@ -4,10 +4,10 @@ use crate::{
settings::{ControlSettings, Settings},
ui, Error,
};
use crossbeam::channel;
use gilrs::{EventType, Gilrs};
use hashbrown::HashMap;
use crossbeam::channel;
use old_school_gfx_glutin_ext::{ContextBuilderExt, WindowInitExt, WindowUpdateExt};
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use tracing::{error, info, warn};
@ -241,7 +241,7 @@ pub enum AnalogGameInput {
}
/// Represents an incoming event from the window.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Event {
/// The window has been requested to close.
Close,
@ -280,19 +280,20 @@ pub enum Event {
ScreenshotMessage(String),
}
pub type MouseButton = winit::MouseButton;
pub type PressState = winit::ElementState;
pub type MouseButton = winit::event::MouseButton;
pub type PressState = winit::event::ElementState;
pub type EventLoop = winit::event_loop::EventLoop<()>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub enum KeyMouse {
Key(glutin::VirtualKeyCode),
Mouse(glutin::MouseButton),
Key(winit::event::VirtualKeyCode),
Mouse(winit::event::MouseButton),
}
impl fmt::Display for KeyMouse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::KeyMouse::*;
use glutin::{MouseButton, VirtualKeyCode::*};
use winit::event::{MouseButton, VirtualKeyCode::*};
write!(f, "{}", match self {
Key(Key1) => "1",
Key(Key2) => "2",
@ -466,23 +467,23 @@ impl fmt::Display for KeyMouse {
}
pub struct Window {
events_loop: glutin::EventsLoop,
renderer: Renderer,
window: glutin::ContextWrapper<glutin::PossiblyCurrent, winit::Window>,
window: glutin::ContextWrapper<glutin::PossiblyCurrent, winit::window::Window>,
cursor_grabbed: bool,
pub pan_sensitivity: u32,
pub zoom_sensitivity: u32,
pub zoom_inversion: bool,
pub mouse_y_inversion: bool,
fullscreen: bool,
modifiers: winit::event::ModifiersState,
needs_refresh_resize: bool,
keypress_map: HashMap<GameInput, glutin::ElementState>,
keypress_map: HashMap<GameInput, winit::event::ElementState>,
pub remapping_keybindings: Option<GameInput>,
supplement_events: Vec<Event>,
events: Vec<Event>,
focused: bool,
gilrs: Option<Gilrs>,
controller_settings: ControllerSettings,
cursor_position: winit::dpi::LogicalPosition,
cursor_position: winit::dpi::PhysicalPosition<f64>,
mouse_emulation_vec: Vec2<f32>,
// Currently used to send and receive screenshot result messages
message_sender: channel::Sender<String>,
@ -490,30 +491,32 @@ pub struct Window {
}
impl Window {
pub fn new(settings: &Settings) -> Result<Window, Error> {
let events_loop = glutin::EventsLoop::new();
pub fn new(settings: &Settings) -> Result<(Window, EventLoop), Error> {
let event_loop = EventLoop::new();
let size = settings.graphics.window_size;
let win_builder = glutin::WindowBuilder::new()
let win_builder = winit::window::WindowBuilder::new()
.with_title("Veloren")
.with_dimensions(glutin::dpi::LogicalSize::new(
size[0] as f64,
size[1] as f64,
))
.with_inner_size(winit::dpi::LogicalSize::new(size[0] as f64, size[1] as f64))
.with_maximized(true);
let ctx_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2)))
.with_vsync(false);
// Avoid cpal / winit OleInitialize conflict
// See: https://github.com/rust-windowing/winit/pull/1524
#[cfg(target_os = "windows")]
let win_builder = winit::platform::windows::WindowBuilderExtWindows::with_drag_and_drop(
win_builder,
false,
);
let (window, device, factory, win_color_view, win_depth_view) =
gfx_window_glutin::init::<WinColorFmt, WinDepthFmt>(
win_builder,
ctx_builder,
&events_loop,
)
.map_err(|err| Error::BackendError(Box::new(err)))?;
glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2)))
.with_vsync(false)
.with_gfx_color_depth::<WinColorFmt, WinDepthFmt>()
.build_windowed(win_builder, &event_loop)
.map_err(|err| Error::BackendError(Box::new(err)))?
.init_gfx::<WinColorFmt, WinDepthFmt>();
let vendor = device.get_info().platform_name.vendor;
let renderer = device.get_info().platform_name.renderer;
@ -559,7 +562,6 @@ impl Window {
) = channel::unbounded::<String>();
let mut this = Self {
events_loop,
renderer: Renderer::new(
device,
factory,
@ -576,14 +578,15 @@ impl Window {
zoom_inversion: settings.gameplay.zoom_inversion,
mouse_y_inversion: settings.gameplay.mouse_y_inversion,
fullscreen: false,
modifiers: Default::default(),
needs_refresh_resize: false,
keypress_map,
remapping_keybindings: None,
supplement_events: vec![],
events: Vec::new(),
focused: true,
gilrs,
controller_settings,
cursor_position: winit::dpi::LogicalPosition::new(0.0, 0.0),
cursor_position: winit::dpi::PhysicalPosition::new(0.0, 0.0),
mouse_emulation_vec: Vec2::zero(),
// Currently used to send and receive screenshot result messages
message_sender,
@ -592,195 +595,30 @@ impl Window {
this.fullscreen(settings.graphics.fullscreen);
Ok(this)
Ok((this, event_loop))
}
pub fn window(
&self,
) -> &glutin::ContextWrapper<glutin::PossiblyCurrent, winit::window::Window> {
&self.window
}
pub fn renderer(&self) -> &Renderer { &self.renderer }
pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
#[allow(clippy::match_bool)] // TODO: Pending review in #587
pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec<Event> {
let mut events = vec![];
events.append(&mut self.supplement_events);
pub fn fetch_events(&mut self) -> Vec<Event> {
// Refresh ui size (used when changing playstates)
if self.needs_refresh_resize {
events.push(Event::Ui(ui::Event::new_resize(self.logical_size())));
self.events
.push(Event::Ui(ui::Event::new_resize(self.logical_size())));
self.needs_refresh_resize = false;
}
// Receive any messages sent through the message channel
self.message_receiver
.try_iter()
.for_each(|message| events.push(Event::ScreenshotMessage(message)));
// Copy data that is needed by the events closure to avoid lifetime errors.
// TODO: Remove this if/when the compiler permits it.
let cursor_grabbed = self.cursor_grabbed;
let renderer = &mut self.renderer;
let window = &mut self.window;
let remapping_keybindings = &mut self.remapping_keybindings;
let focused = &mut self.focused;
let controls = &mut settings.controls;
let keypress_map = &mut self.keypress_map;
let pan_sensitivity = self.pan_sensitivity;
let zoom_sensitivity = self.zoom_sensitivity;
let zoom_inversion = match self.zoom_inversion {
true => -1.0,
false => 1.0,
};
let mouse_y_inversion = match self.mouse_y_inversion {
true => -1.0,
false => 1.0,
};
let mut toggle_fullscreen = false;
let mut take_screenshot = false;
let mut cursor_position = None;
self.events_loop.poll_events(|event| {
// Get events for ui.
if let Some(event) = ui::Event::try_from(event.clone(), window) {
events.push(Event::Ui(event));
}
match event {
glutin::Event::WindowEvent { event, .. } => match event {
glutin::WindowEvent::CloseRequested => events.push(Event::Close),
glutin::WindowEvent::Resized(glutin::dpi::LogicalSize { width, height }) => {
let (mut color_view, mut depth_view) = renderer.win_views_mut();
gfx_window_glutin::update_views(window, &mut color_view, &mut depth_view);
renderer.on_resize().unwrap();
events.push(Event::Resize(Vec2::new(width as u32, height as u32)));
},
glutin::WindowEvent::Moved(glutin::dpi::LogicalPosition { x, y }) => {
events.push(Event::Moved(Vec2::new(x as u32, y as u32)))
},
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
glutin::WindowEvent::MouseInput { button, state, .. } => {
if let (true, Some(game_inputs)) = (
cursor_grabbed,
Window::map_input(
KeyMouse::Mouse(button),
controls,
remapping_keybindings,
),
) {
for game_input in game_inputs {
events.push(Event::InputUpdate(
*game_input,
state == glutin::ElementState::Pressed,
));
}
}
events.push(Event::MouseButton(button, state));
},
glutin::WindowEvent::KeyboardInput { input, .. } => {
if let Some(key) = input.virtual_keycode {
if let Some(game_inputs) = Window::map_input(
KeyMouse::Key(key),
controls,
remapping_keybindings,
) {
for game_input in game_inputs {
match game_input {
GameInput::Fullscreen => {
if input.state == glutin::ElementState::Pressed
&& !Self::is_pressed(
keypress_map,
GameInput::Fullscreen,
)
{
toggle_fullscreen = !toggle_fullscreen;
}
Self::set_pressed(
keypress_map,
GameInput::Fullscreen,
input.state,
);
},
GameInput::Screenshot => {
take_screenshot = input.state
== glutin::ElementState::Pressed
&& !Self::is_pressed(
keypress_map,
GameInput::Screenshot,
);
Self::set_pressed(
keypress_map,
GameInput::Screenshot,
input.state,
);
},
_ => events.push(Event::InputUpdate(
*game_input,
input.state == glutin::ElementState::Pressed,
)),
}
}
}
}
},
glutin::WindowEvent::Focused(state) => {
*focused = state;
events.push(Event::Focused(state));
},
glutin::WindowEvent::CursorMoved { position, .. } => {
cursor_position = Some(position);
},
_ => {},
},
glutin::Event::DeviceEvent { event, .. } => match event {
glutin::DeviceEvent::MouseMotion {
delta: (dx, dy), ..
} if *focused => {
let delta = Vec2::new(
dx as f32 * (pan_sensitivity as f32 / 100.0),
dy as f32 * (pan_sensitivity as f32 * mouse_y_inversion / 100.0),
);
if cursor_grabbed {
events.push(Event::CursorPan(delta));
} else {
events.push(Event::CursorMove(delta));
}
},
glutin::DeviceEvent::MouseWheel { delta, .. } if cursor_grabbed && *focused => {
events.push(Event::Zoom({
// Since scrolling apparently acts different depending on platform
#[cfg(target_os = "windows")]
const PLATFORM_FACTOR: f32 = -4.0;
#[cfg(not(target_os = "windows"))]
const PLATFORM_FACTOR: f32 = 1.0;
let y = match delta {
glutin::MouseScrollDelta::LineDelta(_x, y) => y,
// TODO: Check to see if there is a better way to find the "line
// height" than just hardcoding 16.0 pixels. Alternately we could
// get rid of this and have the user set zoom sensitivity, since
// it's unlikely people would expect a configuration file to work
// across operating systems.
glutin::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32,
};
y * (zoom_sensitivity as f32 / 100.0) * zoom_inversion * PLATFORM_FACTOR
}))
},
_ => {},
},
_ => {},
}
});
if let Some(pos) = cursor_position {
self.cursor_position = pos;
}
if take_screenshot {
self.take_screenshot(&settings);
}
if toggle_fullscreen {
self.toggle_fullscreen(settings);
for message in self.message_receiver.try_iter() {
self.events.push(Event::ScreenshotMessage(message))
}
if let Some(gilrs) = &mut self.gilrs {
@ -808,7 +646,7 @@ impl Window {
| EventType::ButtonRepeated(button, code) => {
handle_buttons(
&self.controller_settings,
&mut events,
&mut self.events,
&Button::from((button, code)),
true,
);
@ -816,7 +654,7 @@ impl Window {
EventType::ButtonReleased(button, code) => {
handle_buttons(
&self.controller_settings,
&mut events,
&mut self.events,
&Button::from((button, code)),
false,
);
@ -843,13 +681,14 @@ impl Window {
},
EventType::AxisChanged(axis, value, code) => {
let value = match self
let value = if self
.controller_settings
.inverted_axes
.contains(&Axis::from((axis, code)))
{
true => value * -1.0,
false => value,
-value
} else {
value
};
let value = self
@ -865,17 +704,17 @@ impl Window {
for action in actions {
match *action {
AxisGameAction::MovementX => {
events.push(Event::AnalogGameInput(
self.events.push(Event::AnalogGameInput(
AnalogGameInput::MovementX(value),
));
},
AxisGameAction::MovementY => {
events.push(Event::AnalogGameInput(
self.events.push(Event::AnalogGameInput(
AnalogGameInput::MovementY(value),
));
},
AxisGameAction::CameraX => {
events.push(Event::AnalogGameInput(
self.events.push(Event::AnalogGameInput(
AnalogGameInput::CameraX(
value
* self.controller_settings.pan_sensitivity
@ -885,7 +724,7 @@ impl Window {
));
},
AxisGameAction::CameraY => {
events.push(Event::AnalogGameInput(
self.events.push(Event::AnalogGameInput(
AnalogGameInput::CameraY(
value
* self.controller_settings.pan_sensitivity
@ -906,22 +745,22 @@ impl Window {
for action in actions {
match *action {
AxisMenuAction::MoveX => {
events.push(Event::AnalogMenuInput(
self.events.push(Event::AnalogMenuInput(
AnalogMenuInput::MoveX(value),
));
},
AxisMenuAction::MoveY => {
events.push(Event::AnalogMenuInput(
self.events.push(Event::AnalogMenuInput(
AnalogMenuInput::MoveY(value),
));
},
AxisMenuAction::ScrollX => {
events.push(Event::AnalogMenuInput(
self.events.push(Event::AnalogMenuInput(
AnalogMenuInput::ScrollX(value),
));
},
AxisMenuAction::ScrollY => {
events.push(Event::AnalogMenuInput(
self.events.push(Event::AnalogMenuInput(
AnalogMenuInput::ScrollY(value),
));
},
@ -936,6 +775,7 @@ impl Window {
}
}
let mut events = std::mem::take(&mut self.events);
// Mouse emulation for the menus, to be removed when a proper menu navigation
// system is available
if !self.cursor_grabbed {
@ -952,46 +792,231 @@ impl Window {
self.mouse_emulation_vec.y = d * -1.0;
None
},
_ => {
let event = Event::AnalogMenuInput(input);
Some(event)
},
},
Event::MenuInput(input, state) => match input {
MenuInput::Apply => Some(match state {
true => Event::Ui(ui::Event(conrod_core::event::Input::Press(
conrod_core::input::Button::Mouse(
conrod_core::input::state::mouse::Button::Left,
),
))),
false => Event::Ui(ui::Event(conrod_core::event::Input::Release(
conrod_core::input::Button::Mouse(
conrod_core::input::state::mouse::Button::Left,
),
))),
}),
_ => Some(event),
input => Some(Event::AnalogMenuInput(input)),
},
Event::MenuInput(MenuInput::Apply, state) => Some(match state {
true => Event::Ui(ui::Event(conrod_core::event::Input::Press(
conrod_core::input::Button::Mouse(
conrod_core::input::state::mouse::Button::Left,
),
))),
false => Event::Ui(ui::Event(conrod_core::event::Input::Release(
conrod_core::input::Button::Mouse(
conrod_core::input::state::mouse::Button::Left,
),
))),
}),
_ => Some(event),
})
.collect();
let sensitivity = self.controller_settings.mouse_emulation_sensitivity;
if self.mouse_emulation_vec != Vec2::zero() {
self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32)
.unwrap_or(());
}
// TODO: make this independent of framerate
// TODO: consider multiplying by scale factor
self.offset_cursor(self.mouse_emulation_vec * sensitivity as f32);
}
events
}
pub fn handle_device_event(&mut self, event: winit::event::DeviceEvent) {
use winit::event::DeviceEvent;
let mouse_y_inversion = match self.mouse_y_inversion {
true => -1.0,
false => 1.0,
};
match event {
DeviceEvent::MouseMotion {
delta: (dx, dy), ..
} if self.focused => {
let delta = Vec2::new(
dx as f32 * (self.pan_sensitivity as f32 / 100.0),
dy as f32 * (self.pan_sensitivity as f32 * mouse_y_inversion / 100.0),
);
if self.cursor_grabbed {
self.events.push(Event::CursorPan(delta));
} else {
self.events.push(Event::CursorMove(delta));
}
},
DeviceEvent::MouseWheel { delta, .. } if self.cursor_grabbed && self.focused => {
self.events.push(Event::Zoom({
// Since scrolling apparently acts different depending on platform
#[cfg(target_os = "windows")]
const PLATFORM_FACTOR: f32 = -4.0;
#[cfg(not(target_os = "windows"))]
const PLATFORM_FACTOR: f32 = 1.0;
let y = match delta {
winit::event::MouseScrollDelta::LineDelta(_x, y) => y,
// TODO: Check to see if there is a better way to find the "line
// height" than just hardcoding 16.0 pixels. Alternately we could
// get rid of this and have the user set zoom sensitivity, since
// it's unlikely people would expect a configuration file to work
// across operating systems.
winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.y / 16.0) as f32,
};
y * (self.zoom_sensitivity as f32 / 100.0)
* if self.zoom_inversion { -1.0 } else { 1.0 }
* PLATFORM_FACTOR
}))
},
_ => {},
}
}
pub fn handle_window_event(
&mut self,
event: winit::event::WindowEvent,
settings: &mut Settings,
) {
use winit::event::WindowEvent;
let controls = &mut settings.controls;
// TODO: these used to be used to deduplicate events which they no longer do
// this needs to be handled elsewhere
let mut toggle_fullscreen = false;
let mut take_screenshot = false;
match event {
WindowEvent::CloseRequested => self.events.push(Event::Close),
WindowEvent::Resized(winit::dpi::PhysicalSize { width, height }) => {
let (mut color_view, mut depth_view) = self.renderer.win_views_mut();
self.window.update_gfx(&mut color_view, &mut depth_view);
self.renderer.on_resize().unwrap();
// TODO: update users of this event with the fact that it is now the physical
// size
self.events
.push(Event::Resize(Vec2::new(width as u32, height as u32)));
},
WindowEvent::ReceivedCharacter(c) => self.events.push(Event::Char(c)),
WindowEvent::MouseInput { button, state, .. } => {
if let (true, Some(game_inputs)) =
// Mouse input not mapped to input if it is not grabbed
(
self.cursor_grabbed,
Window::map_input(
KeyMouse::Mouse(button),
controls,
&mut self.remapping_keybindings,
),
) {
for game_input in game_inputs {
self.events.push(Event::InputUpdate(
*game_input,
state == winit::event::ElementState::Pressed,
));
}
}
self.events.push(Event::MouseButton(button, state));
},
WindowEvent::ModifiersChanged(modifiers) => self.modifiers = modifiers,
WindowEvent::KeyboardInput {
input,
is_synthetic,
..
} => {
// Ignore synthetic tab presses so that we don't get tabs when alt-tabbing back
// into the window
if matches!(
input.virtual_keycode,
Some(winit::event::VirtualKeyCode::Tab)
) && is_synthetic
{
return;
}
// Ignore Alt-F4 so we don't try to do anything heavy like take a screenshot
// when the window is about to close
if matches!(input, winit::event::KeyboardInput {
state: winit::event::ElementState::Pressed,
virtual_keycode: Some(winit::event::VirtualKeyCode::F4),
..
}) && self.modifiers.alt()
{
return;
}
if let Some(key) = input.virtual_keycode {
if let Some(game_inputs) = Window::map_input(
KeyMouse::Key(key),
controls,
&mut self.remapping_keybindings,
) {
for game_input in game_inputs {
match game_input {
GameInput::Fullscreen => {
if input.state == winit::event::ElementState::Pressed
&& !Self::is_pressed(
&mut self.keypress_map,
GameInput::Fullscreen,
)
{
toggle_fullscreen = !toggle_fullscreen;
}
Self::set_pressed(
&mut self.keypress_map,
GameInput::Fullscreen,
input.state,
);
},
GameInput::Screenshot => {
take_screenshot = input.state
== winit::event::ElementState::Pressed
&& !Self::is_pressed(
&mut self.keypress_map,
GameInput::Screenshot,
);
Self::set_pressed(
&mut self.keypress_map,
GameInput::Screenshot,
input.state,
);
},
_ => self.events.push(Event::InputUpdate(
*game_input,
input.state == winit::event::ElementState::Pressed,
)),
}
}
}
}
},
WindowEvent::Focused(state) => {
self.focused = state;
self.events.push(Event::Focused(state));
},
WindowEvent::CursorMoved { position, .. } => {
self.cursor_position = position;
},
_ => {},
}
if take_screenshot {
self.take_screenshot(&settings);
}
if toggle_fullscreen {
self.toggle_fullscreen(settings);
}
}
/// Moves cursor by an offset
pub fn offset_cursor(&self, d: Vec2<f32>) -> Result<(), String> {
self.window
.window()
.set_cursor_position(winit::dpi::LogicalPosition::new(
d.x as f64 + self.cursor_position.x,
d.y as f64 + self.cursor_position.y,
))
pub fn offset_cursor(&self, d: Vec2<f32>) {
if d != Vec2::zero() {
if let Err(err) =
self.window
.window()
.set_cursor_position(winit::dpi::LogicalPosition::new(
d.x as f64 + self.cursor_position.x,
d.y as f64 + self.cursor_position.y,
))
{
error!("Error setting cursor position: {:?}", err);
}
}
}
pub fn swap_buffers(&self) -> Result<(), Error> {
@ -1004,8 +1029,8 @@ impl Window {
pub fn grab_cursor(&mut self, grab: bool) {
self.cursor_grabbed = grab;
self.window.window().hide_cursor(grab);
let _ = self.window.window().grab_cursor(grab);
self.window.window().set_cursor_visible(!grab);
let _ = self.window.window().set_cursor_grab(grab);
}
pub fn toggle_fullscreen(&mut self, settings: &mut Settings) {
@ -1020,7 +1045,13 @@ impl Window {
let window = self.window.window();
self.fullscreen = fullscreen;
if fullscreen {
window.set_fullscreen(Some(window.get_current_monitor()));
window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive(
window
.current_monitor()
.video_modes()
.max_by_key(|mode| mode.size().width)
.expect("No video modes available!!"),
)));
} else {
window.set_fullscreen(None);
}
@ -1033,8 +1064,8 @@ impl Window {
let (w, h) = self
.window
.window()
.get_inner_size()
.unwrap_or(glutin::dpi::LogicalSize::new(0.0, 0.0))
.inner_size()
.to_logical::<f64>(self.window.window().scale_factor())
.into();
Vec2::new(w, h)
}
@ -1048,7 +1079,7 @@ impl Window {
));
}
pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) }
pub fn send_event(&mut self, event: Event) { self.events.push(event) }
pub fn take_screenshot(&mut self, settings: &Settings) {
match self.renderer.create_screenshot() {
@ -1086,15 +1117,20 @@ impl Window {
}
}
fn is_pressed(map: &mut HashMap<GameInput, glutin::ElementState>, input: GameInput) -> bool {
*(map.entry(input).or_insert(glutin::ElementState::Released))
== glutin::ElementState::Pressed
fn is_pressed(
map: &mut HashMap<GameInput, winit::event::ElementState>,
input: GameInput,
) -> bool {
*(map
.entry(input)
.or_insert(winit::event::ElementState::Released))
== winit::event::ElementState::Pressed
}
fn set_pressed(
map: &mut HashMap<GameInput, glutin::ElementState>,
map: &mut HashMap<GameInput, winit::event::ElementState>,
input: GameInput,
state: glutin::ElementState,
state: winit::event::ElementState,
) {
map.insert(input, state);
}
@ -1109,6 +1145,7 @@ impl Window {
remapping: &mut Option<GameInput>,
) -> Option<impl Iterator<Item = &'a GameInput>> {
match *remapping {
// TODO: save settings
Some(game_input) => {
controls.modify_binding(game_input, key_mouse);
*remapping = None;