diff options
46 files changed, 8398 insertions, 0 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..fde969b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target-dir = "./.cargo/shared/target" diff --git a/.cargo/scripts/generate_documents.bat b/.cargo/scripts/generate_documents.bat new file mode 100644 index 0000000..edde8ea --- /dev/null +++ b/.cargo/scripts/generate_documents.bat @@ -0,0 +1,59 @@ +@echo off +setlocal enabledelayedexpansion + +:: ================= 路径 ================= +set "CARGO_TARGET_DIR=.\.cargo\shared\target" +set "DOC_SOURCE_DIR=%CARGO_TARGET_DIR%\doc" +set "DOC_DEST_DIR=.\documents\cargo_doc" + +:: ============== Crate 列表 ============== +set "CRATE_PATHS=core console" + +:: =============== 文档路径 =============== +set "DOC_LINK_PATHS=nogamepads_core nogpadc nogpads" + +:: =============== 生成文档 =============== +if not exist "%DOC_DEST_DIR%" mkdir "%DOC_DEST_DIR%" +:: cargo clean --target-dir "%CARGO_TARGET_DIR%" +for %%c in (%CRATE_PATHS%) do ( + echo [INFO] Generating documentation for crate: %%c + cargo doc --no-deps --manifest-path "./%%c/Cargo.toml" + if !errorlevel! neq 0 ( + echo [ERROR] Failed to generate docs for crate: %%c + exit /b !errorlevel! + ) +) + +:: ============== 清理旧文件 ============== +if exist "%DOC_DEST_DIR%" ( + echo [INFO] Removing old documents... + rmdir /s /q "%DOC_DEST_DIR%" +) + +:: ============== 复制新文件 ============== +echo [INFO] Copying new documents to %DOC_DEST_DIR%... +xcopy /E /I /Q /Y "%DOC_SOURCE_DIR%" "%DOC_DEST_DIR%" +if !errorlevel! neq 0 ( + echo [ERROR] Failed to copy documents + exit /b !errorlevel! +) + +:: ================= 清理 ================= +:: Uncomment to clean generated docs from target directory +:: rmdir /s /q "%DOC_SOURCE_DIR%" + +:: =============== 生成链接 =============== +echo. +echo [DOCUMENTATION LINKS] + +:: 获取当前目录绝对路径(转换为URL格式) +set "CURRENT_DIR=%CD:\=/%" + +:: 遍历自定义路径列表 +for %%p in (%DOC_LINK_PATHS%) do ( + set "HTML_PATH=file:///%CURRENT_DIR%/documents/cargo_doc/%%p/index.html" + echo !HTML_PATH! +) + +echo [SUCCESS] Documentation generated at: %DOC_DEST_DIR% +endlocal
\ No newline at end of file diff --git a/.cargo/scripts/release_console/generate_console_release_file.bat b/.cargo/scripts/release_console/generate_console_release_file.bat new file mode 100644 index 0000000..601391b --- /dev/null +++ b/.cargo/scripts/release_console/generate_console_release_file.bat @@ -0,0 +1,63 @@ +@echo off +setlocal enabledelayedexpansion + +:: ================= 路径 ================= +:: 源文件目录 +set "CONSOLE_DIR=.cargo\shared\target\release\deps" +:: 待复制文件列表 +set "FILE_LIST=.cargo\scripts\release_files_win" +:: 控制台文件目标目录(基础路径) +set "BASE_DEST_DIR=release\dev\" +:: 证书文件 +set "LICENSE_FILE=LICENSE-LGPL-2" +set "LICENSE_TP_FILE=LICENSE-THIRD-PARTY" + +:: ============= 创建基础目录 ============= +if not exist "%BASE_DEST_DIR%" mkdir "%BASE_DEST_DIR%" + +:: =========== 清空目标目录内容 =========== +if exist "%BASE_DEST_DIR%" ( + echo [INFO] Cleaning target directory "%BASE_DEST_DIR%" + :: 删除所有文件(包括隐藏/只读文件) + del /f /s /q "%BASE_DEST_DIR%\*.*" >nul 2>&1 + :: 递归删除所有子目录 + for /d %%D in ("%BASE_DEST_DIR%\*") do rd /s /q "%%D" >nul 2>&1 +) + +:: ======== 读取文件列表并复制文件 ======== +for /f "delims=" %%F in ('findstr /v /r "^// ^$" "%FILE_LIST%"') do ( + set "line=%%F" + :: 分割行内容为 filename 和相对路径 + for /f "tokens=1,* delims=:" %%A in ("!line!") do ( + set "filename=%%A" + set "rel_path=%%B" + ) + :: 去除两端的空格 + for /f "tokens=* delims= " %%L in ("!filename!") do set "filename=%%L" + for /f "tokens=* delims= " %%P in ("!rel_path!") do set "rel_path=%%P" + :: 构造完整目标路径 + set "dest_path=%BASE_DEST_DIR%!rel_path:.\=!" + :: 提取目标目录并创建 + for %%I in ("!dest_path!") do set "dest_dir=%%~dpI" + if not exist "!dest_dir!" mkdir "!dest_dir!" + :: 复制文件 + set "source_file=%CONSOLE_DIR%\!filename!" + if exist "!source_file!" ( + echo [INFO] Copying "!filename!" to "!dest_path!" + copy /Y "!source_file!" "!dest_path!" >nul + if errorlevel 1 ( + echo [ERROR] Failed to copy "!filename!" + ) else ( + echo [SUCCESS] Copied "!filename!" + ) + ) else ( + echo [ERROR] Source file "!source_file!" not found + ) +) + +:: ============= 复制证书文件 ============= +copy /Y "%LICENSE_FILE%" "%BASE_DEST_DIR%\" >nul +copy /Y "%LICENSE_TP_FILE%" "%BASE_DEST_DIR%\" >nul + +endlocal +exit /b 0
\ No newline at end of file diff --git a/.cargo/scripts/release_files_win b/.cargo/scripts/release_files_win new file mode 100644 index 0000000..bfc7841 --- /dev/null +++ b/.cargo/scripts/release_files_win @@ -0,0 +1,6 @@ +// 控制台部分 +nogpadc.exe : .\bin\nogpadc.exe +nogpads.exe : .\bin\nogpads.exe + +// Dll部分 +nogamepads_c.dll : .\bridge\libs\nogamepads_c.dll
\ No newline at end of file diff --git a/.cargo/scripts/release_project.bat b/.cargo/scripts/release_project.bat new file mode 100644 index 0000000..3979e9d --- /dev/null +++ b/.cargo/scripts/release_project.bat @@ -0,0 +1,37 @@ +@echo off +setlocal enabledelayedexpansion + +:: ================= 路径 ================= +:: 控制台程序的 CRATE +set "CONSOLE_CRATE_DIR=console" + +:: C动态链接库的 CRATE +set "DLL_CRATE_DIR=core_c" + +:: 打包地址 +set "RELEASE_DIR=release\dev" + +:: ================= 构建 ================= +:: 控制台程序 +echo [INFO] Building console ... +pushd ".\%CONSOLE_CRATE_DIR%\" +cargo build --release +popd +echo +:: C动态链接库 +echo [INFO] Building dll ... +pushd ".\%DLL_CRATE_DIR%\" +cargo build --release +popd + +echo Done. +echo + +:: =============== 发布文件 =============== +echo +echo [INFO] Generating release files +call .\.cargo\scripts\release_console\generate_console_release_file.bat + +explorer.exe ".\%RELEASE_DIR%\" + +echo Done.
\ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8dd64d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.cargo/shared/ +/release/dev/ +/documents/cargo_doc/
\ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/NoGamepads.iml b/.idea/NoGamepads.iml new file mode 100644 index 0000000..188f62f --- /dev/null +++ b/.idea/NoGamepads.iml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="EMPTY_MODULE" version="4"> + <component name="NewModuleRootManager"> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/core/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/console/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/core/examples" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/core_c/src" isTestSource="false" /> + <excludeFolder url="file://$MODULE_DIR$/.cargo/shared" /> + <excludeFolder url="file://$MODULE_DIR$/core/target" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/console/target" /> + <excludeFolder url="file://$MODULE_DIR$/documents/cargo_doc" /> + <excludeFolder url="file://$MODULE_DIR$/release/dev" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..87df6da --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/NoGamepads.iml" filepath="$PROJECT_DIR$/.idea/NoGamepads.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project>
\ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..faaeb0f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1505 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "clearscreen" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c41dc435a7b98e4608224bbf65282309f5403719df9113621b30f8b6f74e2f4" +dependencies = [ + "nix", + "terminfo", + "thiserror 2.0.12", + "which", + "windows-sys 0.59.0", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nogamepads" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "env_logger", + "log", + "shell-words", + "tokio", +] + +[[package]] +name = "nogamepads-console" +version = "0.1.0" +dependencies = [ + "clap", + "nogamepads-core", + "prettytable-rs", + "rand 0.9.1", + "rpassword", + "serde", + "serde_yaml", +] + +[[package]] +name = "nogamepads-core" +version = "0.1.0" +dependencies = [ + "bincode", + "clap", + "clearscreen", + "hex", + "log", + "nogamepads", + "serde", + "sha1", + "tokio", +] + +[[package]] +name = "nogamepads_c" +version = "0.1.0" +dependencies = [ + "nogamepads-core", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5507615 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "nogamepads" +authors = [ "CatilGrass" ] +description = "Connect everyone's phones to the game! A mobile device control solution for local multiplayer gaming, featuring an in-game interface, controller client, and support for universal devices." +license = "LGPL-2" + +homepage = "https://github.com/CatilGrass/NoGamepads" +repository = "https://github.com/CatilGrass/NoGamepads" +readme = "README.md" + +version = "0.1.0" +edition = "2024" + +[workspace] +members = [ + "core", + "console", + "core_c" +] + +[dependencies] +env_logger = "0.10.2" +shell-words = "1.1.0" +clap = { version = "4.5.38", features = ["derive"] } +tokio = { version = "1.45.0", features = ["full"] } +chrono = "0.4" +log = "0.4.27" + +[profile.release] +strip = true +debug = false + +[profile.dev] +strip = false +debug = true diff --git a/LICENSE-LGPL-2 b/LICENSE-LGPL-2 new file mode 100644 index 0000000..a00e7d8 --- /dev/null +++ b/LICENSE-LGPL-2 @@ -0,0 +1,464 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + NoGamePads - Connect everyone's phones to the game! + Copyright (C) 2025-2026 Zewen Pan + + Contact information : + - Email: catil_grass@qq.com
\ No newline at end of file diff --git a/LICENSE-THIRD-PARTY b/LICENSE-THIRD-PARTY new file mode 100644 index 0000000..12bfdd9 --- /dev/null +++ b/LICENSE-THIRD-PARTY @@ -0,0 +1,273 @@ +THIRD-PARTY LICENSES +===================== + +This project uses third-party components with the following licenses: + +[MIT License Dependencies] +----------------------------------------------------------------------- +* bincode +* tokio +* chrono (original license: Apache-2.0 OR MIT) +* clap (original license: Apache-2.0 OR MIT) +* clearscreen (original license: Apache-2.0 OR MIT) +* env_logger (original license: Apache-2.0 OR MIT) +* hex (original license: Apache-2.0 OR MIT) +* log (original license: Apache-2.0 OR MIT) +* rand (original license: Apache-2.0 OR MIT) +* serde (original license: Apache-2.0 OR MIT) +* serde_yaml (original license: Apache-2.0 OR MIT) +* sha1 (original license: Apache-2.0 OR MIT) +* shell-words (original license: Apache-2.0 OR MIT) + +MIT License + +Copyright (c) 2009-2014 Mozilla Foundation +Copyright (c) 2013-2014 The Rust Project Developers. + +* tokio - Copyright (c) Tokio Contributors. +* chrono - Copyright (c) 2014, Kang Seonghoon. +* clap - Copyright (c) The clap Project Developer(s). +* clearscreen - Copyright (c) The clearscreen Project Developer(s). +* env_logger - Copyright (c) The env_logger Project Developer(s). +* hex - Copyright (c) 2015-2020 The rust-hex Developers. +* log - Copyright (c) The log Project Developer(s). +* rand - Copyright 2018 Developers of the Rand project. +* serde - Copyright (c) The serde Project Developer(s). +* serde_yaml - Copyright (c) The serde_yaml Project Developer(s). + +* sha1 - + Copyright (c) 2006-2009 Graydon Hoare + Copyright (c) 2016 Artyom Pavlov + +* shell-words - Copyright (c) 2016 Tomasz Miąsko + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the “Software”), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +[Apache-2.0 License Dependencies] +----------------------------------------------------------------------- +* rpassword + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +[BSD-3-Clause License Dependencies] +----------------------------------------------------------------------- +* prettytable-rs + +Copyright (c) 2022, Pierre-Henri Symoneaux +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of prettytable-rs nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/console/Cargo.lock b/console/Cargo.lock new file mode 100644 index 0000000..17c623e --- /dev/null +++ b/console/Cargo.lock @@ -0,0 +1,1482 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "clearscreen" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c41dc435a7b98e4608224bbf65282309f5403719df9113621b30f8b6f74e2f4" +dependencies = [ + "nix", + "terminfo", + "thiserror 2.0.12", + "which", + "windows-sys 0.59.0", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +dependencies = [ + "memchr", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nogamepads-console" +version = "0.1.0" +dependencies = [ + "clap", + "nogamepads-core", + "prettytable-rs", + "rand 0.9.1", + "rpassword", + "serde", + "serde_yaml", +] + +[[package]] +name = "nogamepads-core" +version = "0.1.0" +dependencies = [ + "bincode", + "chrono", + "clap", + "clearscreen", + "env_logger", + "hex", + "log", + "serde", + "sha1", + "shell-words", + "tokio", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/console/Cargo.toml b/console/Cargo.toml new file mode 100644 index 0000000..03ec382 --- /dev/null +++ b/console/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nogamepads-console" +description = "This crate provides a set of tools for simulating and deploying the client and server of the NoGamepads library." + +homepage = "https://github.com/CatilGrass/NoGamepads" +repository = "https://github.com/CatilGrass/NoGamepads" + +version = "0.1.0" +edition = "2024" + +[dependencies] +nogamepads-core = { path = "../core" } +clap = { version = "4.5.38", features = ["derive"] } +rand = { version = "0.9.1", features = [] } +rpassword = "7.4.0" +serde = { version = "1.0.219", features = ["derive"] } +serde_yaml = "0.9.34" +prettytable-rs = "0.10.0"
\ No newline at end of file diff --git a/console/src/bin/nogpadc.rs b/console/src/bin/nogpadc.rs new file mode 100644 index 0000000..a4b6245 --- /dev/null +++ b/console/src/bin/nogpadc.rs @@ -0,0 +1,436 @@ +use crate::AccountCommands::{Add, Customize, List, Remove}; +use crate::Commands::{Account, Connect}; +use clap::{Args, Parser, Subcommand}; +use nogamepads_core::pad_io::client::nogamepads_client::PadClient; +use nogamepads_core::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; +use nogamepads_core::DEFAULT_PORT; +use prettytable::{row, Table}; +use rand::Rng; +use std::env::current_dir; +use std::fs::{create_dir_all, remove_file, File}; +use std::io::{BufReader, Write}; +use std::net::{IpAddr, Ipv4Addr}; +use std::path::PathBuf; +use std::process::exit; +use std::str::FromStr; + +/// NoGamePads Console - Client Cli +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct NoGamepadClientCli { + #[command(subcommand)] + command: Commands, +} + +/// 主要命令 +#[derive(Subcommand, Debug)] +enum Commands { + + // 账户设置 + #[command(subcommand, about = "Operation of player account")] + Account(AccountCommands), + + // 连接到服务器 + #[command(about = "Connect a player to server")] + Connect(ConnectArgs) +} + +/// 账户设置 命令 +#[derive(Subcommand, Debug)] +enum AccountCommands { + + // 列出所有账号 + #[command(about = "List all local players")] + List(ListAccountArgs), + + // 添加账号 + #[command(about = "Add a local player")] + Add(AddAccountArgs), + + // 移除账号 + #[command(about = "Remove a local player")] + Remove(RemoveAccountArgs), + + // 自定义账号显示信息 + #[command(about = "Customize how the player appears")] + Customize(CustomizeAccountArgs) +} + +/// 列出所有账号 参数 +#[derive(Args, Debug)] +struct ListAccountArgs { } + +/// 添加账号 参数 +#[derive(Args, Debug)] +struct AddAccountArgs { + + // 注册的角色 ID + #[arg(value_name = "NAME")] + id: String +} + +/// 移除账号 参数 +#[derive(Args, Debug)] +struct RemoveAccountArgs { + + // 删除的角色 ID + #[arg(value_name = "NAME")] + id: String +} + +/// 自定义账号显示信息 参数 +#[derive(Args, Debug)] +struct CustomizeAccountArgs { + + // 定制的角色 ID + #[arg(value_name = "WHO")] + id: String, + + // 昵称 + #[arg(short, long, help = "Nickname")] + name: Option<String>, + + // 颜色 + #[arg(short = 'H', long = "hsv", num_args = 3, value_names = ["H", "S", "V"], help = "Player color, h(0 - 360), s(0 - 1), v(0 - 1)")] + hsv: Option<Vec<f64>> +} + +/// 连接到服务器 +#[derive(Args, Debug)] +struct ConnectArgs { + + // 连接的玩家 + #[arg(value_name = "WHO")] + id: String, + + // 目标服务器 + #[arg(value_name = "WHERE", default_value = "127.0.0.1")] + target: String, + + // 目标端口 + #[arg(short, long, default_value = "5989")] // <---- DEFAULT_PORT + port: Option<u16>, + + // 启用调试 + #[arg(long)] + debug: bool, +} + +/// 配置文件后缀名称 +const EXTENSION_NAME : &str = "yaml"; + +fn main() { + + // 初始化 + let root = get_config_folder_path(); + if ! root.exists() { + create_dir_all(root.as_path()).unwrap(); + } + + // 命令行 + let cli = NoGamepadClientCli::parse(); + match cli.command { + + // 账户设置部分 + Account(commands) => { + match commands { + + // 添加账号 + Add(args) => { + if is_account_exist(&args.id) { + println!("Account already exists!"); + } else { + // 账号 ID 和 配置路径 + let account_id = process_inputted_text(args.id); + let account_config_path = get_account_config_path(&account_id); + + // 为新号准备的配置文件 + let mut new_info = PlayerInfo::new(); + + // 密码输入和密码验证 + let password = rpassword::prompt_password("Type password: ").unwrap(); + let password_confirm = rpassword::prompt_password("Confirm password: ").unwrap(); + if ! password_confirm.eq(&password) { + println!("Password does not match!"); + exit(1); + } + + // 随机生成 色调 值 + let mut rng = rand::rng(); + let random_hue: i32 = rng.random_range(0..=360); + + // 建立账号信息并预填入信息 + new_info.setup_account_info(account_id.as_str(), password.as_str()); + new_info.set_nickname(account_id.as_str()); + new_info.set_customize_color_hsv(random_hue, 0.8, 0.8); + + // 新配置信息的文本 + let new_info_yaml = serde_yaml::to_string(&new_info); + + // 将信息写入文件系统 + let mut buffer = File::create(account_config_path).unwrap(); + buffer.write_all(new_info_yaml.unwrap().as_bytes()).unwrap(); + + println!("Account created."); + } + }, + + // 移除账号 + Remove(args) => { + if ! is_account_exist(&args.id) { + println!("Account not found!"); + } else { + // 账号 ID 和 配置路径 + let account_id = process_inputted_text(args.id); + let account_config_path = get_account_config_path(&account_id); + + // 删除文件 + remove_file(account_config_path).unwrap(); + + println!("Account removed."); + } + }, + + // 列出所有账号 + List(_args) => { + // 账号信息文件夹 + let folder_path = get_config_folder_path(); + + // 输出表格 + let mut info_table = Table::new(); + + // 表头 + info_table.add_row(row!["ACCOUNT_ID", "NICKNAME", "COLOR", "HASH"]); + + // 遍历目录下文件,将信息逐一填入表格 + for item in folder_path.read_dir().unwrap() { + if let Ok(path) = item { + // 文件名 + let file_name = path.file_name().into_string().unwrap(); + let ext = format!(".{}", EXTENSION_NAME); + + // 判断是否为指定后缀 + if file_name.contains(ext.as_str()) { + + // 去除后缀内容,截取为 ID + let id = file_name.replace(ext.as_str(), ""); + + // 读取并加载其中的玩家信息 + let file = File::open(get_account_config_path(&id)).unwrap(); + let reader = BufReader::new(file); + let info: PlayerInfo = serde_yaml::from_reader(reader).unwrap(); + + // 填入表格 + info_table.add_row(row![ + &id, // ACCOUNT_ID + info.customize.nickname, // NICKNAME + hsv_to_hex( // COLOR + info.customize.color_hue, + info.customize.color_saturation, + info.customize.color_value), + info.account.player_hash // HASH + ]); + } + } + } + println!("{}", info_table.to_string()) + }, + + // 自定义账号显示信息 + Customize(args) => { + + // 加载配置文件 + let file = File::open(get_account_config_path(&args.id)).unwrap(); + let reader = BufReader::new(file); + let mut info: PlayerInfo = serde_yaml::from_reader(reader).unwrap(); + + // HSV 参数 + if args.hsv.is_some() { + let hsv = args.hsv.unwrap(); + let hue = hsv[0].round().clamp(0.0, 360.0); + let sat = hsv[1].clamp(0.0, 1.0); + let val = hsv[2].clamp(0.0, 1.0); + + info.customize.color_hue = hue as i32; + info.customize.color_saturation = sat; + info.customize.color_value = val; + + println!("Set {}'s HSV color to: {}, {}, {}.", &args.id, hue, sat, val); + } + + // 昵称 参数 + if args.name.is_some() { + let name = args.name.unwrap(); + info.customize.nickname = name.clone(); + + println!("Set {}'s display name to: {}.", &args.id, name); + } + + // 写入配置文件 + let yaml_content = serde_yaml::to_string(&info); + let mut buffer = File::create(get_account_config_path(&args.id)).unwrap(); + buffer.write_all(yaml_content.unwrap().as_bytes()).unwrap(); + }, + } + }, + + // 连接到服务器 + Connect(args) => { + if ! is_account_exist(&args.id) { + println!("Player not found!"); + exit(1); + } + + // 加载配置文件 + let file = File::open(get_account_config_path(&args.id)).unwrap(); + let reader = BufReader::new(file); + let info: PlayerInfo = serde_yaml::from_reader(reader).unwrap(); + + // 从参数获得 Ip 地址 (或默认) + let addr : IpAddr; + match IpAddr::from_str(&args.target) { + Ok(result) => { addr = result; } + Err(_err) => { + addr = IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)); + } + } + + // 从参数获得端口地址 (或默认) + let port : u16 = if args.port.is_some() { args.port.unwrap() } else { DEFAULT_PORT } + .clamp(0, 65535); + + // 绑定目标地址 + let mut client = PadClient::bind_addr_with_port(addr, port); + + // 启动调试模式 ? + if args.debug { + println!("- DEBUG MODE -"); + client.enable_console(); + } + + // 写入玩家信息 + client.bind_player(info); + + // 连接 + client.connect(); + println!("Connected {} to {}:{}", args.id, addr.to_string(), port.to_string()); + } + } +} + +/// # 处理输入的文本 +/// +/// 将输入的文本进行初步处理,以适合文件名称显示 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | input | String | 输入原始文本 | +/// | -> | String | 处理后的结果 | +fn process_inputted_text(input: String) -> String { + // 截取前后文本,并转换为小写 + let s = input.trim().to_lowercase(); + let mut result = String::new(); + + // 处理其中的特殊符号,部分用于分割的符号需要转换为下划线 + for c in s.chars() { + match c { + '\n' | '_' => continue, + '-' | '.' | ',' | ' ' => result.push('_'), + _ => result.push(c), + } + } + + // 仅保留 ASCII 字符 + result.chars() + .filter(|&c| c.is_ascii_alphanumeric() || c == '_') + .collect() +} + +/// # 将 HSV 数值转换为 HEX 颜色码字符串 +/// +/// 在显示玩家信息时,因 HSV 不如 RGB 直观,便转换为 HEX 字符串 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | h | i32 | 色相值 (0 - 360) | +/// | s | f64 | 饱和度 (0 - 1) | +/// | v | f64 | 明亮度 (0 - 1) | +/// | -> | String | HEX 字符串 | +fn hsv_to_hex(h: i32, s: f64, v: f64) -> String { + let h = (h as f64).clamp(0.0, 360.0); + let s = s.clamp(0.0, 1.0); + let v = v.clamp(0.0, 1.0); + + let c = v * s; + let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs()); + let m = v - c; + + let (r, g, b) = match (h / 60.0) as usize { + 0 => (c, x, 0.0), + 1 => (x, c, 0.0), + 2 => (0.0, c, x), + 3 => (0.0, x, c), + 4 => (x, 0.0, c), + 5 => (c, 0.0, x), + _ => (0.0, 0.0, 0.0), + }; + + let r = ((r + m) * 255.0).round() as u8; + let g = ((g + m) * 255.0).round() as u8; + let b = ((b + m) * 255.0).round() as u8; + format!("#{:02X}{:02X}{:02X}", r, g, b) +} + +/// # 获得配置文件目录地址 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | PathBuf | 地址 | +fn get_config_folder_path() -> PathBuf { + current_dir().unwrap().join(".nogpadc") +} + +/// # 获得账户配置文件地址 +/// +/// 输入指定的账户ID,获得其配置文件的目录 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | id | &str | 账户 ID | +/// | -> | PathBuf | 地址 | +fn get_account_config_path(id: &str) -> PathBuf { + get_config_folder_path().join(format!("{}.{}", process_inputted_text(id.to_string()), EXTENSION_NAME)) +} + +/// # 判断账户是否存在 +/// +/// 输入指定的账户ID,获得其配置文件的目录 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | id | &str | 账户 ID | +/// | -> | bool | 是否存在 | +fn is_account_exist(id: &str) -> bool { + let id = process_inputted_text(id.to_string()); + let path = get_config_folder_path(); + let dir = path.as_path().read_dir().unwrap(); + let mut found = false; + for item in dir { + if let Ok(path) = item { + if path.file_name().eq(format!("{}.{}", id, EXTENSION_NAME).as_str()) { + found = true; + } + } + } + found +}
\ No newline at end of file diff --git a/console/src/bin/nogpads.rs b/console/src/bin/nogpads.rs new file mode 100644 index 0000000..95f5ec7 --- /dev/null +++ b/console/src/bin/nogpads.rs @@ -0,0 +1,257 @@ +use clap::{arg, Args, Parser, Subcommand}; +use nogamepads_core::pad_io::server::nogamepads_server::{PadServer}; +use nogamepads_core::DEFAULT_PORT; +use serde::{Deserialize, Serialize}; +use std::env::current_dir; +use std::fs::{create_dir, File}; +use std::io::{BufReader, Write}; +use std::net::{IpAddr, Ipv4Addr}; +use std::path::PathBuf; +use nogamepads_core::pad_data::game_profile::game_profile::GameProfile; + +/// NoGamePads Console - Server Cli +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct NoGamepadServerCli { + #[command(subcommand)] + command: Commands, +} + +/// 主要命令 +#[derive(Subcommand, Debug)] +enum Commands { + + // 服务端配置 + #[command(about = "Configure the server")] + Config(ConfigArgs), + + // 运行服务端 + #[command(about = "Run the server")] + Run(RunArgs) +} + +/// 服务端配置 参数 +#[derive(Args, Debug)] +struct ConfigArgs { + + // 绑定的端口号 + #[arg(short, long, help = "Server port (0 = Default)")] // <---- DEFAULT_PORT + port: Option<u16>, + + // 游戏名称 + #[arg(short ='n', long = "name")] + game_name: Option<String>, + + // 游戏描述 + #[arg(short = 'd', long = "description")] + game_description: Option<String>, + + // 游戏组织 + #[arg(short = 'o', long = "organization")] + organization: Option<String>, + + // 游戏版本 + #[arg(short = 'v', long = "version")] + version: Option<String>, + + // 工作室 & 游戏 主页 + #[arg(short = 'w', long = "website")] + website: Option<String>, + + // 交流邮箱 + #[arg(short = 'e', long = "email")] + email: Option<String> +} + +/// 运行服务端 参数 +#[derive(Args, Debug)] +struct RunArgs { + + // 调试模式 + #[arg(long)] + debug: bool, +} + +/// 本地存储的配置信息 +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct ServerConfig { + port: u16, + profile: GameProfile, +} + +impl Default for ServerConfig { + fn default() -> Self { + ServerConfig { + port: DEFAULT_PORT, + profile: GameProfile::default(), + } + } +} + +/// # 快速生成 更新服务端信息 的宏 +macro_rules! update_config { + ($config:expr, $args:expr, $($field:ident),+) => { + $( + if let Some(ref value) = $args.$field { + $config.profile.$field = value.clone(); + println!("Changed profile \"{}\" to \"{}\"", + stringify!($field), + value + ); + } + )+ + }; +} + +fn main () { + + // 命令行 + let cli = NoGamepadServerCli::parse(); + match cli.command { + + // 服务端配置 + Commands::Config(args) => { + // 读取服务端配置 + let mut config = read_config(); + + // 端口信息配置: + // 端口数值被限定在 0 - 65535,但是若端口参数为 0,则会被设置为默认端口 + if args.port.is_some() { + let port = args.port.unwrap_or(DEFAULT_PORT).clamp(0, 65535); + config.port = if port == 0 { DEFAULT_PORT } else { port }; + } + + // 其他信息配置 + update_config!( + config, args, + game_name, + game_description, + organization, + version, + website, + email + ); + + // 写入配置 + write_config(config); + }, + + // 运行服务端 + Commands::Run(args) => { + // 读取服务端配置 + let config = read_config(); + + // 根据调试选项启动服务端 + if args.debug { + println!("- DEBUG MODE -"); + println!("Server started!"); + PadServer::default() + .addr(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), config.port) + .put_profile(config.profile) + .enable_console() + .build() + .start_listening(); + } else { + println!("Server started!"); + PadServer::default() + .addr(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), config.port) + .put_profile(config.profile) + .build() + .start_listening(); + } + } + } +} + +/// # 获得配置文件目录地址 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | PathBuf | 地址 | +#[allow(dead_code)] +fn get_config_folder_path () -> PathBuf { + current_dir().unwrap().join(".nogpads") +} + +/// # 获得配置文件地址 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | PathBuf | 地址 | +#[allow(dead_code)] +fn get_config_file_path () -> PathBuf { + get_config_folder_path().join("config.yaml") +} + +/// # 获得布局文件地址 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | PathBuf | 地址 | +#[allow(dead_code)] +fn get_layout_file_path () -> PathBuf { + get_config_folder_path().join("layout.yaml") +} + +/// # 获得皮肤资源地址 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | PathBuf | 地址 | +#[allow(dead_code)] +fn get_assets_package_path () -> PathBuf { + get_config_folder_path().join("assets.zip") +} + +/// # 读取配置信息 +/// +/// 读取 服务端控制台 的配置信息 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | -> | ServerConfig | 配置信息 | +fn read_config () -> ServerConfig { + let config_folder_path = get_config_folder_path(); + let config_file_path = get_config_file_path(); + + if ! config_folder_path.exists() { + create_dir(&config_folder_path).unwrap(); + } + + if ! config_file_path.exists() { + let config = ServerConfig::default(); + let config_text = serde_yaml::to_string(&config).unwrap(); + File::create(&config_file_path).unwrap().write_all(config_text.as_bytes()).unwrap(); + config + } else { + let config_file = File::open(&config_file_path).unwrap(); + let config_reader = BufReader::new(config_file); + serde_yaml::from_reader(config_reader).unwrap() + } +} + +/// # 写入配置信息 +/// +/// 将 服务端控制台 的配置信息写入本地 +/// +/// ## 参数 - Parameters +/// +/// | Field | Type | Description | +/// | ------ | --------------------- | ----------- | +/// | config | ServerConfig | 配置信息 | +fn write_config (config: ServerConfig) { + let config_file_path = get_config_file_path(); + + let config_text = serde_yaml::to_string(&config).unwrap(); + File::create(config_file_path).unwrap().write_all(config_text.as_bytes()).unwrap(); +}
\ No newline at end of file diff --git a/core/Cargo.lock b/core/Cargo.lock new file mode 100644 index 0000000..21c26ea --- /dev/null +++ b/core/Cargo.lock @@ -0,0 +1,1151 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "clearscreen" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c41dc435a7b98e4608224bbf65282309f5403719df9113621b30f8b6f74e2f4" +dependencies = [ + "nix", + "terminfo", + "thiserror", + "which", + "windows-sys 0.59.0", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nogamepads-core" +version = "0.1.0" +dependencies = [ + "bincode", + "chrono", + "clap", + "clearscreen", + "env_logger", + "hex", + "log", + "serde", + "sha1", + "shell-words", + "tokio", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2611b99ab098a31bdc8be48b4f1a285ca0ced28bd5b4f23e45efa8c63b09efa5" +dependencies = [ + "once_cell", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..fade27a --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "nogamepads-core" + +homepage = "https://github.com/CatilGrass/NoGamepads" +repository = "https://github.com/CatilGrass/NoGamepads" +readme = "../README.md" + +version = "0.1.0" +edition = "2024" + +[dependencies] +nogamepads = { path = "../../NoGamepads" } +bincode = { version = "2.0.1", features = ["serde"]} +clearscreen = "4.0.1" +hex = "0.4.3" +tokio = { version = "1.45.0", features = ["full"] } +serde = { version = "1.0.219", features = ["derive"] } +clap = { version = "4.5.38", features = ["derive"] } +sha1 = "0.10.6" +log = "0.4.27"
\ No newline at end of file diff --git a/core/examples/start_client_console.rs b/core/examples/start_client_console.rs new file mode 100644 index 0000000..3d6653b --- /dev/null +++ b/core/examples/start_client_console.rs @@ -0,0 +1,24 @@ +use std::net::{IpAddr, Ipv4Addr}; +use nogamepads_core::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; +use nogamepads_core::pad_io::client::nogamepads_client::PadClient; + +const PASSWORD : &str = "password"; + +fn main() { + + // 构建玩家信息 + let mut player_info = PlayerInfo::new(); + player_info.setup_account_info("juliet", PASSWORD); + player_info.set_nickname("Juliet_Smile"); + player_info.set_customize_color_hsv(320, 0.5, 1.0); // PINK + + // 构建客户端 + let mut client = PadClient::bind_addr( + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)) + ); + client.bind_player(player_info); // 绑定玩家 + client.enable_console(); // 启用控制台 + + // 连接至目标地址 + client.connect(); +}
\ No newline at end of file diff --git a/core/examples/start_server_console.rs b/core/examples/start_server_console.rs new file mode 100644 index 0000000..e643390 --- /dev/null +++ b/core/examples/start_server_console.rs @@ -0,0 +1,30 @@ +use std::net::{IpAddr, Ipv4Addr}; +use nogamepads_core::DEFAULT_PORT; +use nogamepads_core::pad_data::game_profile::game_profile::GameProfile; +use nogamepads_core::pad_io::server::nogamepads_server::PadServer; + +fn main() { + + // 简易构建服务端 + // let server = PadServer::build_simple(); + // server.start_listening_debug() + + // 构建服务端 + let server = PadServer::default() + .addr( + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + DEFAULT_PORT + ) + .put_profile( + GameProfile::default() + .game_name("My Multiplayer Game") + .game_description("My Game Description") + .version("0.1 alpha") + .to_owned() + ) + .enable_console() + .build(); + + // 运行服务端 + server.start_listening(); +}
\ No newline at end of file diff --git a/core/examples/tetris_multicmd_game.rs b/core/examples/tetris_multicmd_game.rs new file mode 100644 index 0000000..cf74f49 --- /dev/null +++ b/core/examples/tetris_multicmd_game.rs @@ -0,0 +1,3 @@ +fn main() { + +}
\ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..f70fc4e --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,10 @@ +use bincode::config; +use bincode::config::Configuration; + +pub mod pad_io; +pub mod pad_data; + +pub const DEFAULT_PORT : u16 = 5989; +pub const BINCODE_CONVERT_FAILED : Vec<u8> = Vec::new(); +pub const BINCODE_CONFIG : Configuration = config::standard(); + diff --git a/core/src/pad_data/game_layout.rs b/core/src/pad_data/game_layout.rs new file mode 100644 index 0000000..62afbcc --- /dev/null +++ b/core/src/pad_data/game_layout.rs @@ -0,0 +1,9 @@ +pub mod game_layout { + use bincode::{Decode, Encode}; + use serde::{Deserialize, Serialize}; + + #[derive(Encode, Decode, Serialize, Deserialize, PartialEq, Debug)] + pub struct GameLayout { + + } +}
\ No newline at end of file diff --git a/core/src/pad_data/game_profile.rs b/core/src/pad_data/game_profile.rs new file mode 100644 index 0000000..8d6e279 --- /dev/null +++ b/core/src/pad_data/game_profile.rs @@ -0,0 +1,99 @@ +pub mod game_profile { + use std::fmt::Display; + use bincode::{Decode, Encode}; + use serde::{Deserialize, Serialize}; + + #[derive(Encode, Decode, Serialize, Deserialize, PartialEq, Debug)] + pub struct GameProfile { + + // 游戏名称 + pub game_name: String, + + // 游戏描述 + pub game_description: String, + + // 游戏组织 + pub organization: String, + + // 游戏版本 + pub version: String, + + // 工作室 & 游戏 主页 + pub website: String, + + // 交流邮箱 + pub email: String + } + + impl Default for GameProfile { + fn default() -> Self { + GameProfile { + game_name: "Unnamed Game".to_string(), + game_description: "".to_string(), + organization: "".to_string(), + version: "0.1".to_string(), + website: "".to_string(), + email: "".to_string() + } + } + } + + impl Clone for GameProfile { + fn clone(&self) -> Self { + GameProfile { + game_name: self.game_name.clone(), + game_description: self.game_description.clone(), + organization: self.organization.clone(), + version: self.version.clone(), + website: self.website.clone(), + email: self.email.clone() + } + } + } + + impl Display for GameProfile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut string = String::new(); + string += format!("Game Name: {}\n", self.game_name).as_str(); + if !self.game_description.eq("") { string += format!("Description: {}\n", self.game_description).as_str(); } + if !self.organization.eq("") { string += format!("Org: {}\n", self.organization).as_str(); } + if !self.website.eq("") { string += format!("- Web: {}\n", self.website).as_str(); } + if !self.version.eq("") { string += format!("Version: {}\n", self.version).as_str(); } + if !self.email.eq("") { string += format!("- E-mail: {}\n", self.email).as_str(); } + + write!(f, "{}", string) + } + } + + impl GameProfile { + pub fn game_name(&mut self, game_name: &str) -> &mut GameProfile { + self.game_name = game_name.to_string(); + self + } + + pub fn game_description(&mut self, game_description: &str) -> &mut GameProfile { + self.game_description = game_description.to_string(); + self + } + + pub fn organization(&mut self, organization: &str) -> &mut GameProfile { + self.organization = organization.to_string(); + self + } + + pub fn version(&mut self, version: &str) -> &mut GameProfile { + self.version = version.to_string(); + self + } + + pub fn website(&mut self, website: &str) -> &mut GameProfile { + self.website = website.to_string(); + self + } + + pub fn email(&mut self, email: &str) -> &mut GameProfile { + self.email = email.to_string(); + self + } + } +}
\ No newline at end of file diff --git a/core/src/pad_data/mod.rs b/core/src/pad_data/mod.rs new file mode 100644 index 0000000..8ff024a --- /dev/null +++ b/core/src/pad_data/mod.rs @@ -0,0 +1,4 @@ +pub mod pad_messages; +pub mod pad_player_info; +pub mod game_profile; +mod game_layout;
\ No newline at end of file diff --git a/core/src/pad_data/pad_messages.rs b/core/src/pad_data/pad_messages.rs new file mode 100644 index 0000000..f081e5d --- /dev/null +++ b/core/src/pad_data/pad_messages.rs @@ -0,0 +1,176 @@ +pub mod nogamepads_messages { + use bincode::{Decode, Encode}; + use crate::pad_data::game_profile::game_profile::GameProfile; + use crate::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum ControlMessage { + + Msg(String), + + Pressed(u8), + + Released(u8), + + Axis(u8, f64), + + Dir(u8, (f64, f64)), + + Exit, + + Err + } + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum GameMessage { + + SkinEventTrigger(u8), + + DisableKey(u8), + + EnableKey(u8), + + Leave(LeaveReason), + + Err + } + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum LeaveReason { + + GameOver, + + ServerClosed, + + YouAreKicked, + + YouAreBanned + } + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum ConnectionMessage { + + Connection(PlayerInfo), + + RequestProfile, + + RequestLayoutConfigure, + + RequestSkinPackage, + + Ready, + + Err + } + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum ConnectionCallbackMessage { + + Profile(GameProfile), + + Deny(ConnectionErrorType), + + Fail(ConnectionErrorType), + + Ok, + + Welcome, + + Err + } + + #[derive(Encode, Decode, PartialEq, Debug, Clone)] + pub enum ConnectionErrorType { + + ContainSamePlayer, + + PlayerBanned, + + Timeout, + + GameLocked, + + WhatTheHell + } +} + +pub mod nogamepads_message_encoder { + use bincode::{Decode, Encode}; + use crate::{BINCODE_CONFIG, BINCODE_CONVERT_FAILED}; + use crate::pad_data::pad_messages::nogamepads_messages::{ConnectionCallbackMessage, ConnectionMessage, ControlMessage, GameMessage}; + + pub trait NgpdMessageEncoder<Message: Encode + Decode<()>> { + fn err_result_decode () -> Message; + fn err_result_encode () -> Vec<u8> { + BINCODE_CONVERT_FAILED + } + + fn en(&self) -> Vec<u8> where Self : Encode { + bincode::encode_to_vec(self, BINCODE_CONFIG) + .unwrap_or_else(|_| Self::err_result_encode()) + } + + fn de(encoded : Vec<u8>) -> Message { + match bincode::decode_from_slice(&encoded[..], BINCODE_CONFIG) { + Ok((decoded, _)) => decoded, + Err(_) => Self::err_result_decode() + } + } + } + + impl NgpdMessageEncoder<ControlMessage> for ControlMessage { + fn err_result_decode() -> ControlMessage { + ControlMessage::Err + } + } + + impl NgpdMessageEncoder<GameMessage> for GameMessage { + fn err_result_decode() -> GameMessage { + GameMessage::Err + } + } + + impl NgpdMessageEncoder<ConnectionMessage> for ConnectionMessage { + fn err_result_decode() -> ConnectionMessage { + ConnectionMessage::Err + } + } + + impl NgpdMessageEncoder<ConnectionCallbackMessage> for ConnectionCallbackMessage { + fn err_result_decode() -> ConnectionCallbackMessage { + ConnectionCallbackMessage::Err + } + } +} + +pub mod nogamepads_message_transfer { + use bincode::{Decode, Encode}; + use log::error; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use tokio::net::TcpStream; + use crate::pad_data::pad_messages::nogamepads_message_encoder::NgpdMessageEncoder; + + pub async fn send_msg <Message>(stream: &mut TcpStream, msg: impl NgpdMessageEncoder<Message> + Decode<()> + Encode) + where Message: NgpdMessageEncoder<Message> + Decode<()> + Encode { + match stream.write_all(NgpdMessageEncoder::en(&msg).as_slice()).await { + Ok(_) => {} + Err(_) => { + error!("Failed to send message."); + } + } + } + + pub async fn read_msg<Message>(buffer: &mut [u8], stream: &mut TcpStream) -> Message + where Message: NgpdMessageEncoder<Message> + Decode<()> + Encode { + match stream.read(buffer).await { + Ok(read) => { + let received = &buffer[..read]; + <Message as NgpdMessageEncoder<Message>>::de(Vec::from(received)) + } + Err(err) => { + error!("Error reading from socket: {}", err); + <Message as NgpdMessageEncoder<Message>>::err_result_decode() + } + } + } +}
\ No newline at end of file diff --git a/core/src/pad_data/pad_player_info.rs b/core/src/pad_data/pad_player_info.rs new file mode 100644 index 0000000..ab0f6fd --- /dev/null +++ b/core/src/pad_data/pad_player_info.rs @@ -0,0 +1,126 @@ +pub mod nogamepads_player_info { + + use bincode::{Decode, Encode}; + use hex::encode; + use sha1::{Digest, Sha1}; + use serde::{Deserialize, Serialize}; + + pub const ACCOUNT_HASH_SALT : &str = "Mr.Weicao"; + + #[derive(Encode, Decode, + Serialize, Deserialize, + PartialEq, Debug)] + pub struct PlayerInfo { + pub account: PlayerAccountInfo, + pub customize: PlayerCustomizeInfo + } + + #[derive(Encode, Decode, + Serialize, Deserialize, + PartialEq, Debug)] + pub struct PlayerAccountInfo { + pub id: String, + pub player_hash: String + } + + #[derive(Encode, Decode, + Serialize, Deserialize, + PartialEq, Debug)] + pub struct PlayerCustomizeInfo { + pub nickname: String, + + pub color_hue: i32, // 0 - 360 + pub color_saturation: f64, // 0 - 1 + pub color_value: f64 // 0 - 1 + } + + impl PlayerInfo { + + pub fn new() -> PlayerInfo { + PlayerInfo { + customize: PlayerCustomizeInfo::default(), + account: PlayerAccountInfo::default() + } + } + + pub fn set_nickname(&mut self, name: &str) -> &mut PlayerInfo { + self.customize.nickname = String::from(name); + self + } + + pub fn set_customize_color_hue(&mut self, mut hue: i32) -> &mut PlayerInfo { + hue = hue.clamp(0, 360); + self.customize.color_hue = hue; + self + } + + pub fn set_customize_color_hsv(&mut self, mut hue: i32, mut saturation: f64, mut value: f64) -> &mut PlayerInfo { + hue = hue.clamp(0, 360); + saturation = saturation.clamp(0.0, 1.0); + value = value.clamp(0.0, 1.0); + + self.customize.color_hue = hue; + self.customize.color_saturation = saturation; + self.customize.color_value = value; + self + } + + pub fn setup_account_info(&mut self, id: &str, password: &str) -> &mut PlayerInfo { + + let combined = format!("{}{}{}", id, password, ACCOUNT_HASH_SALT); + let mut hasher = Sha1::new(); + hasher.update(combined); + let result = hasher.finalize(); + + self.account.id = String::from(id); + self.account.player_hash = encode(&result[..]); + self + } + } + + impl Clone for PlayerInfo { + fn clone(&self) -> PlayerInfo { + PlayerInfo { + account: PlayerAccountInfo { + id: String::from(self.account.id.clone()), + player_hash: String::from(self.account.player_hash.clone()) + }, + customize: PlayerCustomizeInfo { + nickname: self.customize.nickname.clone(), + color_hue: self.customize.color_hue.clone(), + color_saturation: self.customize.color_saturation.clone(), + color_value: self.customize.color_value.clone() + } + } + } + } + + impl Default for PlayerCustomizeInfo { + fn default() -> Self { + PlayerCustomizeInfo { + nickname: String::from("unnamed"), + + color_hue: 120, + color_saturation: 1.0, + color_value: 1.0 + } + } + } + + impl Default for PlayerAccountInfo { + fn default() -> Self { + PlayerAccountInfo { + id: String::from("empty"), + player_hash: String::from("") + } + } + } +} + +#[cfg(test)] +mod player_info_test { + #[test] + fn test_player_info_setup() { + + } +}
\ No newline at end of file diff --git a/core/src/pad_io/client.rs b/core/src/pad_io/client.rs new file mode 100644 index 0000000..2969742 --- /dev/null +++ b/core/src/pad_io/client.rs @@ -0,0 +1,448 @@ +pub mod nogamepads_client { + use crate::pad_data::pad_messages::nogamepads_message_transfer::{read_msg, send_msg}; + use crate::pad_data::pad_messages::nogamepads_messages::{ConnectionCallbackMessage, ConnectionErrorType, ConnectionMessage, ControlMessage, GameMessage, LeaveReason}; + use crate::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; + use log::{error, info}; + use std::net::{IpAddr, Ipv4Addr}; + use std::process::exit; + use std::sync::atomic::AtomicBool; + use std::sync::atomic::Ordering::SeqCst; + use std::sync::{Arc, Mutex}; + use std::time::Duration; + use clap::CommandFactory; + use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf}; + use tokio::net::TcpStream; + use tokio::{io, spawn}; + use nogamepads::debug_console::debug_console::read_cli; + use nogamepads::logger::logger_build; + use crate::pad_io::client_debug_cli::{process_debug_cmd, Pcc}; + use crate::DEFAULT_PORT; + use crate::pad_data::game_profile::game_profile::GameProfile; + use crate::pad_data::pad_messages::nogamepads_message_encoder::NgpdMessageEncoder; + + type WriteList = Arc<Mutex<Vec<ControlMessage>>>; + type ReadList = Arc<Mutex<Vec<GameMessage>>>; + + pub struct PadClient { + + // --- 主要参数 --- + + // 目标地址 + target_address: IpAddr, + + // 目标端口 + #[allow(dead_code)] + target_port: u16, + + // 绑定的玩家 + bind_player: PlayerInfo, + + // 调试模式 + enable_console: bool, + + // 保持安静,不初始化 env_logger + quiet: bool, + + // --- 运行时参数 --- + + // 发送信息列表 + write_list: WriteList, + + // 读取信息列表 + read_list: ReadList, + + // 是否退出 + exit: AtomicBool, + } + + impl Default for PadClient { + fn default() -> Self { + PadClient { + enable_console: false, + target_address: IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), + target_port: DEFAULT_PORT, + bind_player: PlayerInfo::new(), + quiet: false, + + write_list: WriteList::default(), + read_list: ReadList::default(), + exit: AtomicBool::new(false) + } + } + } + + // 客户端构建部分 + impl PadClient { + + pub fn bind_addr(address: IpAddr) -> PadClient { + PadClient { + target_address: address, + ..PadClient::default() + } + } + + pub fn bind_addr_with_port(address: IpAddr, port: u16) -> PadClient { + PadClient { + target_address: address, + target_port: port, + ..PadClient::default() + } + } + + pub fn enable_console(&mut self) { + self.enable_console = true; + } + + pub fn quiet(&mut self) -> &mut PadClient { + self.quiet = true; + self + } + + pub fn bind_player(&mut self, player: PlayerInfo) { + self.bind_player = player; + } + } + + // 客户端消息管理 + impl PadClient { + + + pub fn put_msg(&self, msg: ControlMessage) { + { + let mut guard = self.write_list.lock().unwrap(); + guard.push(msg); + } + } + + pub fn pop_a_msg(&self) -> Option<GameMessage> { + { + let mut guard = self.read_list.lock().unwrap(); + if ! guard.is_empty() { + Some(guard.remove(0)) + } else { + None + } + } + } + + pub fn pop_msg_or(&self, or: GameMessage) -> GameMessage { + self.pop_a_msg().unwrap_or(or) + } + + pub fn list_received(&self) -> Vec<GameMessage> { + match self.read_list.lock() { + Ok(guard) => { + guard.to_vec() + } + Err(_) => { Vec::new() } + } + } + } + + // 客户端状态控制 + impl PadClient { + + pub fn connect(self) { + + self.exit.store(false, SeqCst); + + // 构建 Logger + if !self.quiet { + logger_build(); + } + + // 调试模式 + let debug = self.enable_console; + + // 客户端对象的 Arc + let arc_client = Arc::new(self); + + // 部署环境 + let runtime = tokio::runtime::Builder::new_multi_thread() + .thread_name("nogpad-pad_io") + .thread_stack_size(32 * 1024 * 1024) + .enable_time() + .enable_io() + .build() + .unwrap(); + + info!("Starting \"NoGamepads Client\"."); + + // 入口 + let entry = async move { + let main_thread = spawn({ + let client = Arc::clone(&arc_client); + async move { + Self::main_client_thread(client).await + } + }); + + let background_thread = spawn({ + let client = Arc::clone(&arc_client); + async move { + Self::background_thread(client).await + } + }); + + if debug { + let debug_cli = spawn({ + let client = Arc::clone(&arc_client); + async move { + Self::process_debug_cli(client).await + } + }); + let _ = tokio::join!(debug_cli, main_thread, background_thread); + } else { + let _ = tokio::join!(main_thread, background_thread); + } + }; + + // 阻塞运行 + runtime.block_on(entry); + } + + pub fn exit_server(&self) { + self.exit.store(true, SeqCst); + } + + async fn main_client_thread(self: Arc<Self>) { + let mut buffer : [u8; 1024] = [0; 1024]; + let addr_str = format!("{}:{}", self.target_address.to_string(), DEFAULT_PORT); + + info!("Connected to {}", &addr_str); + + // 下载服务端配置文件 + { + info!("Check: Downloaded game profile."); + let profile = self.check_server_profile(&mut buffer, addr_str.clone()).await; + if profile.is_some() { + info!("Success: Downloaded."); + let profile = profile.unwrap_or(GameProfile::default()); + for line in profile.to_string().split('\n') { + info!("{}", line); + } + } + else { + error!("Failed: Can't download profile!"); + self.exit_server(); + } + } + + // 尝试加入服务端,并建立长连接 + { + if !self.try_join_game(&mut buffer, addr_str.clone()).await { + error!("Failed: Can't join the game!"); + self.exit_server(); + return; + } + } + } + + async fn check_server_profile (self: &Arc<Self>, buffer: &mut [u8], addr_str: String) -> Option<GameProfile> { + match TcpStream::connect(&addr_str).await { + Ok(mut stream) => { + send_msg(&mut stream, ConnectionMessage::RequestProfile).await; + let callback : ConnectionCallbackMessage = read_msg(buffer, &mut stream).await; + match callback { + ConnectionCallbackMessage::Profile(profile) => { + Some(profile) + } + ConnectionCallbackMessage::Deny(err_type) => { + error!("Request failed: Server denied your request! ({:?})", err_type); + None + } + ConnectionCallbackMessage::Err => { + error!("Connection failed: Can't connect to server!"); + None + } + _ => { None } + } + } + Err(_err) => { + None + } + } + } + + async fn try_join_game(self: &Arc<Self>, buffer: &mut [u8], addr_str: String) -> bool { + + match TcpStream::connect(&addr_str).await { + Ok(mut stream) => { + + // 发送连接请求 + let info = self.bind_player.clone(); + send_msg(&mut stream, ConnectionMessage::Connection(info)).await; + + // 读取回调 + let callback : ConnectionCallbackMessage = read_msg(buffer, &mut stream).await; + match callback { + ConnectionCallbackMessage::Deny(error) => { + match error { + ConnectionErrorType::ContainSamePlayer => { + error!("Connection failed: Contains same player!"); + false + } + ConnectionErrorType::PlayerBanned => { + error!("Connection failed: You are banned!"); + false + } + ConnectionErrorType::Timeout => { + error!("Connection failed: Timeout!"); + false + } + ConnectionErrorType::GameLocked => { + error!("Connection failed: Game was locked!"); + false + } + _ => { false } + } + } + ConnectionCallbackMessage::Ok => { + + // 服务端检查完毕,发送 Ready 以示加入游戏 + send_msg(&mut stream, ConnectionMessage::Ready).await; + let callback : ConnectionCallbackMessage = read_msg(buffer, &mut stream).await; + match callback { + ConnectionCallbackMessage::Welcome => { + info!("Welcome!"); + Self::long_connection(Arc::clone(&self), stream).await; + } + ConnectionCallbackMessage::Deny(_error) => { + error!("Request failed: Server denied your request",); + } + _ => {} + } + true + } + _ => { false } + } + } + Err(err) => { + error!("Failed to connect to server: {}", err); + false + } + } + } + + async fn long_connection(self: Arc<Self>, stream: TcpStream) { + let (reader, writer) = io::split(stream); + spawn(Self::read_task(Arc::clone(&self), reader)); + spawn(Self::write_task(Arc::clone(&self), writer)); + } + + async fn read_task(self: Arc<Self>, mut reader: ReadHalf<TcpStream>) { + let mut buf = [0u8; 1024]; + loop { + match reader.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + let msg = GameMessage::de(buf[0..n].to_vec()); + { + match self.read_list.lock() { + Ok(mut guard) => { + match &msg { + GameMessage::Leave(reason) => { + match reason { + LeaveReason::GameOver => { + info!("Leave Game: Game Over!"); + self.exit_server(); + } + LeaveReason::ServerClosed => { + info!("Leave Game: Server closed!"); + self.exit_server(); + } + LeaveReason::YouAreKicked => { + error!("Kick Game: You are kicked!"); + self.exit_server(); + } + LeaveReason::YouAreBanned => { + error!("Kick Game: You are banned!"); + self.exit_server(); + } + } + } + _ => { + info!("{:?}", &msg); + guard.push(msg); + } + } + } + Err(_) => {} + } + } + } + Err(e) => { + error!("Error reading from stream: {}", e); + self.exit_server(); + break; + } + } + } + } + + async fn write_task(self: Arc<Self>, mut writer: WriteHalf<TcpStream>) { + loop { + let msg : Option<ControlMessage>; + { + let lock = self.write_list.lock(); + match lock { + Ok(mut guard) => { + if ! guard.is_empty() { + msg = Some(guard.remove(0)); + } + else { msg = None; } + } + Err(_) => { + msg = None; + } + } + } + if msg.is_some() { + let msg = msg.unwrap(); + match &writer.write_all(NgpdMessageEncoder::en(&msg).as_slice()).await { + Ok(_) => { + info!("Sent {:?}", msg); + } + Err(_error) => { + error!("Sent {:?} failed!", msg); + } + } + } + } + } + + async fn background_thread(self: Arc<Self>) { + loop { + // 退出程序的监听 + if self.exit.load(SeqCst) { + tokio::time::sleep(Duration::from_secs(1)).await; + info!("Main thread exited."); + exit(0); + } + } + } + + async fn process_debug_cli(self: Arc<Self>) { + loop { + if self.exit.load(SeqCst) { + info!("Debug console exited"); + break + } + tokio::time::sleep(Duration::from_secs_f64(0.2)).await; + let option: Option<Pcc> = read_cli( + format!("CLIENT {}/{}> ", + self.target_address.to_string(), + self.bind_player.account.id).as_str(), + "pcc".to_string(), + Pcc::command() + ).await; + match option { + None => {} + Some(cmd) => { + process_debug_cmd(cmd, Arc::clone(&self)); + } + } + } + } + } +}
\ No newline at end of file diff --git a/core/src/pad_io/client_debug_cli.rs b/core/src/pad_io/client_debug_cli.rs new file mode 100644 index 0000000..c95fb94 --- /dev/null +++ b/core/src/pad_io/client_debug_cli.rs @@ -0,0 +1,91 @@ +use crate::pad_io::client::nogamepads_client::PadClient; +use crate::pad_data::pad_messages::nogamepads_messages::{ControlMessage, GameMessage}; +use clap::{Args, Parser, Subcommand}; +use std::sync::Arc; + +/// NoGamePads Client - Cli +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Pcc { + #[command(subcommand)] + command: Commands, +} + +/// 主要命令 +#[derive(Subcommand, Debug)] +enum Commands { + + // 清屏 + #[command(about = "Clean the screen")] + Clear, + + // 断开当前连接 + #[command(about = "Exit from server")] + Exit, + + // 检查收到的消息 + #[command(about = "Check received")] + Received(ReceivedArgs), + + // 取出一条消息 + #[command(about = "Pop a message")] + Pop(PopArgs), + + // 发送消息 + #[command(about = "Send Message")] + Msg(MsgArgs), +} + +#[derive(Args, Debug)] +struct ReceivedArgs { + + #[arg(long)] + list: bool +} + +/// 发送消息 参数 +#[derive(Args, Debug)] +struct MsgArgs { + + // 消息内容 + #[arg(value_name = "CONTENT")] + message: String, +} + +#[derive(Args, Debug)] +struct PopArgs { } + +pub fn process_debug_cmd (cmd: Pcc, client: Arc<PadClient>) { + match cmd.command { + Commands::Clear => { + clearscreen::clear().expect("Failed to clear screen"); + } + + Commands::Exit => { + client.exit_server(); + } + + Commands::Received(args) => { + if args.list { + for msg in client.list_received() { + println!("{:?}", msg); + } + } else { + println!("Total {} messsage(s)!", client.list_received().iter().count()); + } + } + + Commands::Pop(_args) => { + println!("{:?}", client.pop_msg_or(GameMessage::Err)); + } + + Commands::Msg(args) => { + client.put_msg(ControlMessage::Msg(args.message)); + } + } +} + +#[allow(dead_code)] +fn put_to_list(client: Arc<PadClient>, message: ControlMessage) { + client.put_msg(message); +}
\ No newline at end of file diff --git a/core/src/pad_io/mod.rs b/core/src/pad_io/mod.rs new file mode 100644 index 0000000..ae10f2d --- /dev/null +++ b/core/src/pad_io/mod.rs @@ -0,0 +1,5 @@ +pub mod client; +pub mod client_debug_cli; + +pub mod server; +pub mod server_debug_cli;
\ No newline at end of file diff --git a/core/src/pad_io/server.rs b/core/src/pad_io/server.rs new file mode 100644 index 0000000..c530753 --- /dev/null +++ b/core/src/pad_io/server.rs @@ -0,0 +1,646 @@ +pub mod nogamepads_server { + use std::collections::{HashMap, VecDeque}; + use crate::pad_data::pad_messages::nogamepads_message_encoder::NgpdMessageEncoder; + use crate::pad_data::pad_messages::nogamepads_message_transfer::{read_msg, send_msg}; + use crate::pad_data::pad_messages::nogamepads_messages::{ConnectionCallbackMessage, ConnectionMessage, ControlMessage, GameMessage, LeaveReason}; + use log::{error, info, warn}; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + use std::process::exit; + use std::sync::atomic::AtomicBool; + use std::sync::atomic::Ordering::SeqCst; + use std::sync::{Arc, Mutex, MutexGuard, PoisonError}; + use std::time::Duration; + use clap::CommandFactory; + use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf}; + use tokio::net::{TcpListener, TcpStream}; + use tokio::{io, spawn}; + use tokio::runtime::Runtime; + use nogamepads::debug_console::debug_console::read_cli; + use nogamepads::logger::logger_build; + use crate::DEFAULT_PORT; + use crate::pad_data::game_profile::game_profile::GameProfile; + use crate::pad_data::pad_messages::nogamepads_messages::ConnectionErrorType::{ContainSamePlayer, GameLocked, PlayerBanned, WhatTheHell}; + use crate::pad_data::pad_messages::nogamepads_messages::GameMessage::Leave; + use crate::pad_data::pad_messages::nogamepads_messages::LeaveReason::ServerClosed; + use crate::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; + use crate::pad_io::server_debug_cli::{process_debug_cmd, Psc}; + + type PlayerMap = Arc<Mutex<HashMap<String, PlayerInfo>>>; + type WriteList = Arc<Mutex<HashMap<String, VecDeque<GameMessage>>>>; + type ReadList = Arc<Mutex<HashMap<String, VecDeque<ControlMessage>>>>; + + pub struct PadServer { + + // --- 主要参数 --- + + // 本地监听地址 + address: IpAddr, + + // 游戏信息 + game_profile: GameProfile, + + // 绑定端口 + port: u16, + + // 调试模式 + enable_console: bool, + + // 保持安静,不初始化 env_logger + quiet: bool, + + // --- 运行时参数 --- + + // 发送信息列表 + write_list: WriteList, + + // 读取信息列表 + read_list: ReadList, + + // 在线玩家 + online_players: PlayerMap, + + // 被封禁的玩家 + banned_players: PlayerMap, + + // 是否锁定该游戏:禁止后续玩家加入 + game_locked: AtomicBool, + + // 是否停止服务器 + stop: AtomicBool, + } + + impl Clone for PadServer { + fn clone(&self) -> Self { + PadServer { + address: self.address.clone(), + game_profile: self.game_profile.clone(), + port: self.port.clone(), + enable_console: self.enable_console, + quiet: self.quiet, + + write_list: self.write_list.clone(), + read_list: self.read_list.clone(), + online_players: self.online_players.clone(), + banned_players: self.banned_players.clone(), + game_locked: AtomicBool::new((&self.game_locked.load(SeqCst)).clone()), + stop: AtomicBool::new((&self.stop.load(SeqCst)).clone()), + } + } + } + + impl Default for PadServer { + fn default() -> Self { + PadServer { + address: IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), + game_profile: GameProfile::default(), + port: DEFAULT_PORT, + enable_console: false, + quiet: false, + + write_list: WriteList::default(), + read_list: ReadList::default(), + online_players: PlayerMap::default(), + banned_players: PlayerMap::default(), + game_locked: AtomicBool::new(false), + stop: AtomicBool::new(false), + } + } + } + + // 服务端构建部分 + impl PadServer { + + pub fn build_simple() -> Arc<PadServer> { + Arc::new(Self::default() + .addr(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), DEFAULT_PORT) + .put_profile(GameProfile::default()).to_owned()) + } + + pub fn addr(&mut self, ip_addr: IpAddr, port: u16) -> &mut PadServer { + self.ip_addr(ip_addr).port(port) + } + + pub fn socket_addr(&mut self, socket_addr: SocketAddr) -> &mut PadServer { + self.ip_addr(socket_addr.ip()).port(socket_addr.port()) + } + + pub fn port(&mut self, port: u16) -> &mut PadServer { + self.port = port; + self + } + + pub fn ip_addr(&mut self, ip_addr: IpAddr) -> &mut PadServer { + self.address = ip_addr; + self + } + + pub fn put_profile(&mut self, profile: GameProfile) -> &mut PadServer { + self.game_profile = profile; + self + } + + pub fn enable_console(&mut self) -> &mut PadServer { + self.enable_console = true; + self + } + + pub fn quiet(&mut self) -> &mut PadServer { + self.quiet = true; + self + } + + pub fn build(&self) -> Arc<PadServer> { + Arc::new(self.clone()) + } + + } + + // 服务端消息管理 + impl PadServer { + + pub fn put_msg_to(&self, msg: GameMessage, player: &PlayerInfo) { + match self.write_list.lock() { + Ok(mut guard) => { + let hash = &player.account.player_hash.clone(); + if ! guard.contains_key(hash.as_str()) { + guard.entry(player.account.player_hash.clone()) + .or_insert_with(VecDeque::new) + .push_back(msg); + } + } + Err(_) => { + error!("Cannot lock \"{:?}\" in write_list", player.account.player_hash); + } + } + } + + pub fn put_msg_to_all(&self, msg: &GameMessage) { + match self.list_players() { + Ok(list) => { + for player in list { + self.put_msg_to(msg.clone(), &player); + } + } + Err(_) => { + error!("Cannot put GameMessage with no players."); + } + } + } + + pub fn pop_a_msg(&self, player: &PlayerInfo) -> Option<ControlMessage> { + match self.read_list.lock() { + Ok(mut guard) => { + match guard.get_mut(&player.account.player_hash) { + None => { None } + Some(queue) => { + if ! queue.is_empty() { + queue.pop_front() + } else { + guard.remove(&player.account.player_hash); + None + } + } + } + } + Err(_) => { + error!("Cannot lock \"{:?}\" in read_list", player.account.player_hash); + None + } + } + } + + pub fn pop_msg_or(&self, player: &PlayerInfo, or: ControlMessage) -> ControlMessage { + self.pop_a_msg(player).unwrap_or(or) + } + + pub fn list_received(&self, player: &PlayerInfo) -> Vec<ControlMessage> { + match self.read_list.lock() { + Ok(guard) => { + match guard.get_key_value(player.account.player_hash.as_str()) { + None => { Vec::new() } + Some(result) => { + Self::convert_deque_to_vec(result.1) + } + } + } + Err(_) => { Vec::new() } + } + } + } + + // 服务端玩家管理 + impl PadServer { + + pub fn is_player_online (&self, player: &PlayerInfo) -> bool { + let guard = self.online_players.lock().unwrap(); + guard.contains_key(&player.account.player_hash) + } + + pub fn set_player_online (&self, player: &PlayerInfo, online: bool) { + let online_current = self.is_player_online(player); + if online_current && !online { + let mut guard = self.online_players.lock().unwrap(); + guard.remove(&player.account.player_hash); + info!("{} is OFFLINE!", player.account.id); + } else if !online_current && online { + let mut guard = self.online_players.lock().unwrap(); + guard.insert(player.account.player_hash.clone(), player.clone()); + info!("{} is ONLINE!", player.account.id); + } + } + + pub fn is_player_banned (&self, player: &PlayerInfo) -> bool { + let guard = self.banned_players.lock().unwrap(); + guard.contains_key(&player.account.player_hash) + } + + pub fn kick_player(&self, player: &PlayerInfo) { + if self.is_player_online(player) { + self.put_msg_to(Leave(LeaveReason::YouAreKicked), player); + } + } + + pub fn ban_player(&self, player: &PlayerInfo) { + self.set_player_banned(player, true); + if self.is_player_online(player) { + self.put_msg_to(Leave(LeaveReason::YouAreBanned), player); + } + } + + pub fn pardon_player(&self, player: &PlayerInfo) { + self.set_player_banned(player, false); + } + + fn set_player_banned (&self, player: &PlayerInfo, banned: bool) { + let banned_current = self.is_player_banned(player); + if banned_current && !banned { + let mut guard = self.banned_players.lock().unwrap(); + guard.remove(&player.account.player_hash); + info!("Pardoned player {}", player.account.id); + } else if !banned_current && banned { + let mut guard = self.banned_players.lock().unwrap(); + guard.insert(player.account.player_hash.clone(), player.clone()); + info!("Banned player {}!", player.account.id); + } + } + + pub fn list_players(&self) -> Result<Vec<PlayerInfo>, PoisonError<MutexGuard<HashMap<String, PlayerInfo>>>> { + match self.online_players.lock() { + Ok(guard) => { + Ok(guard.values().cloned().collect()) + } + Err(err) => Err(err) + } + } + + pub fn list_players_banned(&self) -> Result<Vec<PlayerInfo>, PoisonError<MutexGuard<HashMap<String, PlayerInfo>>>> { + match self.banned_players.lock() { + Ok(guard) => { + Ok(guard.values().cloned().collect()) + } + Err(err) => Err(err) + } + } + + fn convert_deque_to_vec (deque: &VecDeque<ControlMessage>) -> Vec<ControlMessage> { + let vec_deque_ref = deque; + let mut vec = Vec::new(); + for item in vec_deque_ref { + vec.push(item.clone()) + } + vec + } + } + + // 服务端状态控制 + #[allow(dead_code)] + impl PadServer { + + pub fn stop_listening(&self) { + self.put_msg_to_all(&Leave(ServerClosed)); + self.stop.store(true, SeqCst); + } + + pub fn start_listening(self: Arc<Self>) { + + // 构建 Logger + if ! self.quiet { + logger_build(); + } + + // 运行时 + let runtime = Self::get_runtime(); + + info!("Starting \"NoGamepads Server\"."); + + // 入口 + let console = self.enable_console; + let entry = self.get_entry(console); + + // 阻塞运行 + runtime.block_on(entry); + } + + fn get_runtime() -> Runtime { + tokio::runtime::Builder::new_multi_thread() + .thread_name("nogpad-server") + .thread_stack_size(32 * 1024 * 1024) + .enable_time() + .enable_io() + .build() + .unwrap() + } + + fn get_entry(self: Arc<Self>, debug: bool) -> impl Future<Output = ()> + Send + 'static { + async move { + let main_thread = spawn({ + let client = Arc::clone(&self); + async move { + Self::main_request_thread(client).await + } + }); + + let background_thread = spawn({ + let client = Arc::clone(&self); + async move { + Self::background_thread(client).await + } + }); + + if debug { + let debug_cli = spawn({ + let client = Arc::clone(&self); + async move { + Self::process_debug_cli(client).await + } + }); + + let _ = tokio::join!(debug_cli, main_thread, background_thread); + } else { + let _ = tokio::join!(main_thread, background_thread); + } + } + } + + fn lock_game(&self) { + self.game_locked.store(true, SeqCst); + } + + fn unlock_game(&self) { + self.game_locked.store(false, SeqCst); + } + + fn is_game_locked(&self) -> bool { + self.game_locked.load(SeqCst) + } + + async fn main_request_thread(self: Arc<Self>) { + + let addr_str = format!("{}:{}", self.address.to_string(), self.port); + info!("Server listening at {}", addr_str); + + // Tcp 监听器 + let listener : TcpListener; + match TcpListener::bind(&addr_str).await { + Ok(result) => { + info!("Listener created."); + listener = result; + } + Err(_) => { + error!("Server listening at {} failed!", addr_str); + exit(1); + } + } + + // 请求信息循环 + loop { + match listener.accept().await { + Ok((stream, _)) => { + spawn(Self::process_request(Arc::clone(&self), stream)); + } + Err(error) => { + error!("Error: {}", error); + } + } + } + } + + async fn process_request(self: Arc<Self>, mut stream: TcpStream) { + let mut buffer = [0; 1024]; + let connection_msg : ConnectionMessage = read_msg(&mut buffer, &mut stream).await; + match connection_msg { + + // 客户端请求加入游戏,并建立长连接 + ConnectionMessage::Connection(info) => { + + // 加入游戏资格检测 + info!("Account {} trying to connect.", info.account.player_hash); + + // 0. 当前游戏是否已经锁定? + if self.is_game_locked() { + // 当前游戏已经锁定,禁止加入玩家,发送失败信息,并断开连接 + send_msg(&mut stream, ConnectionCallbackMessage::Deny(GameLocked)).await; + return; + } + + // 1. 是否存在重复玩家? + let online = self.is_player_online(&info); + if online { + // 当前玩家已在线,发送失败信息,并断开连接 + send_msg(&mut stream, ConnectionCallbackMessage::Deny(ContainSamePlayer)).await; + return; + } + + // 2. 该玩家是否被封禁? + let banned = self.is_player_banned(&info); + if banned { + // 当前玩家已被封禁,发送失败信息,并断开连接 + send_msg(&mut stream, ConnectionCallbackMessage::Deny(PlayerBanned)).await; + return; + } + + // OK!若执行到此处,说明该玩家具有加入资格,Welcome! + + send_msg(&mut stream, ConnectionCallbackMessage::Ok).await; + let callback : ConnectionMessage = read_msg(&mut buffer, &mut stream).await; + + match callback { + // 玩家已就绪,发送 Welcome 信息以邀请该玩家加入游戏 + ConnectionMessage::Ready => { + info!("Player \"{}\" is ready!", info.account.id); + + // 发送 Welcome + send_msg(&mut stream, ConnectionCallbackMessage::Welcome).await; + + // 注册该玩家到在线列表 + self.set_player_online(&info, true); + + // 启动控制循环 + spawn(Self::long_connection(Arc::clone(&self), stream, info)); + }, + _ => { + send_msg(&mut stream, ConnectionCallbackMessage::Deny(WhatTheHell)).await; // WTH ? + } + } + } + + // 客户端请求获得游戏信息 + ConnectionMessage::RequestProfile => { + // 发送游戏信息到客户端 + send_msg(&mut stream, ConnectionCallbackMessage::Profile(self.game_profile.clone())).await; + } + + // 客户端发来了错误信息 + ConnectionMessage::Err => { + match stream.peer_addr() { + Ok(addr) => { + warn!("Received an error message from {}.", addr.to_string()); + } + Err(_) => { + warn!("Received an error message from unknown pad_io."); + } + } + } + + // 客户端发来了不相干的信息 + _ => { + match stream.peer_addr() { + Ok(addr) => { + warn!("Received unknown connection message from {}.", addr.to_string()); + } + Err(_) => { + warn!("Received unknown connection message from unknown pad_io."); + } + } + } + } + } + + async fn long_connection(self: Arc<Self>, stream: TcpStream, player_info: PlayerInfo) { + let player_info_arc = Arc::new(player_info); + let (reader, writer) = io::split(stream); + spawn(Self::read_task(Arc::clone(&self), reader, Arc::clone(&player_info_arc))); + spawn(Self::write_task(Arc::clone(&self), writer, Arc::clone(&player_info_arc))); + } + + async fn read_task(self: Arc<Self>, + mut reader: ReadHalf<TcpStream>, + player_info: Arc<PlayerInfo>) { + let player_hash = player_info.account.player_hash.clone(); + let mut buf = [0u8; 1024]; + loop { + match reader.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + let msg = ControlMessage::de(buf[0..n].to_vec()); + { + match self.read_list.lock() { + Ok(mut guard) => { + info!("{:?} from {}({})", &msg, player_info.customize.nickname, player_info.account.id); + guard + .entry(player_hash.clone()) + .or_insert_with(VecDeque::new) + .push_back(msg); + } + Err(_) => { + } + } + } + } + Err(e) => { + warn!("Error reading from stream: {}", e); + self.set_player_online(&player_info, false); + + // 放入一条错误信息到队列,使 write_task 及时发现该玩家离开 + self.put_msg_to(GameMessage::Err, &player_info); + + break; + } + } + } + } + + async fn write_task(self: Arc<Self>, + mut writer: WriteHalf<TcpStream>, + player_info: Arc<PlayerInfo>) { + let player_hash = player_info.account.player_hash.clone(); + let mut exit = false; + loop { + let msg : Option<GameMessage>; + match self.write_list.lock() { + Ok(mut hash_map) => { + if ! hash_map.is_empty() { + match hash_map.get_mut(&player_hash) { + None => { + msg = None; + } + Some(queue) => { + if ! queue.is_empty() { + msg = queue.pop_front(); + } else { + msg = None; + hash_map.remove(&player_hash); + } + } + } + } + else { msg = None; } + } + Err(_) => { + msg = None; + } + } + if msg.is_some() { + let msg = msg.unwrap(); + match &writer.write_all(NgpdMessageEncoder::en(&msg).as_slice()).await { + Ok(_) => { + info!("Sent {:?} to {}", msg, &player_info.account.id); + } + Err(error) => { + warn!("Sent {:?} to {} failed!", msg, &player_info.account.id); + warn!("{:?}", error); + + exit = true; + } + } + } + if exit { + warn!("Long connection between \"{}\" closed.", &player_info.account.id); + break + } + } + } + + async fn background_thread(self: Arc<Self>) { + loop { + // 退出程序的监听 + if self.stop.load(SeqCst) { + tokio::time::sleep(Duration::from_secs(1)).await; + info!("Main thread exited."); + exit(0); + } + } + } + + async fn process_debug_cli(self: Arc<Self>) { + loop { + if self.stop.load(SeqCst) { + info!("Debug console exited"); + return; + } + tokio::time::sleep(Duration::from_secs_f64(0.2)).await; + let option: Option<Psc> = read_cli( + format!("SERVER {}> ", self.address.to_string()).as_str(), + "psc".to_string(), + Psc::command() + ).await; + match option { + None => {} + Some(cmd) => { + process_debug_cmd(cmd, Arc::clone(&self)); + } + } + } + } + } +}
\ No newline at end of file diff --git a/core/src/pad_io/server_debug_cli.rs b/core/src/pad_io/server_debug_cli.rs new file mode 100644 index 0000000..76e331f --- /dev/null +++ b/core/src/pad_io/server_debug_cli.rs @@ -0,0 +1,204 @@ +use std::collections::HashMap; +use crate::pad_data::pad_messages::nogamepads_messages::{ControlMessage, GameMessage}; +use crate::pad_io::server::nogamepads_server::PadServer; +use clap::{Args, Parser, Subcommand}; +use std::ops::{Index}; +use std::sync::{Arc, MutexGuard, PoisonError}; +use crate::pad_data::pad_player_info::nogamepads_player_info::PlayerInfo; + +/// NoGamePads Server - Cli +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Psc { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + + // 清屏 + #[command(about = "Clean the screen")] + Clear, + + // 关闭服务器 + #[command(about = "Close the server")] + Stop, + + // 展示所有玩家 + #[command(about = "List all online players")] + List, + + // 展示所有封禁的玩家 + #[command(about = "List all banned players")] + Banned, + + // 检查收到的消息 + #[command(about = "Check received")] + Received(ReceivedArgs), + + // 取出一条消息 + #[command(about = "Pop a message")] + Pop(PlayerArgs), + + // 踢出玩家 + #[command(about = "Kick a player")] + Kick(PlayerArgs), + + // 封禁玩家 + #[command(about = "Ban a player")] + Ban(PlayerArgs), + + // 解封(赦免)玩家 + #[command(about = "Pardon a player")] + Pardon(PlayerArgs), + + // 激活事件触发器 + #[command(about = "Send SkinEventTrigger")] + Event(EventArgs) +} + +// 检查收到的消息 +#[derive(Args, Debug)] +struct ReceivedArgs { + + #[arg(default_value = "0")] + player: usize, + + #[arg(long)] + list: bool, +} + +/// 激活事件触发器 参数 +#[derive(Args, Debug)] +struct EventArgs { + + // 玩家序号 + #[arg(value_name = "PLAYER_INDEX")] + index: usize, + + // 事件编号 + #[arg(value_name = "CONTENT")] + message: u8, +} + +#[derive(Args, Debug)] +struct PlayerArgs { + + // 玩家序号 + #[arg(value_name = "PLAYER_INDEX")] + index: usize +} + +pub fn process_debug_cmd (cmd: Psc, server: Arc<PadServer>) { + match cmd.command { + + Commands::Clear => { + clearscreen::clear().expect("Failed to clear screen"); + } + + Commands::Stop => { + server.stop_listening(); + } + + Commands::List => { + print_player_list(server.list_players()); + } + + Commands::Banned => { + print_player_list(server.list_players_banned()); + } + + Commands::Received(args) => { + let players = server.list_players().unwrap_or(Vec::new()); + let player = players.index(args.player.clamp(0, players.iter().count() -1)); + if args.list { + for msg in server.list_received(player) { + println!("{:?}", msg); + } + } else { + println!("Total {} messsage(s)!", server.list_received(player).iter().count()); + } + } + + Commands::Pop(args) => { + match get_player_by_index(&server, args.index) { + None => { + eprintln!("Pup message failed : Player index \"{}\" not found!", args.index); + } + Some(player) => { + let message = server.pop_msg_or(&player, ControlMessage::Err); + println!("{:?}", message); + } + } + } + + Commands::Kick(args) => { + let player = get_player_by_index(&server, args.index); + if player.is_some() { + let player = player.unwrap(); + server.kick_player(&player); + } + } + + Commands::Ban(args) => { + let player = get_player_by_index(&server, args.index); + if player.is_some() { + let player = player.unwrap(); + server.ban_player(&player); + } + } + + Commands::Pardon(args) => { + let player = get_player_by_ban_index(&server, args.index); + if player.is_some() { + let player = player.unwrap(); + server.pardon_player(&player); + } + } + + Commands::Event(args) => { + put_to_list(server, args.index, GameMessage::SkinEventTrigger(args.message)); + } + } +} + +fn put_to_list(server: Arc<PadServer>, player_index: usize, message: GameMessage) { + match get_player_by_index(&server, player_index) { + None => { + eprintln!("Put message failed : Player index \"{}\" not found!", player_index); + } + Some(player) => { + server.put_msg_to(message, &player); + } + } +} + +fn get_player_by_index(server: &Arc<PadServer>, index: usize) -> Option<PlayerInfo> { + let list = server.list_players().unwrap_or(Vec::new()); + let max = list.iter().count(); + let index = if max > 0 { index.clamp(0, max - 1) } else { 0 }; + + let result = list.get(index).cloned(); + result +} + +fn get_player_by_ban_index(server: &Arc<PadServer>, index: usize) -> Option<PlayerInfo> { + let list = server.list_players_banned().unwrap_or(Vec::new()); + let max = list.iter().count(); + let index = if max > 0 { index.clamp(0, max - 1) } else { 0 }; + + let result = list.get(index).cloned(); + result +} + +fn print_player_list(list: Result<Vec<PlayerInfo>, PoisonError<MutexGuard<HashMap<String, PlayerInfo>>>>) { + let list = list.unwrap_or(Vec::new()); + let mut i = 0; + for player in list { + let n = player.customize.nickname; + print!("({}){} ", i, n); + i += 1; + println!(); + } +}
\ No newline at end of file diff --git a/core_c/Cargo.toml b/core_c/Cargo.toml new file mode 100644 index 0000000..bdf762e --- /dev/null +++ b/core_c/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nogamepads_c" +version = "0.1.0" +edition = "2024" + +[dependencies] +nogamepads-core = { path = "../core" } + +[lib] +crate-type = [ "cdylib" ]
\ No newline at end of file diff --git a/core_c/src/lib.rs b/core_c/src/lib.rs new file mode 100644 index 0000000..1e28dc6 --- /dev/null +++ b/core_c/src/lib.rs @@ -0,0 +1,16 @@ +mod test; + +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/core_c/src/test.rs b/core_c/src/test.rs new file mode 100644 index 0000000..6399278 --- /dev/null +++ b/core_c/src/test.rs @@ -0,0 +1,6 @@ +use std::ffi::c_schar; + +#[unsafe(no_mangle)] +pub extern "C" fn test (a: i32, b: c_schar) -> bool { + true +}
\ No newline at end of file diff --git a/documents/About Toolchain - 关于 Rust 工具链.md b/documents/About Toolchain - 关于 Rust 工具链.md new file mode 100644 index 0000000..f3e947b --- /dev/null +++ b/documents/About Toolchain - 关于 Rust 工具链.md @@ -0,0 +1,17 @@ +# 关于工具链的使用 + +## Core + +| 平台 | 工具链名称 | +| ------- | ----------------------------- | +| Windows | stable-x86_64-pc-windows-msvc | +| Linux | NONE | +| Mac OS | NONE | + +## Console + +| 平台 | 工具链名称 | +| ------- | ----------------------------- | +| Windows | stable-x86_64-pc-windows-msvc | +| Linux | NONE | +| Mac OS | NONE | diff --git a/release/latest/LICENSE b/release/latest/LICENSE new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/release/latest/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/release/latest/LICENSE-THIRD-PARTY b/release/latest/LICENSE-THIRD-PARTY new file mode 100644 index 0000000..0f4d740 --- /dev/null +++ b/release/latest/LICENSE-THIRD-PARTY @@ -0,0 +1,29 @@ + +============ Crate core Dependencies ============ + +(MIT OR Apache-2.0) AND Unicode-3.0 (1): unicode-ident +0BSD OR Apache-2.0 OR MIT (1): adler2 +Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT (3): linux-raw-sys, rustix, wasi +Apache-2.0 OR MIT (99): addr2line, android-tzdata, android_system_properties, anstream, anstyle, anstyle-parse, anstyle-query, anstyle-wincon, autocfg, backtrace, bitflags, block-buffer, bumpalo, cc, cfg-if, chrono, clap, clap_builder, clap_derive, clap_lex, clearscreen, colorchoice, core-foundation-sys, cpufeatures, crypto-common, digest, either, env_home, env_logger, errno, fnv, gimli, heck, hermit-abi, hex, humantime, iana-time-zone, iana-time-zone-haiku, is_terminal_polyfill, js-sys, libc, lock_api, log, minimal-lexical, num-traits, object, once_cell, once_cell_polyfill, parking_lot, parking_lot_core, pin-project-lite, proc-macro2, quote, rand, rand_core, regex, regex-automata, regex-syntax, rustc-demangle, rustversion, scopeguard, serde, serde_derive, sha1, shell-words, shlex, signal-hook-registry, siphasher, smallvec, socket2, syn, thiserror, thiserror-impl, typenum, unty, utf8parse, version_check, wasm-bindgen, wasm-bindgen-backend, wasm-bindgen-macro, wasm-bindgen-macro-support, wasm-bindgen-shared, windows-core, windows-implement, windows-interface, windows-link, windows-result, windows-strings, windows-sys, windows-sys, windows-targets, windows_aarch64_gnullvm, windows_aarch64_msvc, windows_i686_gnu, windows_i686_gnullvm, windows_i686_msvc, windows_x86_64_gnu, windows_x86_64_gnullvm, windows_x86_64_msvc +Apache-2.0 OR MIT OR Zlib (1): miniz_oxide +LGPL-2 (1): nogamepads-core +MIT (20): bincode, bincode_derive, bytes, cfg_aliases, generic-array, is-terminal, mio, nix, nom, phf, phf_codegen, phf_generator, phf_shared, redox_syscall, strsim, tokio, tokio-macros, virtue, which, winsafe +MIT OR Unlicense (4): aho-corasick, memchr, termcolor, winapi-util +WTFPL (1): terminfo + +============ Crate console Dependencies ============ + +(MIT OR Apache-2.0) AND Unicode-3.0 (1): unicode-ident +0BSD OR Apache-2.0 OR MIT (1): adler2 +Apache-2.0 (2): rpassword, rtoolbox +Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT (5): linux-raw-sys, rustix, wasi, wasi, wit-bindgen-rt +Apache-2.0 OR BSD-2-Clause OR MIT (2): zerocopy, zerocopy-derive +Apache-2.0 OR BSL-1.0 (1): ryu +Apache-2.0 OR LGPL-2.1-or-later OR MIT (1): r-efi +Apache-2.0 OR MIT (120): addr2line, android-tzdata, android_system_properties, anstream, anstyle, anstyle-parse, anstyle-query, anstyle-wincon, autocfg, backtrace, bitflags, block-buffer, bumpalo, cc, cfg-if, chrono, clap, clap_builder, clap_derive, clap_lex, clearscreen, colorchoice, core-foundation-sys, cpufeatures, crypto-common, digest, dirs-next, dirs-sys-next, either, encode_unicode, env_home, env_logger, equivalent, errno, fnv, getrandom, getrandom, gimli, hashbrown, heck, hermit-abi, hex, humantime, iana-time-zone, iana-time-zone-haiku, indexmap, is_terminal_polyfill, itoa, js-sys, lazy_static, libc, lock_api, log, minimal-lexical, num-traits, object, once_cell, parking_lot, parking_lot_core, pin-project-lite, ppv-lite86, proc-macro2, quote, rand, rand, rand_chacha, rand_core, rand_core, regex, regex-automata, regex-syntax, rustc-demangle, rustversion, scopeguard, serde, serde_derive, serde_yaml, sha1, shell-words, shlex, signal-hook-registry, siphasher, smallvec, socket2, syn, term, thiserror, thiserror, thiserror-impl, thiserror-impl, typenum, unicode-width, unty, utf8parse, version_check, wasm-bindgen, wasm-bindgen-backend, wasm-bindgen-macro, wasm-bindgen-macro-support, wasm-bindgen-shared, winapi, winapi-i686-pc-windows-gnu, winapi-x86_64-pc-windows-gnu, windows-core, windows-implement, windows-interface, windows-link, windows-result, windows-strings, windows-sys, windows-sys, windows-targets, windows_aarch64_gnullvm, windows_aarch64_msvc, windows_i686_gnu, windows_i686_gnullvm, windows_i686_msvc, windows_x86_64_gnu, windows_x86_64_gnullvm, windows_x86_64_msvc +Apache-2.0 OR MIT OR Zlib (1): miniz_oxide +BSD-3-Clause (1): prettytable-rs +LGPL-2 (2): nogamepads-console, nogamepads-core +MIT (23): bincode, bincode_derive, bytes, cfg_aliases, generic-array, is-terminal, libredox, mio, nix, nom, phf, phf_codegen, phf_generator, phf_shared, redox_syscall, redox_users, strsim, tokio, tokio-macros, unsafe-libyaml, virtue, which, winsafe +MIT OR Unlicense (6): aho-corasick, csv, csv-core, memchr, termcolor, winapi-util +WTFPL (1): terminfo diff --git a/release/latest/nogpadc.exe b/release/latest/nogpadc.exe Binary files differnew file mode 100644 index 0000000..fcfcef4 --- /dev/null +++ b/release/latest/nogpadc.exe diff --git a/release/latest/nogpads.exe b/release/latest/nogpads.exe Binary files differnew file mode 100644 index 0000000..6be87d2 --- /dev/null +++ b/release/latest/nogpads.exe diff --git a/src/debug_console.rs b/src/debug_console.rs new file mode 100644 index 0000000..3e673fc --- /dev/null +++ b/src/debug_console.rs @@ -0,0 +1,64 @@ +pub mod debug_console { + use clap::{Command, FromArgMatches}; + use clap::ColorChoice::Auto; + use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; + + pub async fn read_cli<Cmd>(prefix: &str, entry: String, cmd: Command) -> Option<Cmd> + where + Cmd: FromArgMatches, + { + let input: String = { + let mut buffer = String::new(); + let mut stdin = tokio::io::BufReader::new(tokio::io::stdin()); + let mut stdout = tokio::io::stdout(); + + stdout.write_all(prefix.as_bytes()).await.ok().unwrap(); + stdout.flush().await.ok().unwrap(); + + stdin.read_line(&mut buffer).await.ok().unwrap(); + buffer.trim().to_string() + }; + + process_debug_cli(entry, input, cmd).await + } + + async fn process_debug_cli<Cmd>(entry: String, input: String, cmd: Command) -> Option<Cmd> + where + Cmd: FromArgMatches + { + if input.trim().is_empty() { + return None; + } + + let cmd = cmd + .color(Auto) + .help_template( + "{subcommands}{options}" + ) + .disable_help_flag(true) + .disable_version_flag(true); + + let args = shell_words::split(input.as_str()).unwrap_or_else(|_e| { + ["".to_string()].to_vec() + }); + + let full_args = std::iter::once(entry.into()).chain(args); + + match cmd.try_get_matches_from(full_args) { + Ok(matches) => { + match Cmd::from_arg_matches(&matches) { + Ok(cmd) => { + Some(cmd) + } + Err(_err) => { + None + } + } + } + Err(err) => { + println!("{}", err); + None + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..55ff0ec --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod debug_console; +pub mod logger;
\ No newline at end of file diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..77b136e --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,22 @@ +use env_logger::Builder; +use log::{info, LevelFilter}; + +pub fn logger_build () { + Builder::new() + .format(|buf, record| { + use std::io::Write; + let now = chrono::Local::now(); + let level_style = buf.default_level_style(record.level()); + writeln!( + buf, + "[{}] [{}] {}", + now.format("%Y-%m-%d %H:%M:%S"), + level_style.value(record.level()), + record.args() + ) + }) + .filter(None, LevelFilter::Info) + .init(); + info!("Logger Built.") +} + |
