Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "process.h"

#include <filesystem>
#include <set>

#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
Expand All @@ -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"
Expand Down Expand Up @@ -301,6 +303,11 @@ void saveApp(resp_https_t response, req_https_t request) {
response->write(data.str());
});

std::set<std::string> ids;
for(auto const &app : proc::proc.get_apps()) {
ids.insert(app.id);
}

pt::ptree inputTree, fileTree;

BOOST_LOG(fatal) << config::stream.file_apps;
Expand All @@ -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<std::int32_t>::max());
while(ids.count(std::to_string(id)) > 0) {
id = util::generate_int32(1, std::numeric_limits<std::int32_t>::max());
}
inputTree.put("id", id);

if(inputTree.get_child("prep-cmd").empty()) {
inputTree.erase("prep-cmd");
}
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
}

Expand Down
17 changes: 8 additions & 9 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,8 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::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;

Expand Down Expand Up @@ -683,13 +683,12 @@ void applist(resp_https_t response, req_https_t request) {

apps.put("<xmlattr>.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)));
}
Expand Down Expand Up @@ -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.<xmlattr>.status_code", 400);

return;
}

if(appid >= 0) {
if(appid > 0) {
auto err = proc::proc.execute(appid);
if(err) {
tree.put("root.<xmlattr>.status_code", err);
Expand Down Expand Up @@ -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.<xmlattr>.status_code", 503);

Expand Down Expand Up @@ -826,7 +825,7 @@ void cancel(resp_https_t response, req_https_t request) {
tree.put("root.cancel", 1);
tree.put("root.<xmlattr>.status_code", 200);

if(proc::proc.running() != -1) {
if(proc::proc.running() > 0) {
proc::proc.terminate();
}
}
Expand Down
37 changes: 25 additions & 12 deletions src/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -182,7 +185,7 @@ int proc_t::running() {
terminate();
}

return -1;
return 0;
}

void proc_t::terminate() {
Expand Down Expand Up @@ -230,16 +233,15 @@ std::vector<ctx_t> &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;
}

Expand Down Expand Up @@ -368,6 +370,7 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
this_env[name] = parse_env_val(this_env, val.get_value<std::string>());
}

int app_index = 1; // Start at 1, 0 indicates no app running
std::vector<proc::ctx_t> apps;
for(auto &[_, app_node] : apps_node) {
proc::ctx_t ctx;
Expand All @@ -379,6 +382,7 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
auto cmd = app_node.get_optional<std::string>("cmd"s);
auto image_path = app_node.get_optional<std::string>("image-path"s);
auto working_dir = app_node.get_optional<std::string>("working-dir"s);
auto id = app_node.get_optional<std::string>("id"s);

std::vector<proc::cmd_t> prep_cmds;
if(prep_nodes_opt) {
Expand Down Expand Up @@ -424,6 +428,15 @@ std::optional<proc::proc_t> 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);
Expand Down
7 changes: 5 additions & 2 deletions src/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct ctx_t {
std::string working_dir;
std::string output;
std::string image_path;
std::string id;
};

class proc_t {
Expand All @@ -62,14 +63,14 @@ class proc_t {

proc_t(
boost::process::environment &&env,
std::vector<ctx_t> &&apps) : _app_id(-1),
std::vector<ctx_t> &&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();

Expand All @@ -96,6 +97,8 @@ class proc_t {
file_t _pipe;
std::vector<cmd_t>::const_iterator _undo_it;
std::vector<cmd_t>::const_iterator _undo_begin;

int app_index_from_id(int app_id);
};

void refresh(const std::string &file_name);
Expand Down
23 changes: 23 additions & 0 deletions src/rand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef SUNSHINE_RAND_H
#define SUNSHINE_RAND_H

#include <random>

namespace util {

static int32_t generate_int32(std::default_random_engine &engine, int32_t min, int32_t max) {
std::uniform_int_distribution<std::int32_t> 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
2 changes: 1 addition & 1 deletion src/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down