diff --git a/Plugins/SML/Content/CrashReporEndpointPrompt/CrashEndpointPromtListEntry.uasset b/Plugins/SML/Content/CrashReporEndpointPrompt/CrashEndpointPromtListEntry.uasset new file mode 100644 index 0000000000..03440fc6f2 Binary files /dev/null and b/Plugins/SML/Content/CrashReporEndpointPrompt/CrashEndpointPromtListEntry.uasset differ diff --git a/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_CrashReportEndpointPrompt.uasset b/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_CrashReportEndpointPrompt.uasset new file mode 100644 index 0000000000..6a2ab878c0 Binary files /dev/null and b/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_CrashReportEndpointPrompt.uasset differ diff --git a/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_EndpointPromtListEntry.uasset b/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_EndpointPromtListEntry.uasset new file mode 100644 index 0000000000..5896df06cb Binary files /dev/null and b/Plugins/SML/Content/CrashReporEndpointPrompt/Widget_EndpointPromtListEntry.uasset differ diff --git a/Plugins/SML/ModdingCrashReporter/Build_Windows.bat b/Plugins/SML/ModdingCrashReporter/Build_Windows.bat new file mode 100644 index 0000000000..d72387e0ea --- /dev/null +++ b/Plugins/SML/ModdingCrashReporter/Build_Windows.bat @@ -0,0 +1,5 @@ +WHERE cargo >nul 2>nul +IF %ERRORLEVEL% EQU 0 ( + cargo build --release 2>&1 + copy target\release\ModdingCrashReporter.exe ..\Resources +) diff --git a/Plugins/SML/ModdingCrashReporter/Cargo.lock b/Plugins/SML/ModdingCrashReporter/Cargo.lock new file mode 100644 index 0000000000..d00125ddd7 --- /dev/null +++ b/Plugins/SML/ModdingCrashReporter/Cargo.lock @@ -0,0 +1,985 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ModdingCrashReporter" +version = "0.1.0" +dependencies = [ + "futures", + "hyper", + "hyper-tls", + "json", + "log", + "rand", + "simple-error", + "simple_logger", + "tokio", + "url", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "openssl" +version = "0.10.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "simple-error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" + +[[package]] +name = "simple_logger" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48047e77b528151aaf841a10a9025f9459da80ba820e425ff7eb005708a76dc7" +dependencies = [ + "atty", + "colored", + "log", + "time", + "winapi", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[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-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Plugins/SML/ModdingCrashReporter/Cargo.toml b/Plugins/SML/ModdingCrashReporter/Cargo.toml new file mode 100644 index 0000000000..6330ca2b8d --- /dev/null +++ b/Plugins/SML/ModdingCrashReporter/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ModdingCrashReporter" +version = "0.1.0" +edition = "2022" + +[dependencies] +log = "0.4" +hyper = { version = "0.14", features = ["full"] } +hyper-tls = "0.5.0" +tokio = { version = "1", features = ["full"] } +futures = "0.3" +simple_logger = "2.3.0" +json = "0.12.4" +url = "2.3.1" +simple-error = "0.2.3" +rand = "0.8.5" \ No newline at end of file diff --git a/Plugins/SML/ModdingCrashReporter/rustfmt.toml b/Plugins/SML/ModdingCrashReporter/rustfmt.toml new file mode 100644 index 0000000000..c8f3d8ab9e --- /dev/null +++ b/Plugins/SML/ModdingCrashReporter/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2018" +hard_tabs = true \ No newline at end of file diff --git a/Plugins/SML/ModdingCrashReporter/src/main.rs b/Plugins/SML/ModdingCrashReporter/src/main.rs new file mode 100644 index 0000000000..8cbb5d291b --- /dev/null +++ b/Plugins/SML/ModdingCrashReporter/src/main.rs @@ -0,0 +1,180 @@ +use log::{error, info, warn}; +use std::path::{PathBuf}; +use std::{env, fs}; +use std::io::Write; + +use hyper::client::ResponseFuture; +use hyper::{Body, Client}; +use hyper::header::CONTENT_TYPE; +use hyper::Request; + +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; + +#[tokio::main] +async fn main() { + simple_logger::SimpleLogger::new().env().init().unwrap(); + + info!("Satisfactory Modding Crash Reporter Launched!"); + + let log_text = match read_log() { + Some(log) => log, + None => return, + }; + + let endpoints = get_endpoints(); + + send_log_to_endpoints(&endpoints, &log_text).await; + + warn!("done!"); +} + +macro_rules! skip_fail { + ($res:expr) => { + match $res { + Ok(val) => val, + Err(_) => { + continue; + } + } + }; +} + +fn get_endpoints() -> Vec { + let args: Vec<_> = std::env::args().collect(); + if args.len() < 3 { + error!("No Mods-Path and/or SML-Config-Path Argument given!"); + return vec![]; + } + let mods_path = PathBuf::from(&args[1]); + let sml_config_path = PathBuf::from(&args[2]); + + let sml_config = std::fs::read_to_string(sml_config_path).expect("Failed to read SML config!"); + let sml_config = json::parse(&sml_config).expect("Failed to parse SML config!"); + let accepted_endpoints: Vec<_> = sml_config["acceptedCrashReportingEndpoints"].members().map(|endpoint| { + match endpoint.as_str() { + Some(str) => str, + None => "" + } + }).filter(|endpoint| endpoint.len() > 0).collect(); + + let mut endpoints = Vec::new(); + for entry in fs::read_dir(mods_path).expect("Invalid Mods folder path!") { + let entry = skip_fail!(entry); + if !skip_fail!(entry.file_type()).is_dir() { + continue + } + let mod_name = entry.file_name().to_str().unwrap().to_owned(); + let plugin_path = entry + .path() + .join(entry.file_name()) + .with_extension("uplugin"); + let plugin_json = skip_fail!(std::fs::read_to_string(plugin_path)); + let plugin_descriptor = skip_fail!(json::parse(&plugin_json)); + let endpoint = &plugin_descriptor["CrashReporterEndpoint"]; + endpoints.push(match endpoint.as_str() { + Some(str) => { + if accepted_endpoints.contains(&str) { + info!( + "Found accepted endpoint '{}' for Mod '{}'", + str, + mod_name + ); + String::from(str) + } else { + info!("Found rejected endpoint '{}' for Mod '{}'", str, mod_name); + continue + } + } + None => continue, + }); + } + + endpoints +} + +fn get_log_path() -> Option { + let key = "LOCALAPPDATA"; + match env::var_os(key) { + Some(val) => Some(PathBuf::from(val).join("FactoryGame/Saved/Logs/FactoryGame.log")), + None => { + error!("Environment variable '{key}' is not defined."); + None + } + } +} + +fn read_log() -> Option { + match std::fs::read_to_string(get_log_path()?) { + Ok(log) => Some(log), + Err(_) => { + error!("Should have been able to read the file"); + None + } + } +} + +async fn send_log_to_endpoints(endpoints: &Vec, log_text: &str) { + let client = Client::builder().build(hyper_tls::HttpsConnector::new()); + + let futures: Vec = endpoints + .iter() + .map(|endpoint| { + let request = create_request_for_endpoint(endpoint.as_str(), log_text); + client.request(request) + }) + .collect(); + + let responses = futures::future::join_all(futures).await; + for (i, response) in responses.iter().enumerate() { + match response { + Ok(_) => info!("Report successfully sent to: '{}'", endpoints[i]), + Err(e) => warn!("Failed to send report to '{}'!\n{}", endpoints[i], e), + } + } +} + +fn create_request_for_endpoint(endpoint: &str, log_text: &str) -> Request { + match try_create_request_for_endpoint(endpoint, log_text) { + Ok(req) => req, + Err(_) => { + Request::post(endpoint) + .body(log_text.to_owned().into()) + .unwrap() + } + } +} + +fn try_create_request_for_endpoint(endpoint: &str, log_text: &str) -> Result, Box> { + let url = url::Url::parse(endpoint)?; + if url.host_str().ok_or(simple_error::SimpleError::new(""))?.ends_with("discord.com") { + let boundary: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(30) + .map(char::from) + .collect(); + + let mut body: Vec = Vec::new(); + write!(body, "--{}\r\n", boundary)?; + write!(body, "Content-Disposition: form-data; name=\"payload_json\"\r\n")?; + write!(body, "Content-Type: application/json\r\n\r\n")?; + write!(body, "{}\r\n", json::object!{ + "attachments": [{ + "id": 0u64, + "description": "A Satisfactory Log File after a Game Crash", + "filename": "FactoryGame.log" + }] + }.dump())?; + write!(body, "--{}\r\n", boundary)?; + write!(body, "Content-Disposition: form-data; name=\"files[0]\"; filename=\"FactoryGame.log\"\r\n")?; + write!(body, "Content-Type: text/plain\r\n\r\n")?; + write!(body, "{}\r\n", log_text)?; + write!(body, "--{}--\r\n", boundary)?; + Ok(Request::post(endpoint) + .header(CONTENT_TYPE, &*format!("multipart/form-data; boundary={}", boundary)) + .body(body.into()) + .unwrap()) + } else { + Err(Box::new(simple_error::SimpleError::new(""))) + } +} diff --git a/Plugins/SML/SML.uplugin b/Plugins/SML/SML.uplugin index 7f2479a788..70ed3bcf44 100644 --- a/Plugins/SML/SML.uplugin +++ b/Plugins/SML/SML.uplugin @@ -21,5 +21,10 @@ "Type": "Runtime", "LoadingPhase": "PostDefault" } - ] + ], + "PreBuildSteps": { + "Win64": [ + "cd $(PluginDir)\\ModdingCrashReporter && Build_Windows.bat" + ] + } } \ No newline at end of file diff --git a/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReportEndpointPrompt.cpp b/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReportEndpointPrompt.cpp new file mode 100644 index 0000000000..bd412b17c3 --- /dev/null +++ b/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReportEndpointPrompt.cpp @@ -0,0 +1,71 @@ +#include "CrashReporting/SMLCrashReportEndpointPrompt.h" +#include "SatisfactoryModLoader.h" +#include "Components/Overlay.h" +#include "Components/OverlaySlot.h" +#include "ModLoading/ModLoadingLibrary.h" +#include "Reflection/ReflectionHelper.h" + +#define SML_CRASH_REPORT_ENDPOINT_PROMPT TEXT("/SML/CrashReporEndpointPrompt/Widget_CrashReportEndpointPrompt.Widget_CrashReportEndpointPrompt_C") + +void USMLCrashReportEndpointPrompt::ApplyMainMenuPatch(UUserWidget* MainMenu) { + FSMLConfiguration Configuration = FSatisfactoryModLoader::GetSMLConfiguration(); + if (Configuration.bDisableModdingCrashReporter) return; + + UUserWidget* InviteNotification = FReflectionHelper::GetObjectPropertyValue(MainMenu, TEXT("Widget_InviteNotification")); + UOverlay* Overlay = Cast(InviteNotification->GetParent()); + checkf(Overlay, TEXT("Widget_InviteNotification parent is not a overlay")); + + UClass* MenuBaseClass = LoadObject(NULL, SML_CRASH_REPORT_ENDPOINT_PROMPT); + checkf(MenuBaseClass, TEXT("Failed to load SML menu page asset from path '%s'"), SML_CRASH_REPORT_ENDPOINT_PROMPT); + + USMLCrashReportEndpointPrompt* NewWidget = Cast(CreateWidgetInstance(*MainMenu->GetWorld(), MenuBaseClass, TEXT("ModListSubMenu"))); + NewWidget->LoadEndpointSelections(); + if (NewWidget->EndpointSelections.Num() > 0) { + UOverlaySlot* Slot = Overlay->AddChildToOverlay(NewWidget); + Slot->SetVerticalAlignment(VAlign_Fill); + Slot->SetHorizontalAlignment(HAlign_Fill); + } +} + +void USMLCrashReportEndpointPrompt::LoadEndpointSelections() { + FSMLConfiguration Configuration = FSatisfactoryModLoader::GetSMLConfiguration(); + + UModLoadingLibrary* SubSys = GEngine->GetEngineSubsystem(); + TArray Mods = SubSys->GetLoadedMods(); + + EndpointSelections.Empty(); + + for (const FModInfo& Mod : Mods) { + FString URL = Mod.CrashReportingEndpoint; + if (URL.IsEmpty()) continue; + if (Configuration.AcceptedCrashReportingEndpoints.Contains(URL)) continue; + if (Configuration.RejectedCrashReportingEndpoints.Contains(URL)) continue; + + EndpointSelections.Add(FSMLCrashReportEndpointSelection(Mod)); + } +} + +void USMLCrashReportEndpointPrompt::SaveEndpointSelections() { +#if !WITH_EDITOR + FSMLConfiguration& Configuration = FSatisfactoryModLoader::GetSMLConfiguration(); + + for (const FSMLCrashReportEndpointSelection& Selection : EndpointSelections) { + const FString& URL = Selection.ModInfo.CrashReportingEndpoint; + if (Selection.bAccepted) { + Configuration.AcceptedCrashReportingEndpoints.AddUnique(URL); + Configuration.RejectedCrashReportingEndpoints.Remove(URL); + } else { + Configuration.RejectedCrashReportingEndpoints.AddUnique(URL); + Configuration.AcceptedCrashReportingEndpoints.Remove(URL); + } + } + + FSatisfactoryModLoader::SaveSMLConfiguration(); +#endif +} + +void USMLCrashReportEndpointPrompt::DisableCrashReporter() { + FSMLConfiguration& Configuration = FSatisfactoryModLoader::GetSMLConfiguration(); + Configuration.bDisableModdingCrashReporter = true; + FSatisfactoryModLoader::SaveSMLConfiguration(); +} diff --git a/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReporter.cpp b/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReporter.cpp new file mode 100644 index 0000000000..a9fee06811 --- /dev/null +++ b/Plugins/SML/Source/SML/Private/CrashReporting/SMLCrashReporter.cpp @@ -0,0 +1,24 @@ +#include "CrashReporting/SMLCrashReporter.h" + +#include "SatisfactoryModLoader.h" +#include "SMLConfiguration.h" + +void FSmlCrashReporter::ApplyCrashReporterPatch() { + FSMLConfiguration Config = FSatisfactoryModLoader::GetSMLConfiguration(); + if (Config.bDisableModdingCrashReporter) return; + + FCoreDelegates::OnShutdownAfterError.AddStatic([]() { + static bool bAlreadyLaunched = false; + if (bAlreadyLaunched) return; + bAlreadyLaunched = true; + + UE_LOG(LogSatisfactoryModLoader, Warning, TEXT("Launch Satisfactory Modding Crash Reporter")); + + FString Path = FPlatformMisc::RootDir(); + FString ModsFolderPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectModsDir()); + FString CrashReporterPath = FPaths::Combine(ModsFolderPath, TEXT("SML/Resources/ModdingCrashReporter.exe")); + FString SMLConfigPath = FPaths::Combine(ModsFolderPath, TEXT("../Configs/SML.cfg")); + + FPlatformProcess::CreateProc(*CrashReporterPath, *("\"" + ModsFolderPath + "\" \"" + SMLConfigPath + "\""), true, false, false, NULL, 0, NULL, NULL); + }); +} diff --git a/Plugins/SML/Source/SML/Private/ModLoading/ModLoadingLibrary.cpp b/Plugins/SML/Source/SML/Private/ModLoading/ModLoadingLibrary.cpp index 3fe5427994..baf70b8451 100644 --- a/Plugins/SML/Source/SML/Private/ModLoading/ModLoadingLibrary.cpp +++ b/Plugins/SML/Source/SML/Private/ModLoading/ModLoadingLibrary.cpp @@ -56,13 +56,19 @@ void FSMLPluginDescriptorMetadata::Load(const FString& PluginName, const TShared } } + //Try getting Crash-Reporter-Server Endpoint + FString CrashReporterEndpointString; + if (Source->TryGetStringField(TEXT("CrashReporterEndpoint"), CrashReporterEndpointString)) { + this->CrashReporterEndpoint = CrashReporterEndpointString; + } + //Loop plugins specified in the plugin manifest and detect version predicate inside of them const TArray>* PluginsArray; if (Source->TryGetArrayField(TEXT("Plugins"), PluginsArray)) { for (const TSharedPtr& Item : *PluginsArray) { const TSharedPtr* ObjectPtr; - if (Item.IsValid() && Item->TryGetObject(ObjectPtr)) { + if (Item.IsValid() && Item->TryGetObject(ObjectPtr)) { const FString DependencyName = (*ObjectPtr)->GetStringField(TEXT("Name")); FString DependencyVersionRangeString; if ((*ObjectPtr)->TryGetStringField(TEXT("SemVersion"), DependencyVersionRangeString)) { @@ -77,7 +83,6 @@ void FSMLPluginDescriptorMetadata::Load(const FString& PluginName, const TShared } } } - } } } @@ -250,6 +255,8 @@ void UModLoadingLibrary::PopulatePluginModInfo(IPlugin& Plugin, FModInfo& OutMod OutModInfo.bAcceptsAnyRemoteVersion = PluginDescriptorMetadata.bAcceptsAnyRemoteVersion; OutModInfo.RemoteVersionRange = PluginDescriptorMetadata.RemoteVersionRange; + + OutModInfo.CrashReportingEndpoint = PluginDescriptorMetadata.CrashReporterEndpoint.Get(""); } void UModLoadingLibrary::OnNewPluginCreated(IPlugin& Plugin) { diff --git a/Plugins/SML/Source/SML/Private/Patching/Patch/MainMenuPatch.cpp b/Plugins/SML/Source/SML/Private/Patching/Patch/MainMenuPatch.cpp index 5969ff6b65..63d4610e16 100644 --- a/Plugins/SML/Source/SML/Private/Patching/Patch/MainMenuPatch.cpp +++ b/Plugins/SML/Source/SML/Private/Patching/Patch/MainMenuPatch.cpp @@ -6,6 +6,7 @@ #include "Components/TextBlock.h" #include "Components/VerticalBox.h" #include "Blueprint/WidgetBlueprintLibrary.h" +#include "CrashReporting/SMLCrashReportEndpointPrompt.h" #include "Engine/Engine.h" #include "ModLoading/ModLoadingLibrary.h" @@ -83,6 +84,7 @@ void FMainMenuPatch::RegisterPatch() { UBlueprintHookManager* HookManager = GEngine->GetEngineSubsystem(); HookManager->HookBlueprintFunction(ConstructFunction, [](FBlueprintHookHelper& HookHelper) { FMainMenuPatch::ApplyMainMenuPatch(CastChecked(HookHelper.GetContext())); + USMLCrashReportEndpointPrompt::ApplyMainMenuPatch(CastChecked(HookHelper.GetContext())); }, EPredefinedHookOffset::Return); } diff --git a/Plugins/SML/Source/SML/Private/SMLConfiguration.cpp b/Plugins/SML/Source/SML/Private/SMLConfiguration.cpp index 2ae14665be..65477477ee 100644 --- a/Plugins/SML/Source/SML/Private/SMLConfiguration.cpp +++ b/Plugins/SML/Source/SML/Private/SMLConfiguration.cpp @@ -38,6 +38,32 @@ void FSMLConfiguration::ReadFromJson(const TSharedPtr& Json, FSMLCo bIsMissingSectionsInternal = true; } + if (Json->HasTypedField(TEXT("acceptedCrashReportingEndpoints"))) { + const TArray>& AcceptedCrashReportingEndpoints = Json->GetArrayField(TEXT("acceptedCrashReportingEndpoints")); + for (const TSharedPtr& Value : AcceptedCrashReportingEndpoints) { + if (Value->Type == EJson::String) + OutConfiguration.AcceptedCrashReportingEndpoints.Add(Value->AsString()); + } + } else { + bIsMissingSectionsInternal = true; + } + + if (Json->HasTypedField(TEXT("rejectedCrashReportingEndpoints"))) { + const TArray>& RejectedCrashReportingEndpoints = Json->GetArrayField(TEXT("rejectedCrashReportingEndpoints")); + for (const TSharedPtr& Value : RejectedCrashReportingEndpoints) { + if (Value->Type == EJson::String) + OutConfiguration.RejectedCrashReportingEndpoints.Add(Value->AsString()); + } + } else { + bIsMissingSectionsInternal = true; + } + + if (Json->HasTypedField(TEXT("disableModdingCrashReporter"))) { + OutConfiguration.bDisableModdingCrashReporter = Json->GetBoolField(TEXT("disableModdingCrashReporter")); + } else { + bIsMissingSectionsInternal = true; + } + if (OutIsMissingSections) { *OutIsMissingSections = bIsMissingSectionsInternal; } @@ -53,4 +79,18 @@ void FSMLConfiguration::WriteToJson(const TSharedPtr& OutJson, cons DisabledChatCommands.Add(MakeShareable(new FJsonValueString(Value))); } OutJson->SetArrayField(TEXT("disabledChatCommands"), DisabledChatCommands); + + TArray> AcceptedCrashReportingEndpoints; + for (const FString& Value : Configuration.AcceptedCrashReportingEndpoints) { + AcceptedCrashReportingEndpoints.Add(MakeShared(Value)); + } + OutJson->SetArrayField(TEXT("acceptedCrashReportingEndpoints"), AcceptedCrashReportingEndpoints); + + TArray> RejectedCrashReportingEndpoints; + for (const FString& Value : Configuration.RejectedCrashReportingEndpoints) { + RejectedCrashReportingEndpoints.Add(MakeShared(Value)); + } + OutJson->SetArrayField(TEXT("rejectedCrashReportingEndpoints"), RejectedCrashReportingEndpoints); + + OutJson->SetBoolField(TEXT("disableModdingCrashReporter"), Configuration.bDisableModdingCrashReporter); } diff --git a/Plugins/SML/Source/SML/Private/SatisfactoryModLoader.cpp b/Plugins/SML/Source/SML/Private/SatisfactoryModLoader.cpp index 34cc2e73bb..a57a58538f 100644 --- a/Plugins/SML/Source/SML/Private/SatisfactoryModLoader.cpp +++ b/Plugins/SML/Source/SML/Private/SatisfactoryModLoader.cpp @@ -1,6 +1,7 @@ #include "SatisfactoryModLoader.h" #include "FGPlayerController.h" #include "Configuration/ConfigManager.h" +#include "CrashReporting/SMLCrashReporter.h" #include "Tooltip/ItemTooltipSubsystem.h" #include "Registry/ModContentRegistry.h" #include "Network/NetworkHandler.h" @@ -70,22 +71,28 @@ void FSatisfactoryModLoader::LoadSMLConfiguration(bool bAllowSave) { } if (bShouldWriteConfiguration) { - const TSharedRef JsonObject = MakeShareable(new FJsonObject()); - FSMLConfiguration::WriteToJson(JsonObject, SMLConfigurationPrivate); + SaveSMLConfiguration(); + } +} - FString OutSerializedConfiguration; - const TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&OutSerializedConfiguration); - FJsonSerializer::Serialize(JsonObject, JsonWriter); +void FSatisfactoryModLoader::SaveSMLConfiguration() { + const FString ConfigLocation = UConfigManager::GetConfigurationFilePath(FConfigId{TEXT("SML")}); + + const TSharedRef JsonObject = MakeShareable(new FJsonObject()); + FSMLConfiguration::WriteToJson(JsonObject, SMLConfigurationPrivate); - //Make sure configuration directory exists - FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*FPaths::GetPath(ConfigLocation)); + FString OutSerializedConfiguration; + const TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&OutSerializedConfiguration); + FJsonSerializer::Serialize(JsonObject, JsonWriter); - //Write file onto the disk now - if (FFileHelper::SaveStringToFile(OutSerializedConfiguration, *ConfigLocation)) { - UE_LOG(LogSatisfactoryModLoader, Display, TEXT("Successfully saved SML configuration")); - } else { - UE_LOG(LogSatisfactoryModLoader, Error, TEXT("Failed to save SML configuration to %s"), *ConfigLocation); - } + //Make sure configuration directory exists + FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*FPaths::GetPath(ConfigLocation)); + + //Write file onto the disk now + if (FFileHelper::SaveStringToFile(OutSerializedConfiguration, *ConfigLocation)) { + UE_LOG(LogSatisfactoryModLoader, Display, TEXT("Successfully saved SML configuration")); + } else { + UE_LOG(LogSatisfactoryModLoader, Error, TEXT("Failed to save SML configuration to %s"), *ConfigLocation); } } @@ -127,6 +134,8 @@ void FSatisfactoryModLoader::RegisterSubsystemPatches() { //Only register these patches in shipping, where bodies of the ACharacter::Cheat methods are stripped #if UE_BUILD_SHIPPING FCheatManagerPatch::RegisterPatch(); + + FSmlCrashReporter::ApplyCrashReporterPatch(); #endif } diff --git a/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReportEndpointPrompt.h b/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReportEndpointPrompt.h new file mode 100644 index 0000000000..b55083e89b --- /dev/null +++ b/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReportEndpointPrompt.h @@ -0,0 +1,41 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "ModLoading/ModLoadingLibrary.h" +#include "SMLCrashReportEndpointPrompt.generated.h" + +USTRUCT(BlueprintType) +struct FSMLCrashReportEndpointSelection { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FModInfo ModInfo; + + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool bAccepted = true; + + FSMLCrashReportEndpointSelection() = default; + FSMLCrashReportEndpointSelection(const FModInfo& ModInfo) : ModInfo(ModInfo) {} +}; + +UCLASS(Blueprintable) +class SML_API USMLCrashReportEndpointPrompt : public UUserWidget { + GENERATED_BODY() + +public: + static void ApplyMainMenuPatch(UUserWidget* MainMenu); + +protected: + UPROPERTY(BlueprintReadOnly) + TArray EndpointSelections; + + UFUNCTION(BlueprintCallable) + void LoadEndpointSelections(); + + UFUNCTION(BlueprintCallable) + void SaveEndpointSelections(); + + UFUNCTION(BlueprintCallable) + void DisableCrashReporter(); +}; diff --git a/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReporter.h b/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReporter.h new file mode 100644 index 0000000000..d90d45d3f8 --- /dev/null +++ b/Plugins/SML/Source/SML/Public/CrashReporting/SMLCrashReporter.h @@ -0,0 +1,8 @@ +#pragma once + +#include "CoreMinimal.h" + +class SML_API FSmlCrashReporter { +public: + static void ApplyCrashReporterPatch(); +}; \ No newline at end of file diff --git a/Plugins/SML/Source/SML/Public/ModLoading/ModLoadingLibrary.h b/Plugins/SML/Source/SML/Public/ModLoading/ModLoadingLibrary.h index 361df99cfc..d4b3e766f4 100644 --- a/Plugins/SML/Source/SML/Public/ModLoading/ModLoadingLibrary.h +++ b/Plugins/SML/Source/SML/Public/ModLoading/ModLoadingLibrary.h @@ -62,6 +62,10 @@ struct SML_API FModInfo { /** Range of the remote versions accepted by this mod. Defaults to >=CurrentVersion, unless explicitly defined */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) FVersionRange RemoteVersionRange; + + /** If set, URL of the crash reporting endpoint at witch the mod developer wants to receive crash reports. Otherwise Empty. */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) + FString CrashReportingEndpoint; }; @@ -80,6 +84,9 @@ struct SML_API FSMLPluginDescriptorMetadata { /** Version constraints for dependencies as specified in plugin refs */ TMap DependenciesVersions; + /** Crash-Report-Server Endpoint to where crash reports should get sent to if accepted by the user */ + TOptional CrashReporterEndpoint; + /** Setups defaults for metadata from normal plugin descriptor */ void SetupDefaults(const struct FPluginDescriptor& PluginDescriptor); diff --git a/Plugins/SML/Source/SML/Public/SMLConfiguration.h b/Plugins/SML/Source/SML/Public/SMLConfiguration.h index dd3cc98aeb..b3ecbd057e 100644 --- a/Plugins/SML/Source/SML/Public/SMLConfiguration.h +++ b/Plugins/SML/Source/SML/Public/SMLConfiguration.h @@ -34,6 +34,27 @@ struct SML_API FSMLConfiguration { * See UFGCheatManager for command list */ bool bEnableCheatConsoleCommands; + + /** + * List of all user accepted crash reporting endpoints. + * If any mod defines a Crash Reporting Endpoint URL that is contained within this list, + * the modding crash reporter will send a crash report to the URL if possible without further notice to the user. + */ + TArray AcceptedCrashReportingEndpoints; + + /** + * List of user rejected crash reporting endpoints. + * Opposite to AcceptedCrashReportingEndpoints. + * Necessary to not repeat asking a user for the same endpoint multiple times between sessions. + */ + TArray RejectedCrashReportingEndpoints; + + /** + * If enabled, disables the custom satisfactory modding crash reporter and no crash reports will ever be sent + * to any of the mods nor the modding infrastructure maintainers. + */ + bool bDisableModdingCrashReporter; + public: /** Deserializes configuration from JSON object */ static void ReadFromJson(const TSharedPtr& Json, FSMLConfiguration& OutConfiguration, bool* OutIsMissingSections = NULL); diff --git a/Plugins/SML/Source/SML/Public/SatisfactoryModLoader.h b/Plugins/SML/Source/SML/Public/SatisfactoryModLoader.h index dfaedae520..93768f63e0 100644 --- a/Plugins/SML/Source/SML/Public/SatisfactoryModLoader.h +++ b/Plugins/SML/Source/SML/Public/SatisfactoryModLoader.h @@ -23,7 +23,10 @@ class SML_API FSatisfactoryModLoader { static TMap GetExtraAttributes(); /** Returns active SML configuration. If not loaded, it will return empty struct */ - FORCEINLINE static FSMLConfiguration GetSMLConfiguration() { return SMLConfigurationPrivate; } + FORCEINLINE static FSMLConfiguration& GetSMLConfiguration() { return SMLConfigurationPrivate; } + + /** Saves SML configuration to file. */ + static void SaveSMLConfiguration(); private: friend class FSMLModule;