mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fcd2835eb | |||
| c6767c94a6 | |||
| f1968e3a58 | |||
| 23fdfff1ac | |||
| fc512d9517 | |||
| 3c317460fd | |||
| b1fcaa5cf4 | |||
| 98c9cf142d | |||
| 1eb1503981 | |||
| 5aa9a65e7b | |||
| 71f1d98132 | |||
| 0037c0f344 | |||
| 752e804101 | |||
| 73354246f3 | |||
| a2d23c2f41 | |||
| 6e652c7dbd | |||
| 20749cf036 |
@@ -110,7 +110,7 @@ jobs:
|
||||
path: output/gopher64-${{ matrix.arch }}.exe
|
||||
- name: SignPath
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
uses: signpath/github-action-submit-signing-request@v1.1
|
||||
uses: signpath/github-action-submit-signing-request@v1.3
|
||||
with:
|
||||
api-token: "${{ secrets.SIGNPATH_API_TOKEN }}"
|
||||
organization-id: "${{ secrets.SIGNPATH_ORG_ID }}"
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
artifact-configuration-slug: "gopher64-${{ matrix.arch }}"
|
||||
signing-policy-slug: "release-signing"
|
||||
github-artifact-id: "${{ steps.upload-unsigned-artifact.outputs.artifact-id }}"
|
||||
wait-for-completion: true
|
||||
wait-for-completion: false
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-15
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
path = parallel-rdp/parallel-rdp-standalone
|
||||
url = https://github.com/gopher64/parallel-rdp-standalone.git
|
||||
branch = gopher64
|
||||
[submodule "src/compat/sse2neon"]
|
||||
path = src/compat/sse2neon
|
||||
url = https://github.com/DLTcollab/sse2neon.git
|
||||
|
||||
Generated
+118
-99
@@ -125,9 +125,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.19"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -155,22 +155,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.9"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -296,9 +296,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.0"
|
||||
version = "3.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
@@ -601,9 +601,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
|
||||
checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -653,13 +653,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.14.2"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10929724661d1c43856fd87c7a127ae944ec55579134fb485e4136fb6a46fdcb"
|
||||
checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"polling",
|
||||
"rustix 0.38.44",
|
||||
"rustix 1.0.8",
|
||||
"slab",
|
||||
"tracing",
|
||||
]
|
||||
@@ -678,9 +678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.30"
|
||||
version = "1.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -770,9 +770,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.41"
|
||||
version = "4.5.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -780,9 +780,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.41"
|
||||
version = "4.5.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -1443,9 +1443,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.0"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
@@ -1698,9 +1698,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
|
||||
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
@@ -1938,9 +1938,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gopher64"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"bindgen 0.72.0",
|
||||
"cc",
|
||||
"chrono",
|
||||
@@ -1958,6 +1957,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"rfd",
|
||||
"sdl3-sys",
|
||||
"sdl3-ttf-sys",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-big-array",
|
||||
@@ -1976,9 +1976,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "governor"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbe789d04bf14543f03c4b60cd494148aa79438c8440ae7d81a7778147745c3"
|
||||
checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dashmap",
|
||||
@@ -1999,9 +1999,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
|
||||
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -2207,7 +2207,7 @@ version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7245ddc95cd0ae039b8dbb53449eb94110b24cbd47a32082970a25ad31b77f8d"
|
||||
dependencies = [
|
||||
"calloop 0.14.2",
|
||||
"calloop 0.14.3",
|
||||
"drm",
|
||||
"gbm",
|
||||
"glutin",
|
||||
@@ -2849,9 +2849,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@@ -2876,7 +2876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2917,13 +2917,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"libc",
|
||||
"redox_syscall 0.5.15",
|
||||
"redox_syscall 0.5.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3073,9 +3073,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.5.1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900106b40381855c6acce992ac062513da7fa4acdb0a9324201da616d4812953"
|
||||
checksum = "26175dd096dfaab9f4fbf577a668842ebc48374d4d06b154bffb49918e242261"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-rayon"
|
||||
@@ -3155,9 +3155,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "muda"
|
||||
version = "0.17.0"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58b89bf91c19bf036347f1ab85a81c560f08c0667c8601bece664d860a600988"
|
||||
checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"dpi",
|
||||
@@ -3169,7 +3169,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"thiserror 2.0.12",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3749,7 +3749,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.15",
|
||||
"redox_syscall 0.5.17",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
@@ -3867,9 +3867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.9.0"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
|
||||
checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
@@ -4260,18 +4260,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.15"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
@@ -4438,9 +4438,9 @@ checksum = "5a2d2f3481209a6b42eec2fbb49063fb4e8d35b57023401495d4fe0f85c817f0"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -4491,9 +4491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.29"
|
||||
version = "0.23.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
|
||||
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@@ -4596,21 +4596,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sdl3-src"
|
||||
version = "3.2.18"
|
||||
version = "3.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9b802554a6d768330a2c9cd30849811676da53bda1b5bb78dfd3a402ca6b55f"
|
||||
checksum = "bcf3cbc4d49f7ab594e9f5915fd7a051a327544077a8f1df83411f063df4be08"
|
||||
|
||||
[[package]]
|
||||
name = "sdl3-sys"
|
||||
version = "0.5.3+SDL3-3.2.18"
|
||||
version = "0.5.4+SDL3-3.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "573534f614334b7bb6f7bceae86448764fcdcafe92d75482092810cc664ece8c"
|
||||
checksum = "5c4f7f348dea346f67f38bc1392bf1edd9250d4c586479778e849f6ee61af2d8"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"rpkg-config",
|
||||
"sdl3-src",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sdl3-ttf-src"
|
||||
version = "3.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28923d2ce72ff317d8eb7cec97ddfb8f351c330d7bcbf8c76e91332122c93b3"
|
||||
|
||||
[[package]]
|
||||
name = "sdl3-ttf-sys"
|
||||
version = "0.2.0+SDL3-ttf-3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1d8477496fa2b081fb8f63cf8d0a9a9ba6a7a8f2a21b87e7d13d2ab6d73582"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"rpkg-config",
|
||||
"sdl3-sys",
|
||||
"sdl3-ttf-src",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
@@ -4648,9 +4666,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -4692,9 +4710,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sevenz-rust2"
|
||||
version = "0.17.0"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f9cf03fd1a56301a078ffb13856eeca76514626d78d059ecc598998194679e"
|
||||
checksum = "98644326f9145490e4c194dd775a9a2ed7e84f8254415eb709a7924f52fcd7a1"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
@@ -4733,9 +4751,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.5"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -4957,7 +4975,7 @@ dependencies = [
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2-quartz-core 0.2.2",
|
||||
"raw-window-handle",
|
||||
"redox_syscall 0.5.15",
|
||||
"redox_syscall 0.5.17",
|
||||
"rustix 0.38.44",
|
||||
"tiny-xlib",
|
||||
"wasm-bindgen",
|
||||
@@ -5303,9 +5321,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.46.1"
|
||||
version = "1.47.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
||||
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -5315,10 +5333,10 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2 0.5.10",
|
||||
"socket2 0.6.0",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5356,9 +5374,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.15"
|
||||
version = "0.7.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
|
||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -5850,13 +5868,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.10"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
|
||||
checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix 0.38.44",
|
||||
"rustix 1.0.8",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
@@ -5864,12 +5882,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.10"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
|
||||
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"rustix 0.38.44",
|
||||
"rustix 1.0.8",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
@@ -5887,20 +5905,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-cursor"
|
||||
version = "0.31.10"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182"
|
||||
checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
|
||||
dependencies = [
|
||||
"rustix 0.38.44",
|
||||
"rustix 1.0.8",
|
||||
"wayland-client",
|
||||
"xcursor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.8"
|
||||
version = "0.32.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
|
||||
checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"wayland-backend",
|
||||
@@ -5910,9 +5928,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-plasma"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175"
|
||||
checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"wayland-backend",
|
||||
@@ -5923,9 +5941,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
|
||||
checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"wayland-backend",
|
||||
@@ -5936,9 +5954,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.6"
|
||||
version = "0.31.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
|
||||
checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
@@ -5947,9 +5965,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.6"
|
||||
version = "0.31.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
|
||||
checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"log",
|
||||
@@ -6167,7 +6185,7 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6218,10 +6236,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -6423,9 +6442,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.30.11"
|
||||
version = "0.30.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4"
|
||||
checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"android-activity",
|
||||
@@ -6775,9 +6794,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
@@ -6885,9 +6904,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a"
|
||||
checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "gopher64"
|
||||
version = "1.1.1"
|
||||
version = "1.1.3"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
rust-version = "1.89"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -18,8 +18,8 @@ serde-big-array = "0.5"
|
||||
slint = { version = "1.12", default-features = false, features = ["compat-1-2", "std", "backend-winit", "renderer-femtovg"] }
|
||||
open = "5.3"
|
||||
sha2 = "0.10"
|
||||
ab_glyph = "0.2"
|
||||
sdl3-sys = { version = "0.5", features = ["build-from-source-static"] }
|
||||
sdl3-ttf-sys = { version = "0.2", features = ["build-static-vendored", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "tokio"] }
|
||||
tokio = {version = "1.46", features = ["rt-multi-thread", "macros"] }
|
||||
spin_sleep = "1.3"
|
||||
|
||||
@@ -19,7 +19,7 @@ Keys are mapped according to [these defaults](https://github.com/gopher64/gopher
|
||||
|
||||
## netplay
|
||||
|
||||
Gopher64 supports netplay (online play with others). It has a few public netplay servers. If you are interested in running a public netplay server, please let me know (open an issue or discussion or contact me on Discord). You can also run the server (https://github.com/gopher64/gopher64-netplay-server) yourself on a LAN.
|
||||
Gopher64 supports netplay (online play with others) via cloud hosted servers. You can also run the server (https://github.com/gopher64/gopher64-netplay-server) yourself on a LAN.
|
||||
|
||||
## portable mode
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ fn main() {
|
||||
.include(
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_OUT_DIR").to_owned().unwrap())
|
||||
.join("include"),
|
||||
)
|
||||
.include(
|
||||
std::path::PathBuf::from(std::env::var("DEP_SDL3_TTF_OUT_DIR").to_owned().unwrap())
|
||||
.join("include"),
|
||||
);
|
||||
|
||||
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
@@ -108,6 +112,7 @@ fn main() {
|
||||
.allowlist_function("rdp_set_vi_register")
|
||||
.allowlist_function("rdp_update_screen")
|
||||
.allowlist_function("rdp_process_commands")
|
||||
.allowlist_function("rdp_onscreen_message")
|
||||
.allowlist_function("rdp_check_callback")
|
||||
.allowlist_function("rdp_new_processor")
|
||||
.allowlist_function("rdp_state_size")
|
||||
@@ -123,7 +128,7 @@ fn main() {
|
||||
|
||||
if arch == "aarch64" {
|
||||
let simd_bindings = bindgen::Builder::default()
|
||||
.header("src/compat/sse2neon/v1.8.0/sse2neon.h")
|
||||
.header("src/compat/sse2neon/sse2neon.h")
|
||||
.allowlist_function("_mm_setzero_si128")
|
||||
.allowlist_function("_mm_set_epi8")
|
||||
.allowlist_function("_mm_movemask_epi8")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
#include "spirv.hpp"
|
||||
#include "spirv_crt.hpp"
|
||||
#include <SDL3/SDL_vulkan.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
|
||||
using namespace Vulkan;
|
||||
|
||||
@@ -74,6 +75,12 @@ static GFX_INFO gfx_info;
|
||||
static const uint32_t *fragment_spirv;
|
||||
static size_t fragment_size;
|
||||
|
||||
static TTF_Font *message_font;
|
||||
static std::queue<std::string> messages;
|
||||
static uint64_t message_timer;
|
||||
|
||||
#define MESSAGE_TIME 3000 // 3 seconds
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float SourceSize[4];
|
||||
@@ -200,6 +207,24 @@ bool sdl_event_filter(void *userdata, SDL_Event *event)
|
||||
case SDL_SCANCODE_RIGHTBRACKET:
|
||||
callback.raise_volume = true;
|
||||
break;
|
||||
case SDL_SCANCODE_0:
|
||||
case SDL_SCANCODE_1:
|
||||
case SDL_SCANCODE_2:
|
||||
case SDL_SCANCODE_3:
|
||||
case SDL_SCANCODE_4:
|
||||
case SDL_SCANCODE_5:
|
||||
case SDL_SCANCODE_6:
|
||||
case SDL_SCANCODE_7:
|
||||
case SDL_SCANCODE_8:
|
||||
case SDL_SCANCODE_9:
|
||||
if (event->key.mod & SDL_KMOD_ALT)
|
||||
{
|
||||
if (event->key.scancode == SDL_SCANCODE_0)
|
||||
callback.save_state_slot = 0;
|
||||
else
|
||||
callback.save_state_slot = event->key.scancode - SDL_SCANCODE_1 + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -232,7 +257,23 @@ void rdp_new_processor(GFX_INFO _gfx_info)
|
||||
processor = new RDP::CommandProcessor(wsi->get_device(), gfx_info.RDRAM, 0, gfx_info.RDRAM_SIZE, gfx_info.RDRAM_SIZE / 2, flags);
|
||||
}
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info)
|
||||
static ImageHandle create_message_image(Vulkan::Device &device, int width, const char *message)
|
||||
{
|
||||
SDL_Color fg = {255, 255, 255, 255};
|
||||
SDL_Color bg = {0, 0, 0, 0};
|
||||
SDL_Surface *surface = TTF_RenderText_LCD_Wrapped(message_font, message, 0, fg, bg, width);
|
||||
ImageCreateInfo info = ImageCreateInfo::immutable_2d_image(surface->w, surface->h, VK_FORMAT_B8G8R8A8_UNORM, false);
|
||||
ImageInitialData initial_data = {};
|
||||
initial_data.data = surface->pixels;
|
||||
initial_data.row_length = surface->pitch / 4;
|
||||
initial_data.image_height = surface->h;
|
||||
|
||||
ImageHandle handle = device.create_image(info, &initial_data);
|
||||
SDL_DestroySurface(surface);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font, size_t font_size)
|
||||
{
|
||||
memset(&rdp_device, 0, sizeof(RDP_DEVICE));
|
||||
|
||||
@@ -269,33 +310,51 @@ void rdp_init(void *_window, GFX_INFO _gfx_info)
|
||||
if (!::Vulkan::Context::init_loader((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr()))
|
||||
{
|
||||
rdp_close();
|
||||
return;
|
||||
}
|
||||
if (!wsi->init_simple(1, handles))
|
||||
{
|
||||
rdp_close();
|
||||
return;
|
||||
}
|
||||
|
||||
rdp_new_processor(gfx_info);
|
||||
|
||||
if (!processor->device_is_supported())
|
||||
{
|
||||
delete processor;
|
||||
delete wsi;
|
||||
processor = nullptr;
|
||||
rdp_close();
|
||||
return;
|
||||
}
|
||||
|
||||
message_font = TTF_OpenFontIO(SDL_IOFromConstMem(font, font_size), true, 30.0);
|
||||
if (!message_font)
|
||||
{
|
||||
rdp_close();
|
||||
return;
|
||||
}
|
||||
|
||||
wsi->begin_frame();
|
||||
|
||||
callback.emu_running = true;
|
||||
callback.enable_speedlimiter = true;
|
||||
callback.paused = false;
|
||||
callback.save_state_slot = 0;
|
||||
crop_letterbox = false;
|
||||
|
||||
messages = std::queue<std::string>();
|
||||
message_timer = 0;
|
||||
}
|
||||
|
||||
void rdp_close()
|
||||
{
|
||||
wsi->end_frame();
|
||||
if (wsi)
|
||||
wsi->end_frame();
|
||||
|
||||
if (message_font)
|
||||
{
|
||||
TTF_CloseFont(message_font);
|
||||
message_font = nullptr;
|
||||
}
|
||||
if (processor)
|
||||
{
|
||||
delete processor;
|
||||
@@ -425,11 +484,30 @@ static void render_frame(Vulkan::Device &device)
|
||||
cmd->push_constants(&push, 0, sizeof(push));
|
||||
}
|
||||
|
||||
cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp);
|
||||
cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::NearestClamp);
|
||||
cmd->set_viewport(vp);
|
||||
// The vertices are constants in the shader.
|
||||
// Draws fullscreen quad using oversized triangle.
|
||||
cmd->draw(3);
|
||||
|
||||
if (!messages.empty())
|
||||
{
|
||||
Vulkan::ImageHandle message_image = create_message_image(device, vp.width, messages.front().c_str());
|
||||
cmd->set_texture(0, 0, message_image->get_view(), Vulkan::StockSampler::NearestClamp);
|
||||
vp.x = vp.x + (vp.width - message_image->get_width()) / 2;
|
||||
vp.y = vp.y + vp.height - message_image->get_height();
|
||||
vp.height = message_image->get_height();
|
||||
vp.width = message_image->get_width();
|
||||
cmd->set_viewport(vp);
|
||||
|
||||
cmd->draw(3);
|
||||
|
||||
if (SDL_GetTicks() > message_timer)
|
||||
{
|
||||
messages.pop();
|
||||
message_timer = SDL_GetTicks() + MESSAGE_TIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmd->end_render_pass();
|
||||
@@ -476,6 +554,13 @@ void rdp_load_state(const uint8_t *state)
|
||||
memcpy(&rdp_device, state, sizeof(RDP_DEVICE));
|
||||
}
|
||||
|
||||
void rdp_onscreen_message(const char *_message)
|
||||
{
|
||||
if (messages.empty())
|
||||
message_timer = SDL_GetTicks() + MESSAGE_TIME;
|
||||
messages.push(_message);
|
||||
}
|
||||
|
||||
uint64_t rdp_process_commands()
|
||||
{
|
||||
uint64_t interrupt_timer = 0;
|
||||
|
||||
@@ -34,14 +34,16 @@ extern "C"
|
||||
bool lower_volume;
|
||||
bool raise_volume;
|
||||
bool paused;
|
||||
uint32_t save_state_slot;
|
||||
} CALL_BACK;
|
||||
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info);
|
||||
void rdp_init(void *_window, GFX_INFO _gfx_info, const void *font, size_t font_size);
|
||||
void rdp_close();
|
||||
void rdp_set_vi_register(uint32_t reg, uint32_t value);
|
||||
void rdp_update_screen();
|
||||
CALL_BACK rdp_check_callback();
|
||||
uint64_t rdp_process_commands();
|
||||
void rdp_onscreen_message(const char *_message);
|
||||
void rdp_new_processor(GFX_INFO _gfx_info);
|
||||
size_t rdp_state_size();
|
||||
void rdp_save_state(uint8_t *state);
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.88.0"
|
||||
channel = "1.89.0"
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ pub fn init(
|
||||
|
||||
if !device.cheats.cheats.is_empty() {
|
||||
device.cheats.enabled = true;
|
||||
ui::audio::play_cheat_event(&device.ui);
|
||||
ui::video::onscreen_message(&device.ui, "Cheats enabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "src/compat/sse2neon/v1.8.0/sse2neon.h"
|
||||
#include "src/compat/sse2neon/sse2neon.h"
|
||||
|
||||
// Static wrappers
|
||||
|
||||
|
||||
Submodule
+1
Submodule src/compat/sse2neon added at 3cf69760cc
File diff suppressed because it is too large
Load Diff
@@ -21,16 +21,6 @@ const JDT_EEPROM_16K: u16 = 0xc000; /* 16k EEPROM */
|
||||
const EEPROM_BLOCK_SIZE: usize = 8;
|
||||
pub const EEPROM_MAX_SIZE: usize = 0x800;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub enum CicType {
|
||||
CicNus6101,
|
||||
CicNus6102,
|
||||
CicNus6103,
|
||||
CicNus6105,
|
||||
CicNus6106,
|
||||
CicNus5167,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct AfRtc {
|
||||
pub control: u16,
|
||||
|
||||
@@ -189,20 +189,22 @@ pub fn pak_switch_event(device: &mut device::Device) {
|
||||
write: device::controller::mempak::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(&device.ui, "MemPak selected");
|
||||
} else if new_pak_type == PakType::RumblePak {
|
||||
channel.pak_handler = Some(device::controller::PakHandler {
|
||||
read: device::controller::rumble::read,
|
||||
write: device::controller::rumble::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(&device.ui, "RumblePak selected");
|
||||
} else if new_pak_type == PakType::TransferPak {
|
||||
channel.pak_handler = Some(device::controller::PakHandler {
|
||||
read: device::controller::transferpak::read,
|
||||
write: device::controller::transferpak::write,
|
||||
pak_type: new_pak_type,
|
||||
});
|
||||
ui::video::onscreen_message(&device.ui, "TransferPak selected");
|
||||
}
|
||||
ui::audio::play_pak_switch(&device.ui, new_pak_type);
|
||||
channel.change_pak = PakType::None;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
use crate::{device, savestates};
|
||||
use crate::{device, savestates, ui};
|
||||
|
||||
#[derive(PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum State {
|
||||
@@ -227,7 +227,10 @@ pub fn init(device: &mut device::Device) {
|
||||
device.cpu.clock_rate = if !device.cpu.overclock {
|
||||
93750000
|
||||
} else {
|
||||
println!("Overclocking enabled, setting clock rate to 125 MHz");
|
||||
ui::video::onscreen_message(
|
||||
&device.ui,
|
||||
"Overclocking enabled, setting clock rate to 125 MHz",
|
||||
);
|
||||
125000000
|
||||
};
|
||||
|
||||
|
||||
+4
-14
@@ -46,14 +46,6 @@ struct InputEvent {
|
||||
plugin: u8,
|
||||
}
|
||||
|
||||
pub const NETPLAY_ERROR_DESYNC: u32 = 0;
|
||||
pub const NETPLAY_ERROR_LOST_CONNECTION: u32 = 1;
|
||||
pub const NETPLAY_ERROR_PLAYER_DISCONNECTED: u32 = 2;
|
||||
pub const NETPLAY_ERROR_PLAYER_1_DISCONNECTED: u32 = 3;
|
||||
pub const NETPLAY_ERROR_PLAYER_2_DISCONNECTED: u32 = 4;
|
||||
pub const NETPLAY_ERROR_PLAYER_3_DISCONNECTED: u32 = 5;
|
||||
pub const NETPLAY_ERROR_PLAYER_4_DISCONNECTED: u32 = 6;
|
||||
|
||||
pub fn send_rtc(netplay: &mut Netplay, rtc: i64) {
|
||||
let mut request: Vec<u8> = [TCP_SEND_RTC].to_vec();
|
||||
let size: u32 = 8;
|
||||
@@ -155,7 +147,7 @@ pub fn get_input(device: &mut device::Device, channel: usize) -> ui::input::Inpu
|
||||
.remove(&netplay.player_data[channel].count);
|
||||
|
||||
if std::time::Instant::now() > timeout {
|
||||
ui::audio::play_netplay_audio(&device.ui, NETPLAY_ERROR_LOST_CONNECTION);
|
||||
ui::video::onscreen_message(&device.ui, "Lost connection to netplay server");
|
||||
input = Some(InputEvent {
|
||||
input: 0,
|
||||
plugin: 0,
|
||||
@@ -200,15 +192,13 @@ fn process_incoming(netplay: &mut Netplay, ui: &ui::Ui) {
|
||||
}
|
||||
if current_status != netplay.status {
|
||||
if ((current_status & 0x1) ^ (netplay.status & 0x1)) != 0 {
|
||||
ui::audio::play_netplay_audio(ui, NETPLAY_ERROR_DESYNC);
|
||||
ui::video::onscreen_message(ui, "Netplay desync detected");
|
||||
ui::video::onscreen_message(ui, "Netplay desync detected"); // queue this message twice to ensure they see it
|
||||
}
|
||||
for dis in 1..5 {
|
||||
if ((current_status & (0x1 << dis)) ^ (netplay.status & (0x1 << dis))) != 0
|
||||
{
|
||||
ui::audio::play_netplay_audio(
|
||||
ui,
|
||||
NETPLAY_ERROR_PLAYER_DISCONNECTED + dis,
|
||||
);
|
||||
ui::video::onscreen_message(ui, &format!("Player {dis} disconnected"));
|
||||
}
|
||||
}
|
||||
netplay.status = current_status;
|
||||
|
||||
+19
-7
@@ -87,9 +87,12 @@ pub fn create_savestate(device: &device::Device) {
|
||||
compressed_file,
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
"Savestate created at {}",
|
||||
device.ui.storage.paths.savestate_file_path.display()
|
||||
ui::video::onscreen_message(
|
||||
&device.ui,
|
||||
&format!(
|
||||
"Savestate created in slot {}",
|
||||
device.ui.storage.save_state_slot
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,12 +198,21 @@ pub fn load_savestate(device: &mut device::Device) {
|
||||
ui::audio::close_game_audio(&mut device.ui);
|
||||
ui::audio::init_game_audio(&mut device.ui, device.ai.freq);
|
||||
ui::video::load_state(device, rdp_state.as_ptr());
|
||||
println!(
|
||||
"Savestate loaded from {}",
|
||||
device.ui.storage.paths.savestate_file_path.display()
|
||||
ui::video::onscreen_message(
|
||||
&device.ui,
|
||||
&format!(
|
||||
"Savestate loaded from slot {}",
|
||||
device.ui.storage.save_state_slot
|
||||
),
|
||||
);
|
||||
} else {
|
||||
println!("Failed to load savestate");
|
||||
ui::video::onscreen_message(
|
||||
&device.ui,
|
||||
&format!(
|
||||
"Failed to load savestate from slot {}",
|
||||
device.ui.storage.save_state_slot
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ pub struct Dirs {
|
||||
pub struct Audio {
|
||||
pub audio_device_spec: sdl3_sys::audio::SDL_AudioSpec,
|
||||
pub audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub event_audio_stream: *mut sdl3_sys::audio::SDL_AudioStream,
|
||||
pub audio_device: u32,
|
||||
pub event_audio: audio::EventAudio,
|
||||
pub gain: f32,
|
||||
}
|
||||
|
||||
@@ -32,6 +30,7 @@ pub struct Storage {
|
||||
pub save_type: Vec<storage::SaveTypes>,
|
||||
pub paths: storage::Paths,
|
||||
pub saves: storage::Saves,
|
||||
pub save_state_slot: u32,
|
||||
}
|
||||
|
||||
pub struct Video {
|
||||
@@ -55,6 +54,7 @@ impl Drop for Ui {
|
||||
fn drop(&mut self) {
|
||||
if self.with_sdl {
|
||||
unsafe {
|
||||
sdl3_ttf_sys::ttf::TTF_Quit();
|
||||
sdl3_sys::init::SDL_Quit();
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,14 @@ pub fn sdl_init(flag: sdl3_sys::init::SDL_InitFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ttf_init() {
|
||||
unsafe {
|
||||
if !sdl3_ttf_sys::ttf::TTF_Init() {
|
||||
panic!("Could not initialize TTF");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dirs() -> Dirs {
|
||||
let exe_path = std::env::current_exe().unwrap();
|
||||
let portable_dir = exe_path.parent();
|
||||
@@ -125,6 +133,7 @@ impl Ui {
|
||||
joysticks,
|
||||
},
|
||||
storage: Storage {
|
||||
save_state_slot: 0,
|
||||
save_type: vec![],
|
||||
paths: storage::Paths {
|
||||
eep_file_path: std::path::PathBuf::new(),
|
||||
@@ -167,24 +176,8 @@ impl Ui {
|
||||
game_id: String::new(),
|
||||
game_hash: String::new(),
|
||||
audio: Audio {
|
||||
event_audio: audio::EventAudio {
|
||||
mempak: include_bytes!("../data/mempak.wav").to_vec(),
|
||||
rumblepak: include_bytes!("../data/rumblepak.wav").to_vec(),
|
||||
transferpak: include_bytes!("../data/transferpak.wav").to_vec(),
|
||||
netplay_desync: include_bytes!("../data/netplay_desync.wav").to_vec(),
|
||||
netplay_lost_connection: include_bytes!("../data/netplay_lost_connection.wav")
|
||||
.to_vec(),
|
||||
netplay_disconnected: [
|
||||
include_bytes!("../data/netplay_p1_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p2_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p3_disconnected.wav").to_vec(),
|
||||
include_bytes!("../data/netplay_p4_disconnected.wav").to_vec(),
|
||||
],
|
||||
cheats_enabled: include_bytes!("../data/cheats_enabled.wav").to_vec(),
|
||||
},
|
||||
audio_device_spec: Default::default(),
|
||||
audio_stream: std::ptr::null_mut(),
|
||||
event_audio_stream: std::ptr::null_mut(),
|
||||
audio_device: 0,
|
||||
gain: 1.0,
|
||||
},
|
||||
|
||||
-105
@@ -1,17 +1,6 @@
|
||||
use crate::device;
|
||||
use crate::netplay;
|
||||
use crate::ui;
|
||||
|
||||
pub struct EventAudio {
|
||||
pub mempak: Vec<u8>,
|
||||
pub rumblepak: Vec<u8>,
|
||||
pub transferpak: Vec<u8>,
|
||||
pub netplay_desync: Vec<u8>,
|
||||
pub netplay_lost_connection: Vec<u8>,
|
||||
pub netplay_disconnected: [Vec<u8>; 4],
|
||||
pub cheats_enabled: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
ui::sdl_init(sdl3_sys::init::SDL_INIT_AUDIO);
|
||||
|
||||
@@ -40,24 +29,6 @@ pub fn init(ui: &mut ui::Ui, frequency: u64) {
|
||||
panic!("Could not get audio device format");
|
||||
}
|
||||
|
||||
let wav_audio_spec = sdl3_sys::audio::SDL_AudioSpec {
|
||||
format: sdl3_sys::audio::SDL_AUDIO_S16LE,
|
||||
freq: 24000,
|
||||
channels: 1,
|
||||
};
|
||||
|
||||
ui.audio.event_audio_stream = unsafe {
|
||||
sdl3_sys::audio::SDL_CreateAudioStream(&wav_audio_spec, &ui.audio.audio_device_spec)
|
||||
};
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain)
|
||||
&& sdl3_sys::audio::SDL_BindAudioStream(
|
||||
ui.audio.audio_device,
|
||||
ui.audio.event_audio_stream,
|
||||
)
|
||||
} {
|
||||
panic!("Could not bind audio stream");
|
||||
}
|
||||
init_game_audio(ui, frequency);
|
||||
}
|
||||
|
||||
@@ -85,10 +56,6 @@ pub fn init_game_audio(ui: &mut ui::Ui, frequency: u64) {
|
||||
pub fn close(ui: &mut ui::Ui) {
|
||||
close_game_audio(ui);
|
||||
unsafe {
|
||||
if !ui.audio.event_audio_stream.is_null() {
|
||||
sdl3_sys::audio::SDL_DestroyAudioStream(ui.audio.event_audio_stream);
|
||||
ui.audio.event_audio_stream = std::ptr::null_mut();
|
||||
}
|
||||
sdl3_sys::audio::SDL_CloseAudioDevice(ui.audio.audio_device);
|
||||
ui.audio.audio_device = 0;
|
||||
}
|
||||
@@ -110,7 +77,6 @@ pub fn lower_audio_volume(ui: &mut ui::Ui) {
|
||||
ui.audio.gain = 0.0;
|
||||
}
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.audio_stream, ui.audio.gain);
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,77 +87,6 @@ pub fn raise_audio_volume(ui: &mut ui::Ui) {
|
||||
ui.audio.gain = 2.0;
|
||||
}
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.audio_stream, ui.audio.gain);
|
||||
sdl3_sys::audio::SDL_SetAudioStreamGain(ui.audio.event_audio_stream, ui.audio.gain);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_cheat_event(ui: &ui::Ui) {
|
||||
if ui.audio.event_audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
let sound = &ui.audio.event_audio.cheats_enabled;
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
ui.audio.event_audio_stream,
|
||||
sound.as_ptr() as *const std::ffi::c_void,
|
||||
sound.len() as i32,
|
||||
)
|
||||
} {
|
||||
panic!("Could not play audio");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_netplay_audio(ui: &ui::Ui, error: u32) {
|
||||
if ui.audio.event_audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
let sound = match error {
|
||||
netplay::NETPLAY_ERROR_DESYNC => &ui.audio.event_audio.netplay_desync,
|
||||
netplay::NETPLAY_ERROR_LOST_CONNECTION => &ui.audio.event_audio.netplay_lost_connection,
|
||||
netplay::NETPLAY_ERROR_PLAYER_1_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[0]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_2_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[1]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_3_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[2]
|
||||
}
|
||||
netplay::NETPLAY_ERROR_PLAYER_4_DISCONNECTED => {
|
||||
&ui.audio.event_audio.netplay_disconnected[3]
|
||||
}
|
||||
_ => panic!("Invalid netplay error"),
|
||||
};
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
ui.audio.event_audio_stream,
|
||||
sound.as_ptr() as *const std::ffi::c_void,
|
||||
sound.len() as i32,
|
||||
)
|
||||
} {
|
||||
panic!("Could not play audio");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_pak_switch(ui: &ui::Ui, pak: device::controller::PakType) {
|
||||
if ui.audio.event_audio_stream.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let sound = match pak {
|
||||
device::controller::PakType::RumblePak => &ui.audio.event_audio.rumblepak,
|
||||
device::controller::PakType::MemPak => &ui.audio.event_audio.mempak,
|
||||
device::controller::PakType::TransferPak => &ui.audio.event_audio.transferpak,
|
||||
_ => panic!("Invalid pak type"),
|
||||
};
|
||||
if !unsafe {
|
||||
sdl3_sys::audio::SDL_PutAudioStreamData(
|
||||
ui.audio.event_audio_stream,
|
||||
sound.as_ptr() as *const std::ffi::c_void,
|
||||
sound.len() as i32,
|
||||
)
|
||||
} {
|
||||
panic!("Could not play audio");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ export { NetplayCreate } from "netplay_create.slint";
|
||||
export { NetplayJoin } from "netplay_join.slint";
|
||||
export { NetplayWait } from "netplay_wait.slint";
|
||||
export { CustomNetplayServer } from "netplay_page.slint";
|
||||
export { DispatcherDialog } from "netplay_create.slint";
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
callback wiki_button_clicked;
|
||||
|
||||
@@ -89,10 +89,12 @@ export component ControllerConfig inherits Page {
|
||||
|
||||
if !ControllerData.blank_profiles: GroupBox {
|
||||
title: @tr("Profile");
|
||||
horizontal-stretch: 0.25;
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for player in [0, 1, 2, 3]: ComboBox {
|
||||
model: ControllerData.input_profiles;
|
||||
min-width: self.preferred-width > 150px ? self.preferred-width : 150px;
|
||||
current-index: ControllerData.selected_profile_binding[player];
|
||||
selected => {
|
||||
ControllerData.selected_profile_binding[player] = self.current-index;
|
||||
@@ -103,11 +105,12 @@ export component ControllerConfig inherits Page {
|
||||
|
||||
GroupBox {
|
||||
title: @tr("Controller");
|
||||
horizontal-stretch: 0.75;
|
||||
VerticalLayout {
|
||||
alignment: space-between;
|
||||
for player in [0, 1, 2, 3]: ComboBox {
|
||||
model: ControllerData.controller_names;
|
||||
width: self.preferred-width;
|
||||
min-width: self.preferred-width > 300px ? self.preferred-width : 300px;
|
||||
current-index: ControllerData.selected_controller[player];
|
||||
selected => {
|
||||
ControllerData.selected_controller[player] = self.current-index;
|
||||
|
||||
@@ -6,7 +6,6 @@ export component NetplayCreate inherits Window {
|
||||
in-out property server_names <=> NetplayData.server_names;
|
||||
in-out property server_urls <=> NetplayData.server_urls;
|
||||
in-out property custom_server_url <=> NetplayData.custom_server_url;
|
||||
in-out property ping <=> NetplayData.ping;
|
||||
in-out property game_name <=> NetplayData.game_name;
|
||||
in-out property game_hash <=> NetplayData.game_hash;
|
||||
in-out property game_cheats <=> NetplayData.game_cheats;
|
||||
@@ -14,7 +13,6 @@ export component NetplayCreate inherits Window {
|
||||
in-out property rom_path <=> NetplayData.rom_path;
|
||||
in-out property peer_addr <=> NetplayData.peer_addr;
|
||||
in-out property rom_dir <=> NetplayData.rom_dir;
|
||||
callback get_ping(string);
|
||||
callback get_custom_url();
|
||||
callback select_rom(string);
|
||||
callback create_session(string, string, string, string, string, string, string);
|
||||
@@ -73,17 +71,10 @@ export component NetplayCreate inherits Window {
|
||||
if (self.current-index >= 0) {
|
||||
if (self.current-value == "Custom") {
|
||||
get_custom_url();
|
||||
} else {
|
||||
get_ping(NetplayData.server_urls[self.current-index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
vertical-alignment: center;
|
||||
text: NetplayData.ping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,3 +121,10 @@ export component NetplayCreate inherits Window {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export component DispatcherDialog inherits Dialog {
|
||||
title: @tr("Creating Server");
|
||||
Text {
|
||||
text: "Creating server, please wait...This may take about 30 seconds.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,22 +6,21 @@ export component NetplayJoin inherits Window {
|
||||
in-out property server_names <=> NetplayData.server_names;
|
||||
in-out property server_urls <=> NetplayData.server_urls;
|
||||
in-out property custom_server_url <=> NetplayData.custom_server_url;
|
||||
in-out property ping <=> NetplayData.ping;
|
||||
in-out property sessions <=> NetplayData.sessions;
|
||||
in-out property current_session <=> NetplayData.current_session;
|
||||
in-out property ports <=> NetplayData.ports;
|
||||
in-out property room_urls <=> NetplayData.room_urls;
|
||||
in-out property game_name <=> NetplayData.game_name;
|
||||
in-out property game_hash <=> NetplayData.game_hash;
|
||||
in-out property game_cheats <=> NetplayData.game_cheats;
|
||||
in-out property peer_addr <=> NetplayData.peer_addr;
|
||||
in-out property pending_session <=> NetplayData.pending_session;
|
||||
in-out property pending_refresh <=> NetplayData.pending_refresh;
|
||||
in-out property rom_path <=> NetplayData.rom_path;
|
||||
in-out property rom_dir <=> NetplayData.rom_dir;
|
||||
callback get_ping(string);
|
||||
callback get_custom_url();
|
||||
callback refresh_session(string);
|
||||
callback refresh_session();
|
||||
callback select_rom(string);
|
||||
callback join_session(string, string, string, int);
|
||||
callback join_session(string, string, string, string, string);
|
||||
preferred-width: 700px;
|
||||
|
||||
VerticalBox {
|
||||
HorizontalBox {
|
||||
@@ -40,43 +39,37 @@ export component NetplayJoin inherits Window {
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Server:");
|
||||
text: @tr("Custom Server URL (optional):");
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
server := ComboBox {
|
||||
model: NetplayData.server_names;
|
||||
selected => {
|
||||
if (self.current-index >= 0) {
|
||||
if (self.current-value == "Custom") {
|
||||
get_custom_url();
|
||||
} else {
|
||||
get_ping(NetplayData.server_urls[self.current-index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
vertical-alignment: center;
|
||||
text: NetplayData.ping;
|
||||
LineEdit {
|
||||
placeholder-text: @tr("example.com:45000");
|
||||
text: NetplayData.custom_server_url;
|
||||
edited(text) => {
|
||||
NetplayData.custom_server_url = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if NetplayData.sessions.length > 0: Text {
|
||||
if NetplayData.pending_refresh: Text {
|
||||
text: @tr("Refreshing session list, please wait...");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length > 0: Text {
|
||||
text: @tr("Click on a session below to select it");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
if NetplayData.sessions.length == 0: Text {
|
||||
text: @tr("No sessions available. Please refresh or try a different server.");
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length == 0: Text {
|
||||
text: @tr("No sessions available. Please refresh to check again.");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
StandardTableView {
|
||||
columns: [
|
||||
{ title: "Server" },
|
||||
{ title: "Session Name" },
|
||||
{ title: "Game Name" },
|
||||
{ title: "Password" },
|
||||
@@ -93,13 +86,14 @@ export component NetplayJoin inherits Window {
|
||||
Rectangle { }
|
||||
|
||||
Button {
|
||||
enabled: !NetplayData.pending_refresh;
|
||||
text: @tr("Refresh Session List");
|
||||
clicked => {
|
||||
if (server.current-value == "Custom") {
|
||||
refresh_session("ws://" + NetplayData.custom_server_url);
|
||||
} else {
|
||||
refresh_session(NetplayData.server_urls[server.current-index]);
|
||||
}
|
||||
NetplayData.pending_refresh = true;
|
||||
NetplayData.current_session = -1;
|
||||
NetplayData.sessions = [];
|
||||
NetplayData.room_urls = [];
|
||||
refresh_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +128,8 @@ export component NetplayJoin inherits Window {
|
||||
NetplayData.player_name,
|
||||
NetplayData.game_hash,
|
||||
NetplayData.password,
|
||||
NetplayData.ports[NetplayData.current_session]);
|
||||
NetplayData.room_urls[NetplayData.current_session],
|
||||
NetplayData.sessions[NetplayData.current_session][1].text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Page } from "page.slint";
|
||||
export global NetplayData {
|
||||
in-out property <[string]> server_names;
|
||||
in-out property <[string]> server_urls;
|
||||
in-out property <string> ping: "Ping: Unknown";
|
||||
in-out property <string> player_name;
|
||||
in-out property <string> game_name;
|
||||
in-out property <string> game_hash;
|
||||
@@ -16,9 +15,10 @@ export global NetplayData {
|
||||
in-out property <string> password;
|
||||
in-out property <string> peer_addr;
|
||||
in-out property <bool> pending_session;
|
||||
in-out property <bool> pending_refresh;
|
||||
in-out property <string> session_name;
|
||||
in-out property <[[StandardListViewItem]]> sessions;
|
||||
in-out property <[int]> ports;
|
||||
in-out property <[string]> room_urls;
|
||||
in-out property <int> current_session: -1;
|
||||
in-out property <string> rom_dir;
|
||||
in-out property <string> custom_server_url;
|
||||
|
||||
@@ -7,6 +7,7 @@ export global NetplayWaitData {
|
||||
in-out property <int> port;
|
||||
in-out property <bool> can_start;
|
||||
in-out property <string> chat_text;
|
||||
in-out property <string> ping;
|
||||
}
|
||||
|
||||
export component NetplayWait inherits Window {
|
||||
@@ -21,6 +22,7 @@ export component NetplayWait inherits Window {
|
||||
in-out property port <=> NetplayWaitData.port;
|
||||
in-out property can_start <=> NetplayWaitData.can_start;
|
||||
in-out property chat_text <=> NetplayWaitData.chat_text;
|
||||
in-out property ping <=> NetplayWaitData.ping;
|
||||
callback send_chat_message(string);
|
||||
callback begin_game;
|
||||
|
||||
@@ -37,6 +39,11 @@ export component NetplayWait inherits Window {
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Ping: ") + NetplayWaitData.ping;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
GridBox {
|
||||
Row {
|
||||
Text {
|
||||
|
||||
+17
-3
@@ -466,6 +466,7 @@ fn close_controllers(
|
||||
|
||||
pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO);
|
||||
ui::ttf_init();
|
||||
|
||||
if profile == "default" {
|
||||
println!("Profile name cannot be default");
|
||||
@@ -567,8 +568,18 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
axis: 0,
|
||||
};
|
||||
|
||||
let font =
|
||||
ab_glyph::FontRef::try_from_slice(include_bytes!("../../data/Roboto-Regular.ttf")).unwrap();
|
||||
let text_engine = unsafe { sdl3_ttf_sys::ttf::TTF_CreateRendererTextEngine(renderer) };
|
||||
let font = unsafe {
|
||||
let font_bytes = include_bytes!("../../data/Roboto-Regular.ttf");
|
||||
sdl3_ttf_sys::ttf::TTF_OpenFontIO(
|
||||
sdl3_sys::everything::SDL_IOFromConstMem(
|
||||
font_bytes.as_ptr() as *const std::ffi::c_void,
|
||||
font_bytes.len(),
|
||||
),
|
||||
true,
|
||||
35.0,
|
||||
)
|
||||
};
|
||||
|
||||
for (key, value) in key_labels.iter() {
|
||||
let mut event: sdl3_sys::events::SDL_Event = Default::default();
|
||||
@@ -577,7 +588,8 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
ui::video::draw_text(
|
||||
format!("Select binding for: {key}").as_str(),
|
||||
renderer,
|
||||
&font,
|
||||
text_engine,
|
||||
font,
|
||||
);
|
||||
|
||||
let mut key_set = false;
|
||||
@@ -671,6 +683,8 @@ pub fn configure_input_profile(ui: &mut ui::Ui, profile: String, dinput: bool) {
|
||||
close_controllers(open_joysticks, open_controllers);
|
||||
|
||||
unsafe {
|
||||
sdl3_ttf_sys::ttf::TTF_CloseFont(font);
|
||||
sdl3_ttf_sys::ttf::TTF_DestroyRendererTextEngine(text_engine);
|
||||
sdl3_sys::render::SDL_DestroyRenderer(renderer);
|
||||
sdl3_sys::video::SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
+346
-229
@@ -1,8 +1,8 @@
|
||||
use crate::device;
|
||||
use crate::ui;
|
||||
use crate::ui::gui::{
|
||||
AppWindow, CustomNetplayServer, ErrorDialog, GameSettings, GbPaths, NetplayCreate,
|
||||
NetplayDevice, NetplayJoin, NetplayWait, VruChannel, run_rom, save_settings,
|
||||
AppWindow, CustomNetplayServer, DispatcherDialog, ErrorDialog, GameSettings, GbPaths,
|
||||
NetplayCreate, NetplayDevice, NetplayJoin, NetplayWait, VruChannel, run_rom, save_settings,
|
||||
};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -47,17 +47,14 @@ pub struct NetplayMessage {
|
||||
trait NetplayPages {
|
||||
fn set_server_names(&self, names: slint::ModelRc<slint::SharedString>);
|
||||
fn set_server_urls(&self, urls: slint::ModelRc<slint::SharedString>);
|
||||
fn set_ping(&self, ping: slint::SharedString);
|
||||
fn set_game_name(&self, game_name: slint::SharedString);
|
||||
fn set_game_hash(&self, game_hash: slint::SharedString);
|
||||
fn set_game_cheats(&self, game_cheats: slint::SharedString);
|
||||
fn set_rom_path(&self, rom_path: slint::SharedString);
|
||||
fn set_peer_addr(&self, peer_addr: slint::SharedString);
|
||||
fn refresh_sessions(&self, _server: slint::SharedString) {
|
||||
fn refresh_sessions(&self) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
fn invoke_get_ping(&self, server_url: slint::SharedString);
|
||||
fn set_custom_server_url(&self, server_url: slint::SharedString);
|
||||
}
|
||||
|
||||
impl NetplayPages for NetplayCreate {
|
||||
@@ -67,9 +64,6 @@ impl NetplayPages for NetplayCreate {
|
||||
fn set_server_urls(&self, urls: slint::ModelRc<slint::SharedString>) {
|
||||
self.set_server_urls(urls);
|
||||
}
|
||||
fn set_ping(&self, ping: slint::SharedString) {
|
||||
self.set_ping(ping);
|
||||
}
|
||||
fn set_game_name(&self, game_name: slint::SharedString) {
|
||||
self.set_game_name(game_name);
|
||||
}
|
||||
@@ -85,12 +79,6 @@ impl NetplayPages for NetplayCreate {
|
||||
fn set_peer_addr(&self, peer_addr: slint::SharedString) {
|
||||
self.set_peer_addr(peer_addr);
|
||||
}
|
||||
fn invoke_get_ping(&self, server_url: slint::SharedString) {
|
||||
self.invoke_get_ping(server_url);
|
||||
}
|
||||
fn set_custom_server_url(&self, server_url: slint::SharedString) {
|
||||
self.set_custom_server_url(server_url);
|
||||
}
|
||||
}
|
||||
|
||||
impl NetplayPages for NetplayJoin {
|
||||
@@ -100,11 +88,8 @@ impl NetplayPages for NetplayJoin {
|
||||
fn set_server_urls(&self, urls: slint::ModelRc<slint::SharedString>) {
|
||||
self.set_server_urls(urls);
|
||||
}
|
||||
fn set_ping(&self, ping: slint::SharedString) {
|
||||
self.set_ping(ping);
|
||||
}
|
||||
fn refresh_sessions(&self, server: slint::SharedString) {
|
||||
self.invoke_refresh_session(server);
|
||||
fn refresh_sessions(&self) {
|
||||
self.invoke_refresh_session();
|
||||
}
|
||||
fn set_game_name(&self, game_name: slint::SharedString) {
|
||||
self.set_game_name(game_name);
|
||||
@@ -121,16 +106,13 @@ impl NetplayPages for NetplayJoin {
|
||||
fn set_peer_addr(&self, peer_addr: slint::SharedString) {
|
||||
self.set_peer_addr(peer_addr);
|
||||
}
|
||||
fn invoke_get_ping(&self, server_url: slint::SharedString) {
|
||||
self.invoke_get_ping(server_url);
|
||||
}
|
||||
fn set_custom_server_url(&self, server_url: slint::SharedString) {
|
||||
self.set_custom_server_url(server_url);
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_server_names<T: ComponentHandle + NetplayPages + 'static>(weak: slint::Weak<T>) {
|
||||
let task = reqwest::get("https://cdn.gopher64.com/servers-gopher64.json");
|
||||
let task = reqwest::Client::new()
|
||||
.get("https://dispatch.gopher64.com/getRegions")
|
||||
.header("netplay-id", EMU_NAME)
|
||||
.send();
|
||||
tokio::spawn(async move {
|
||||
let mut local_servers: Vec<(String, String)> = vec![];
|
||||
|
||||
@@ -158,10 +140,9 @@ fn populate_server_names<T: ComponentHandle + NetplayPages + 'static>(weak: slin
|
||||
}
|
||||
|
||||
let response = task.await;
|
||||
if let Ok(response) = response {
|
||||
let servers: std::collections::HashMap<String, String> = response.json().await.unwrap();
|
||||
|
||||
let weak2 = weak.clone();
|
||||
if let Ok(response) = response
|
||||
&& let Ok(servers) = response.json::<Vec<String>>().await
|
||||
{
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let server_names: slint::VecModel<slint::SharedString> = slint::VecModel::default();
|
||||
let server_urls: slint::VecModel<slint::SharedString> = slint::VecModel::default();
|
||||
@@ -170,12 +151,11 @@ fn populate_server_names<T: ComponentHandle + NetplayPages + 'static>(weak: slin
|
||||
server_urls.push(local_server.1.into());
|
||||
}
|
||||
for server in servers {
|
||||
server_names.push(server.0.into());
|
||||
server_urls.push(server.1.into());
|
||||
server_names.push(server.clone().into());
|
||||
server_urls.push(format!("dispatcher:{server}").into());
|
||||
}
|
||||
server_names.push("Custom".into());
|
||||
update_ping(weak2, server_urls.row_data(0).unwrap().into());
|
||||
handle.refresh_sessions(server_urls.row_data(0).unwrap());
|
||||
handle.refresh_sessions();
|
||||
let server_names_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(server_names);
|
||||
let server_urls_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
@@ -207,7 +187,7 @@ fn select_rom<T: ComponentHandle + NetplayPages + 'static>(
|
||||
if let Some(file) = select_rom.await {
|
||||
if let Some(rom_contents) = device::get_rom_contents(file.path()) {
|
||||
let hash = device::cart::rom::calculate_hash(&rom_contents);
|
||||
let game_name = ui::storage::get_game_name(&rom_contents);
|
||||
let mut game_name = ui::storage::get_game_name(&rom_contents);
|
||||
let game_crc = ui::storage::get_game_crc(&rom_contents);
|
||||
let cheats = ui::config::Cheats::new();
|
||||
let mut parsed_cheats = "".to_string();
|
||||
@@ -216,6 +196,14 @@ fn select_rom<T: ComponentHandle + NetplayPages + 'static>(
|
||||
{
|
||||
parsed_cheats = serde_json::to_string(game_cheats).unwrap();
|
||||
}
|
||||
if game_name.is_empty() {
|
||||
game_name = file
|
||||
.path()
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
}
|
||||
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_game_name(game_name.into());
|
||||
@@ -245,48 +233,13 @@ fn select_rom<T: ComponentHandle + NetplayPages + 'static>(
|
||||
});
|
||||
}
|
||||
|
||||
fn update_ping<T: ComponentHandle + NetplayPages + 'static>(
|
||||
weak: slint::Weak<T>,
|
||||
server_url: String,
|
||||
) {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_ping("Ping: Unknown".into());
|
||||
})
|
||||
.unwrap();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(Ok((mut sock, _response))) = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(1),
|
||||
tokio_tungstenite::connect_async(server_url),
|
||||
)
|
||||
.await
|
||||
{
|
||||
sock.send(Message::Ping(Vec::new().into())).await.unwrap();
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
if let Some(Ok(_response)) = sock.next().await {
|
||||
let elapsed = start.elapsed();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_ping(format!("Ping: {:.0} ms", elapsed.as_millis()).into());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
sock.close(None).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn show_custom_url_dialog<T: ComponentHandle + NetplayPages + 'static>(
|
||||
weak: slint::Weak<T>,
|
||||
server_url: slint::SharedString,
|
||||
) {
|
||||
fn show_custom_url_dialog(weak: slint::Weak<NetplayCreate>, server_url: slint::SharedString) {
|
||||
let url_dialog = CustomNetplayServer::new().unwrap();
|
||||
url_dialog.set_custom_server_url(server_url);
|
||||
let weak_dialog = url_dialog.as_weak();
|
||||
url_dialog.on_ok_clicked(move |server_url| {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_custom_server_url(server_url.clone());
|
||||
let prefix: slint::SharedString = "ws://".into();
|
||||
handle.invoke_get_ping(prefix + &server_url);
|
||||
})
|
||||
.unwrap();
|
||||
weak_dialog.unwrap().window().hide().unwrap();
|
||||
@@ -313,14 +266,9 @@ pub fn setup_create_window(
|
||||
create_window.set_rom_dir(rom_dir);
|
||||
populate_server_names(create_window.as_weak());
|
||||
let weak = create_window.as_weak();
|
||||
create_window.on_get_ping(move |server_url| {
|
||||
update_ping(weak.clone(), server_url.to_string());
|
||||
});
|
||||
let weak = create_window.as_weak();
|
||||
create_window.on_get_custom_url(move || {
|
||||
let weak2 = weak.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_ping("Ping: Unknown".into());
|
||||
show_custom_url_dialog(weak2, handle.get_custom_server_url());
|
||||
})
|
||||
.unwrap();
|
||||
@@ -340,26 +288,87 @@ pub fn setup_create_window(
|
||||
game_cheats,
|
||||
password| {
|
||||
let _ = netplay_write_sender.send(None); // close current websocket if any
|
||||
manage_websocket(
|
||||
server_url.to_string(),
|
||||
netplay_read_sender.clone(),
|
||||
netplay_write_receiver.resubscribe(),
|
||||
weak.clone(),
|
||||
);
|
||||
if server_url.starts_with("dispatcher:") {
|
||||
let message_dialog = DispatcherDialog::new().unwrap();
|
||||
let weak_dialog = message_dialog.as_weak();
|
||||
message_dialog.show().unwrap();
|
||||
|
||||
create_session(
|
||||
netplay_write_sender.clone(),
|
||||
netplay_read_receiver.resubscribe(),
|
||||
session_name.to_string(),
|
||||
player_name.to_string(),
|
||||
game_name.to_string(),
|
||||
game_hash.to_string(),
|
||||
game_cheats.to_string(),
|
||||
password.to_string(),
|
||||
game_settings.clone(),
|
||||
weak_app.clone(),
|
||||
weak.clone(),
|
||||
);
|
||||
let task = reqwest::Client::new()
|
||||
.get("https://dispatch.gopher64.com/createServer")
|
||||
.query(&[("region", server_url.strip_prefix("dispatcher:").unwrap())])
|
||||
.header("netplay-id", EMU_NAME)
|
||||
.send();
|
||||
let netplay_read_sender = netplay_read_sender.clone();
|
||||
let netplay_write_receiver = netplay_write_receiver.resubscribe();
|
||||
let netplay_write_sender = netplay_write_sender.clone();
|
||||
let netplay_read_receiver = netplay_read_receiver.resubscribe();
|
||||
let game_settings = game_settings.clone();
|
||||
let weak = weak.clone();
|
||||
let weak_app = weak_app.clone();
|
||||
tokio::spawn(async move {
|
||||
let response = task.await;
|
||||
weak_dialog
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
handle.window().hide().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
if let Ok(response) = response
|
||||
&& let Ok(server) = response
|
||||
.json::<std::collections::HashMap<String, String>>()
|
||||
.await
|
||||
{
|
||||
let server_url = server.values().next().unwrap();
|
||||
|
||||
manage_websocket(
|
||||
server_url.to_string(),
|
||||
netplay_read_sender,
|
||||
netplay_write_receiver,
|
||||
weak.clone(),
|
||||
);
|
||||
|
||||
create_session(
|
||||
netplay_write_sender,
|
||||
netplay_read_receiver,
|
||||
session_name.to_string(),
|
||||
player_name.to_string(),
|
||||
game_name.to_string(),
|
||||
game_hash.to_string(),
|
||||
game_cheats.to_string(),
|
||||
password.to_string(),
|
||||
game_settings,
|
||||
weak_app,
|
||||
weak.clone(),
|
||||
);
|
||||
} else {
|
||||
weak.upgrade_in_event_loop(|handle| {
|
||||
handle.set_pending_session(false);
|
||||
show_netplay_error("Server could not be created".to_string());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
manage_websocket(
|
||||
server_url.to_string(),
|
||||
netplay_read_sender.clone(),
|
||||
netplay_write_receiver.resubscribe(),
|
||||
weak.clone(),
|
||||
);
|
||||
|
||||
create_session(
|
||||
netplay_write_sender.clone(),
|
||||
netplay_read_receiver.resubscribe(),
|
||||
session_name.to_string(),
|
||||
player_name.to_string(),
|
||||
game_name.to_string(),
|
||||
game_hash.to_string(),
|
||||
game_cheats.to_string(),
|
||||
password.to_string(),
|
||||
game_settings.clone(),
|
||||
weak_app.clone(),
|
||||
weak.clone(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -412,8 +421,11 @@ fn manage_websocket<T: ComponentHandle + NetplayPages + 'static>(
|
||||
Ok(None) => {
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("netplay_write_receiver error: {err}");
|
||||
Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {
|
||||
panic!("netplay_write_receiver lagged");
|
||||
}
|
||||
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
|
||||
break; // exit the loop if the receiver is closed
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,68 +445,112 @@ fn show_netplay_error(message: String) {
|
||||
message_dialog.show().unwrap();
|
||||
}
|
||||
|
||||
fn clear_sessions(handle: &NetplayJoin, message: Option<String>) {
|
||||
handle.set_sessions(slint::ModelRc::default());
|
||||
handle.set_ports(slint::ModelRc::default());
|
||||
handle.set_current_session(-1);
|
||||
if let Some(message) = message {
|
||||
show_netplay_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_sessions(
|
||||
netplay_write_sender: tokio::sync::broadcast::Sender<Option<NetplayMessage>>,
|
||||
mut netplay_read_receiver: tokio::sync::broadcast::Receiver<NetplayMessage>,
|
||||
weak: slint::Weak<NetplayJoin>,
|
||||
) {
|
||||
fn update_sessions(weak: slint::Weak<NetplayJoin>) {
|
||||
let task = reqwest::Client::new()
|
||||
.get("https://dispatch.gopher64.com/getServers")
|
||||
.header("netplay-id", EMU_NAME)
|
||||
.send();
|
||||
tokio::spawn(async move {
|
||||
let now_utc = chrono::Utc::now().timestamp_millis().to_string();
|
||||
let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME);
|
||||
let request_rooms = NetplayMessage {
|
||||
message_type: "request_get_rooms".to_string(),
|
||||
player_name: None,
|
||||
client_sha: None,
|
||||
netplay_version: Some(NETPLAY_VERSION),
|
||||
player_names: None,
|
||||
emulator: Some(EMU_NAME.to_string()),
|
||||
accept: None,
|
||||
rooms: None,
|
||||
message: None,
|
||||
auth_time: Some(now_utc),
|
||||
auth: Some(format!("{:x}", hasher.finalize())),
|
||||
room: None,
|
||||
};
|
||||
|
||||
netplay_write_sender.send(Some(request_rooms)).unwrap();
|
||||
|
||||
match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(2),
|
||||
netplay_read_receiver.recv(),
|
||||
)
|
||||
.await
|
||||
let mut dispatcher_servers = std::collections::HashMap::new();
|
||||
let response = task.await;
|
||||
if let Ok(response) = response
|
||||
&& let Ok(servers) = response
|
||||
.json::<std::collections::HashMap<String, String>>()
|
||||
.await
|
||||
{
|
||||
Ok(Ok(message)) => {
|
||||
if message.accept.unwrap() == 0 {
|
||||
if let Some(rooms) = message.rooms {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let sessions_vec = slint::VecModel::default();
|
||||
let ports_vec = slint::VecModel::default();
|
||||
dispatcher_servers = servers;
|
||||
}
|
||||
let weak2 = weak.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let mut servers: std::collections::HashMap<String, String> =
|
||||
std::collections::HashMap::new();
|
||||
let server_names = handle.get_server_names();
|
||||
let server_urls = handle.get_server_urls();
|
||||
for (i, server_name) in server_names.iter().enumerate() {
|
||||
if server_name == "Custom" {
|
||||
let custom_server_url = handle.get_custom_server_url();
|
||||
if !custom_server_url.is_empty() {
|
||||
servers.insert(
|
||||
"Custom".to_string(),
|
||||
"ws://".to_string() + &custom_server_url,
|
||||
);
|
||||
}
|
||||
} else if let Some(url) = server_urls.row_data(i)
|
||||
&& url.starts_with("dispatcher:")
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
servers.insert(
|
||||
server_name.to_string(),
|
||||
server_urls.row_data(i).unwrap().to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
servers.extend(dispatcher_servers);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut sessions = vec![];
|
||||
let mut room_urls = vec![];
|
||||
for (server_name, server_url) in servers.iter() {
|
||||
if let Ok(Ok((socket, _response))) = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(2),
|
||||
tokio_tungstenite::connect_async(server_url.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
let (mut write, mut read) = socket.split();
|
||||
|
||||
let now_utc = chrono::Utc::now().timestamp_millis().to_string();
|
||||
let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME);
|
||||
let request_rooms = NetplayMessage {
|
||||
message_type: "request_get_rooms".to_string(),
|
||||
player_name: None,
|
||||
client_sha: None,
|
||||
netplay_version: Some(NETPLAY_VERSION),
|
||||
player_names: None,
|
||||
emulator: Some(EMU_NAME.to_string()),
|
||||
accept: None,
|
||||
rooms: None,
|
||||
message: None,
|
||||
auth_time: Some(now_utc),
|
||||
auth: Some(format!("{:x}", hasher.finalize())),
|
||||
room: None,
|
||||
};
|
||||
write
|
||||
.send(Message::Binary(Bytes::from(
|
||||
serde_json::to_vec(&request_rooms).unwrap(),
|
||||
)))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(Ok(response)) = read.next().await
|
||||
&& let Ok(message) =
|
||||
serde_json::from_slice::<NetplayMessage>(&response.into_data())
|
||||
&& message.message_type == "reply_get_rooms"
|
||||
&& message.accept.unwrap() == 0
|
||||
&& let Some(rooms) = message.rooms
|
||||
{
|
||||
for room in rooms {
|
||||
let session_vec = slint::VecModel::default();
|
||||
session_vec.push(slint::StandardListViewItem::from(
|
||||
let mut session = vec![];
|
||||
room_urls.push(server_url.into());
|
||||
|
||||
session.push(slint::StandardListViewItem::from(
|
||||
slint::SharedString::from(server_name),
|
||||
));
|
||||
session.push(slint::StandardListViewItem::from(
|
||||
slint::SharedString::from(room.room_name.unwrap()),
|
||||
));
|
||||
session_vec.push(slint::StandardListViewItem::from(
|
||||
session.push(slint::StandardListViewItem::from(
|
||||
slint::SharedString::from(room.game_name.unwrap()),
|
||||
));
|
||||
session_vec.push(slint::StandardListViewItem::from(
|
||||
session.push(slint::StandardListViewItem::from(
|
||||
slint::SharedString::from(if room.protected.unwrap() {
|
||||
"True"
|
||||
} else {
|
||||
"False"
|
||||
}),
|
||||
));
|
||||
session_vec.push(slint::StandardListViewItem::from(
|
||||
session.push(slint::StandardListViewItem::from(
|
||||
slint::SharedString::from(
|
||||
if room.features.unwrap_or_default().contains_key("cheats")
|
||||
{
|
||||
@@ -504,45 +560,38 @@ fn update_sessions(
|
||||
},
|
||||
),
|
||||
));
|
||||
let session_model: std::rc::Rc<
|
||||
slint::VecModel<slint::StandardListViewItem>,
|
||||
> = std::rc::Rc::new(session_vec);
|
||||
sessions_vec.push(slint::ModelRc::from(session_model));
|
||||
ports_vec.push(room.port.unwrap());
|
||||
sessions.push(session);
|
||||
}
|
||||
let rooms_model: std::rc::Rc<
|
||||
slint::VecModel<slint::ModelRc<slint::StandardListViewItem>>,
|
||||
> = std::rc::Rc::new(sessions_vec);
|
||||
let ports_model: std::rc::Rc<slint::VecModel<i32>> =
|
||||
std::rc::Rc::new(ports_vec);
|
||||
handle.set_sessions(slint::ModelRc::from(rooms_model));
|
||||
handle.set_ports(slint::ModelRc::from(ports_model));
|
||||
handle.set_current_session(-1);
|
||||
})
|
||||
.unwrap();
|
||||
} else {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
clear_sessions(&handle, None);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
clear_sessions(&handle, message.message);
|
||||
}
|
||||
weak2
|
||||
.upgrade_in_event_loop(move |handle| {
|
||||
let sessions_vec = slint::VecModel::default();
|
||||
for session in sessions.iter() {
|
||||
let session_vec = slint::VecModel::from(session.to_vec());
|
||||
let session_model: std::rc::Rc<
|
||||
slint::VecModel<slint::StandardListViewItem>,
|
||||
> = std::rc::Rc::new(session_vec);
|
||||
sessions_vec.push(slint::ModelRc::from(session_model));
|
||||
}
|
||||
let rooms_model: std::rc::Rc<
|
||||
slint::VecModel<slint::ModelRc<slint::StandardListViewItem>>,
|
||||
> = std::rc::Rc::new(sessions_vec);
|
||||
handle.set_sessions(slint::ModelRc::from(rooms_model));
|
||||
|
||||
let room_urls_vec = slint::VecModel::from(room_urls.to_vec());
|
||||
let room_urls_model: std::rc::Rc<slint::VecModel<slint::SharedString>> =
|
||||
std::rc::Rc::new(room_urls_vec);
|
||||
handle.set_room_urls(slint::ModelRc::from(room_urls_model));
|
||||
|
||||
handle.set_current_session(-1);
|
||||
handle.set_pending_refresh(false);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
panic!("netplay_read_receiver error: {err}");
|
||||
}
|
||||
Err(_) => {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
clear_sessions(&handle, Some("Server did not respond".to_string()));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -608,7 +657,7 @@ fn create_session(
|
||||
.await
|
||||
{
|
||||
Ok(Ok(message)) => {
|
||||
if message.accept.unwrap() == 0 {
|
||||
if message.message_type == "reply_create_room" && message.accept.unwrap() == 0 {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let session = message.room.as_ref().unwrap();
|
||||
let features_default = "false".to_string();
|
||||
@@ -683,12 +732,62 @@ fn join_session(
|
||||
player_name: String,
|
||||
game_hash: String,
|
||||
password: String,
|
||||
port: i32,
|
||||
room_name: String,
|
||||
fullscreen: bool,
|
||||
weak_app: slint::Weak<AppWindow>,
|
||||
weak: slint::Weak<NetplayJoin>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
let now_utc = chrono::Utc::now().timestamp_millis().to_string();
|
||||
let hasher = Sha256::new().chain_update(&now_utc).chain_update(EMU_NAME);
|
||||
let request_rooms = NetplayMessage {
|
||||
message_type: "request_get_rooms".to_string(),
|
||||
player_name: None,
|
||||
client_sha: None,
|
||||
netplay_version: Some(NETPLAY_VERSION),
|
||||
player_names: None,
|
||||
emulator: Some(EMU_NAME.to_string()),
|
||||
accept: None,
|
||||
rooms: None,
|
||||
message: None,
|
||||
auth_time: Some(now_utc),
|
||||
auth: Some(format!("{:x}", hasher.finalize())),
|
||||
room: None,
|
||||
};
|
||||
|
||||
netplay_write_sender.send(Some(request_rooms)).unwrap();
|
||||
|
||||
let mut port = 0;
|
||||
match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(2),
|
||||
netplay_read_receiver.recv(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(message)) => {
|
||||
if message.message_type == "reply_get_rooms"
|
||||
&& message.accept.unwrap() == 0
|
||||
&& let Some(rooms) = message.rooms
|
||||
{
|
||||
for room in rooms {
|
||||
if room.room_name.unwrap() == room_name {
|
||||
port = room.port.unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
panic!("netplay_read_receiver error: {err}");
|
||||
}
|
||||
Err(_) => {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_pending_session(false);
|
||||
show_netplay_error("Server did not respond".to_string());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let join_room = NetplayMessage {
|
||||
message_type: "request_join_room".to_string(),
|
||||
player_name: Some(player_name),
|
||||
@@ -722,7 +821,7 @@ fn join_session(
|
||||
.await
|
||||
{
|
||||
Ok(Ok(message)) => {
|
||||
if message.accept.unwrap() == 0 {
|
||||
if message.message_type == "reply_join_room" && message.accept.unwrap() == 0 {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
let session = message.room.as_ref().unwrap();
|
||||
let features_default = "false".to_string();
|
||||
@@ -814,6 +913,7 @@ fn setup_wait_window(
|
||||
wait.set_rom_path(rom_path);
|
||||
wait.set_port(port);
|
||||
wait.set_can_start(can_start);
|
||||
wait.set_ping("Unknown".into());
|
||||
|
||||
let sender = netplay_write_sender.clone();
|
||||
wait.on_send_chat_message(move |message| {
|
||||
@@ -894,11 +994,44 @@ fn setup_wait_window(
|
||||
|
||||
netplay_write_sender.send(Some(motd_message)).unwrap();
|
||||
|
||||
let ping_message = NetplayMessage {
|
||||
message_type: "request_ping".to_string(),
|
||||
player_name: None,
|
||||
client_sha: None,
|
||||
netplay_version: None,
|
||||
emulator: None,
|
||||
accept: None,
|
||||
rooms: None,
|
||||
player_names: None,
|
||||
message: None,
|
||||
auth_time: None,
|
||||
auth: None,
|
||||
room: None,
|
||||
};
|
||||
|
||||
netplay_write_sender
|
||||
.send(Some(ping_message.clone()))
|
||||
.unwrap();
|
||||
|
||||
let weak = wait.as_weak();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
match netplay_read_receiver.recv().await {
|
||||
Ok(response) => match response.message_type.as_str() {
|
||||
"reply_ping" => {
|
||||
if let Some(message) = response.message {
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_ping((message + " ms").into());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
let ping_message = ping_message.clone();
|
||||
let ping_writer = netplay_write_sender.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
let _ = ping_writer.send(Some(ping_message));
|
||||
});
|
||||
}
|
||||
"reply_motd" => {
|
||||
let request_players = NetplayMessage {
|
||||
message_type: "request_players".to_string(),
|
||||
@@ -1044,26 +1177,10 @@ pub fn setup_join_window(
|
||||
tokio::sync::broadcast::Receiver<Option<NetplayMessage>>,
|
||||
) = tokio::sync::broadcast::channel(5);
|
||||
|
||||
join_window.set_pending_refresh(true);
|
||||
join_window.set_rom_dir(rom_dir);
|
||||
populate_server_names(join_window.as_weak());
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_get_ping(move |server_url| {
|
||||
update_ping(weak.clone(), server_url.to_string());
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.invoke_refresh_session(server_url);
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_get_custom_url(move || {
|
||||
let weak2 = weak.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
handle.set_ping("Ping: Unknown".into());
|
||||
show_custom_url_dialog(weak2, handle.get_custom_server_url());
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_select_rom(move |rom_dir| {
|
||||
select_rom(weak.clone(), rom_dir);
|
||||
});
|
||||
@@ -1073,34 +1190,34 @@ pub fn setup_join_window(
|
||||
let _ = sender.send(None); // close current websocket if any
|
||||
slint::CloseRequestResponse::HideWindow
|
||||
});
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_refresh_session(move || {
|
||||
update_sessions(weak.clone());
|
||||
});
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_join_session(
|
||||
move |player_name, game_hash, password, room_url, room_name| {
|
||||
let _ = netplay_write_sender.send(None); // close current websocket if any
|
||||
manage_websocket(
|
||||
room_url.to_string(),
|
||||
netplay_read_sender.clone(),
|
||||
netplay_write_receiver.resubscribe(),
|
||||
weak.clone(),
|
||||
);
|
||||
|
||||
let weak = join_window.as_weak();
|
||||
let sender = netplay_write_sender.clone();
|
||||
let receiver = netplay_read_receiver.resubscribe();
|
||||
join_window.on_refresh_session(move |server_url| {
|
||||
let _ = sender.send(None); // close current websocket if any
|
||||
manage_websocket(
|
||||
server_url.to_string(),
|
||||
netplay_read_sender.clone(),
|
||||
netplay_write_receiver.resubscribe(),
|
||||
weak.clone(),
|
||||
);
|
||||
update_sessions(sender.clone(), receiver.resubscribe(), weak.clone());
|
||||
});
|
||||
let weak = join_window.as_weak();
|
||||
join_window.on_join_session(move |player_name, game_hash, password, port| {
|
||||
join_session(
|
||||
netplay_write_sender.clone(),
|
||||
netplay_read_receiver.resubscribe(),
|
||||
player_name.to_string(),
|
||||
game_hash.to_string(),
|
||||
password.to_string(),
|
||||
port,
|
||||
fullscreen,
|
||||
weak_app.clone(),
|
||||
weak.clone(),
|
||||
);
|
||||
});
|
||||
join_session(
|
||||
netplay_write_sender.clone(),
|
||||
netplay_read_receiver.resubscribe(),
|
||||
player_name.to_string(),
|
||||
game_hash.to_string(),
|
||||
password.to_string(),
|
||||
room_name.to_string(),
|
||||
fullscreen,
|
||||
weak_app.clone(),
|
||||
weak.clone(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
join_window.show().unwrap();
|
||||
}
|
||||
|
||||
+7
-4
@@ -199,10 +199,13 @@ pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.clone_from(&states_path);
|
||||
ui.storage
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.push(prefix.to_owned() + "-" + &ui.game_hash + ".state");
|
||||
ui.storage.paths.savestate_file_path.push(
|
||||
prefix.to_owned()
|
||||
+ "-"
|
||||
+ &ui.game_hash
|
||||
+ ".state"
|
||||
+ ui.storage.save_state_slot.to_string().as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
|
||||
|
||||
+40
-49
@@ -1,10 +1,10 @@
|
||||
#![allow(non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/parallel_bindings.rs"));
|
||||
use crate::{device, ui};
|
||||
use ab_glyph::{Font, ScaleFont};
|
||||
|
||||
pub fn init(device: &mut device::Device) {
|
||||
ui::sdl_init(sdl3_sys::init::SDL_INIT_VIDEO);
|
||||
ui::ttf_init();
|
||||
|
||||
let title = std::ffi::CString::new("gopher64").unwrap();
|
||||
|
||||
@@ -74,7 +74,15 @@ pub fn init(device: &mut device::Device) {
|
||||
crt: device.ui.config.video.crt,
|
||||
};
|
||||
|
||||
unsafe { rdp_init(device.ui.video.window as *mut std::ffi::c_void, gfx_info) }
|
||||
unsafe {
|
||||
let font_bytes = include_bytes!("../../data/Roboto-Regular.ttf");
|
||||
rdp_init(
|
||||
device.ui.video.window as *mut std::ffi::c_void,
|
||||
gfx_info,
|
||||
font_bytes.as_ptr() as *const std::ffi::c_void,
|
||||
font_bytes.len(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(ui: &ui::Ui) {
|
||||
@@ -143,6 +151,19 @@ pub fn check_callback(device: &mut device::Device) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
if device.ui.storage.save_state_slot != callback.save_state_slot {
|
||||
ui::video::onscreen_message(
|
||||
&device.ui,
|
||||
&format!("Switching savestate slot to {}", callback.save_state_slot,),
|
||||
);
|
||||
device.ui.storage.save_state_slot = callback.save_state_slot;
|
||||
device
|
||||
.ui
|
||||
.storage
|
||||
.paths
|
||||
.savestate_file_path
|
||||
.set_extension("state".to_owned() + callback.save_state_slot.to_string().as_str());
|
||||
}
|
||||
if callback.lower_volume {
|
||||
ui::audio::lower_audio_volume(&mut device.ui);
|
||||
} else if callback.raise_volume {
|
||||
@@ -161,60 +182,30 @@ pub fn process_rdp_list() -> u64 {
|
||||
unsafe { rdp_process_commands() }
|
||||
}
|
||||
|
||||
pub fn onscreen_message(_ui: &ui::Ui, message: &str) {
|
||||
unsafe { rdp_onscreen_message(std::ffi::CString::new(message).unwrap().as_ptr()) };
|
||||
}
|
||||
|
||||
pub fn draw_text(
|
||||
text: &str,
|
||||
renderer: *mut sdl3_sys::render::SDL_Renderer,
|
||||
font: &ab_glyph::FontRef,
|
||||
text_engine: *mut sdl3_ttf_sys::ttf::TTF_TextEngine,
|
||||
font: *mut sdl3_ttf_sys::ttf::TTF_Font,
|
||||
) {
|
||||
// Clear the canvas
|
||||
unsafe {
|
||||
sdl3_sys::render::SDL_SetRenderDrawColor(
|
||||
renderer,
|
||||
let (mut w, mut h) = (0, 0);
|
||||
sdl3_sys::render::SDL_GetRenderOutputSize(renderer, &mut w, &mut h);
|
||||
|
||||
let ttf_text = sdl3_ttf_sys::ttf::TTF_CreateText(
|
||||
text_engine,
|
||||
font,
|
||||
std::ffi::CString::new(text).unwrap().as_ptr(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sdl3_sys::pixels::SDL_ALPHA_OPAQUE,
|
||||
);
|
||||
sdl3_sys::render::SDL_RenderClear(renderer);
|
||||
};
|
||||
|
||||
let text_size = 40.0;
|
||||
let (mut w, mut h) = (0, 0);
|
||||
unsafe { sdl3_sys::render::SDL_GetRenderOutputSize(renderer, &mut w, &mut h) };
|
||||
let x_start = 20.0;
|
||||
let y_start = (h / 2) as f32;
|
||||
|
||||
let mut x_offset = 0.0;
|
||||
for c in text.chars() {
|
||||
let q_glyph_id = font.glyph_id(c);
|
||||
let q_glyph = q_glyph_id.with_scale(text_size);
|
||||
|
||||
if let Some(q) = font.outline_glyph(q_glyph) {
|
||||
q.draw(|x, y, c| {
|
||||
if c > 0.5 {
|
||||
unsafe {
|
||||
sdl3_sys::render::SDL_SetRenderDrawColor(
|
||||
renderer,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
sdl3_sys::pixels::SDL_ALPHA_OPAQUE,
|
||||
);
|
||||
sdl3_sys::render::SDL_RenderPoint(
|
||||
renderer,
|
||||
x_start + x_offset + x as f32 - q.px_bounds().width()
|
||||
+ q.px_bounds().max.x,
|
||||
y_start + y as f32 - q.px_bounds().height() + q.px_bounds().max.y,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
x_offset += font.as_scaled(text_size).h_advance(q_glyph_id);
|
||||
}
|
||||
|
||||
// Present the canvas
|
||||
if !unsafe { sdl3_sys::render::SDL_RenderPresent(renderer) } {
|
||||
panic!("Could not present renderer");
|
||||
sdl3_sys::everything::SDL_RenderClear(renderer);
|
||||
sdl3_ttf_sys::ttf::TTF_DrawRendererText(ttf_text, 20.0, h as f32 / 2.0);
|
||||
sdl3_sys::render::SDL_RenderPresent(renderer);
|
||||
sdl3_ttf_sys::ttf::TTF_DestroyText(ttf_text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user