From 052297a1a59ad0c50c41ee0e0c095f560071915e Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Mon, 2 Jan 2023 21:02:10 -0500 Subject: [PATCH] Update app id on edit (#670) Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- CMakeLists.txt | 1 + src/confighttp.cpp | 15 +++++++++++++++ src/main.cpp | 1 + src/nvhttp.cpp | 17 ++++++++--------- src/process.cpp | 37 +++++++++++++++++++++++++------------ src/process.h | 7 +++++-- src/rand.h | 23 +++++++++++++++++++++++ src/stream.cpp | 2 +- 8 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 src/rand.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef731b51..b192e008 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,6 +375,7 @@ set(SUNSHINE_TARGET_FILES src/network.cpp src/network.h src/move_by_copy.h + src/rand.h src/task_pool.h src/thread_pool.h src/thread_safe.h diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 8df67249..e58f53ae 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -6,6 +6,7 @@ #include "process.h" #include +#include #include #include @@ -29,6 +30,7 @@ #include "network.h" #include "nvhttp.h" #include "platform/common.h" +#include "rand.h" #include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -301,6 +303,11 @@ void saveApp(resp_https_t response, req_https_t request) { response->write(data.str()); }); + std::set ids; + for(auto const &app : proc::proc.get_apps()) { + ids.insert(app.id); + } + pt::ptree inputTree, fileTree; BOOST_LOG(fatal) << config::stream.file_apps; @@ -309,6 +316,14 @@ void saveApp(resp_https_t response, req_https_t request) { pt::read_json(ss, inputTree); pt::read_json(config::stream.file_apps, fileTree); + // Moonlight checks the id of an item to determine if an item was changed + // Needs to be a 32-bit positive integer due to client limitations, "0" indicates no app + auto id = util::generate_int32(1, std::numeric_limits::max()); + while(ids.count(std::to_string(id)) > 0) { + id = util::generate_int32(1, std::numeric_limits::max()); + } + inputTree.put("id", id); + if(inputTree.get_child("prep-cmd").empty()) { inputTree.erase("prep-cmd"); } diff --git a/src/main.cpp b/src/main.cpp index 886149e8..639688aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -357,6 +357,7 @@ int main(int argc, char *argv[]) { std::string read_file(const char *path) { if(!std::filesystem::exists(path)) { + BOOST_LOG(debug) << "Missing file: " << path; return {}; } diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 9ffda1b4..444e5ed7 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -640,8 +640,8 @@ void serverinfo(std::shared_ptr::Response> res } auto current_appid = proc::proc.running(); tree.put("root.PairStatus", pair_status); - tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0); - tree.put("root.state", current_appid >= 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); + tree.put("root.currentgame", current_appid); + tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); std::ostringstream data; @@ -683,13 +683,12 @@ void applist(resp_https_t response, req_https_t request) { apps.put(".status_code", 200); - int x = 0; for(auto &proc : proc::proc.get_apps()) { pt::ptree app; app.put("IsHdrSupported"s, config::video.hevc_mode == 3 ? 1 : 0); app.put("AppTitle"s, proc.name); - app.put("ID"s, ++x); + app.put("ID", proc.id); apps.push_back(std::make_pair("App", std::move(app))); } @@ -727,17 +726,17 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) { return; } - auto appid = util::from_view(get_arg(args, "appid")) - 1; + auto appid = util::from_view(get_arg(args, "appid")); auto current_appid = proc::proc.running(); - if(current_appid != -1) { + if(current_appid > 0) { tree.put("root.resume", 0); tree.put("root..status_code", 400); return; } - if(appid >= 0) { + if(appid > 0) { auto err = proc::proc.execute(appid); if(err) { tree.put("root..status_code", err); @@ -777,7 +776,7 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) { } auto current_appid = proc::proc.running(); - if(current_appid == -1) { + if(current_appid == 0) { tree.put("root.resume", 0); tree.put("root..status_code", 503); @@ -826,7 +825,7 @@ void cancel(resp_https_t response, req_https_t request) { tree.put("root.cancel", 1); tree.put("root..status_code", 200); - if(proc::proc.running() != -1) { + if(proc::proc.running() > 0) { proc::proc.terminate(); } } diff --git a/src/process.cpp b/src/process.cpp index 46af3e83..457333da 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -84,14 +84,17 @@ int proc_t::execute(int app_id) { // Ensure starting from a clean slate terminate(); - if(app_id < 0 || app_id >= _apps.size()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { + return app.id == std::to_string(app_id); + }); + if(iter == _apps.end()) { + BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; return 404; } _app_id = app_id; - auto &proc = _apps[app_id]; + auto &proc = *iter; _undo_begin = std::begin(proc.prep_cmds); _undo_it = _undo_begin; @@ -182,7 +185,7 @@ int proc_t::running() { terminate(); } - return -1; + return 0; } void proc_t::terminate() { @@ -230,16 +233,15 @@ std::vector &proc_t::get_apps() { // Returns default image if image configuration is not set. // Returns http content-type header compatible image type. std::string proc_t::get_app_image(int app_id) { - auto app_index = app_id - 1; - if(app_index < 0 || app_index >= _apps.size()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; - return SUNSHINE_ASSETS_DIR "/box.png"; - } + auto default_image = SUNSHINE_ASSETS_DIR "/box.png"; + + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { + return app.id == std::to_string(app_id); + }); + auto app_image_path = iter == _apps.end() ? std::string() : iter->image_path; - auto default_image = SUNSHINE_ASSETS_DIR "/box.png"; - auto app_image_path = _apps[app_index].image_path; if(app_image_path.empty()) { - // image is empty, return default box image + BOOST_LOG(warning) << "Couldn't find app image for ID ["sv << app_id << ']'; return default_image; } @@ -368,6 +370,7 @@ std::optional parse(const std::string &file_name) { this_env[name] = parse_env_val(this_env, val.get_value()); } + int app_index = 1; // Start at 1, 0 indicates no app running std::vector apps; for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; @@ -379,6 +382,7 @@ std::optional parse(const std::string &file_name) { auto cmd = app_node.get_optional("cmd"s); auto image_path = app_node.get_optional("image-path"s); auto working_dir = app_node.get_optional("working-dir"s); + auto id = app_node.get_optional("id"s); std::vector prep_cmds; if(prep_nodes_opt) { @@ -424,6 +428,15 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } + if(id) { + ctx.id = parse_env_val(this_env, *id); + } + else { + ctx.id = std::to_string(app_index); + } + // Always increment index to avoid order shuffling in moonlight + app_index++; + ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); ctx.detached = std::move(detached); diff --git a/src/process.h b/src/process.h index eab324a7..fa6ecc01 100644 --- a/src/process.h +++ b/src/process.h @@ -54,6 +54,7 @@ struct ctx_t { std::string working_dir; std::string output; std::string image_path; + std::string id; }; class proc_t { @@ -62,14 +63,14 @@ public: proc_t( boost::process::environment &&env, - std::vector &&apps) : _app_id(-1), + std::vector &&apps) : _app_id(0), _env(std::move(env)), _apps(std::move(apps)) {} int execute(int app_id); /** - * @return _app_id if a process is running, otherwise returns -1 + * @return _app_id if a process is running, otherwise returns 0 */ int running(); @@ -96,6 +97,8 @@ private: file_t _pipe; std::vector::const_iterator _undo_it; std::vector::const_iterator _undo_begin; + + int app_index_from_id(int app_id); }; void refresh(const std::string &file_name); diff --git a/src/rand.h b/src/rand.h new file mode 100644 index 00000000..62273bae --- /dev/null +++ b/src/rand.h @@ -0,0 +1,23 @@ +#ifndef SUNSHINE_RAND_H +#define SUNSHINE_RAND_H + +#include + +namespace util { + +static int32_t generate_int32(std::default_random_engine &engine, int32_t min, int32_t max) { + std::uniform_int_distribution dist(min, max); + + return dist(engine); +} + +static int32_t generate_int32(int32_t min, int32_t max) { + std::random_device r; + + std::default_random_engine engine { r() }; + + return util::generate_int32(engine, min, max); +} + +} // namespace util +#endif // SUNSHINE_RAND_H diff --git a/src/stream.cpp b/src/stream.cpp index e998ade5..b30175ba 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -776,7 +776,7 @@ void controlBroadcastThread(control_server_t *server) { }) } - if(proc::proc.running() == -1) { + if(proc::proc.running() == 0) { BOOST_LOG(debug) << "Process terminated"sv; break;