mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-01 07:15:54 +00:00
Add support for custom ports with Sunshine
This commit is contained in:
parent
3f00f25a39
commit
0325a3b88c
@ -166,109 +166,128 @@ static int load_cert(const char* keyDirectory) {
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int load_server_status(PSERVER_DATA server) {
|
||||
|
||||
static int load_serverinfo(PSERVER_DATA server, bool https) {
|
||||
uuid_t uuid;
|
||||
char uuid_str[UUID_STRLEN];
|
||||
|
||||
int ret;
|
||||
char url[4096];
|
||||
int ret = GS_INVALID;
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
char *httpsPortText = NULL;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
|
||||
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||
https ? "https" : "http", server->serverInfo.address, https ? server->httpsPort : server->httpPort, unique_id, uuid_str);
|
||||
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL) {
|
||||
ret = GS_OUT_OF_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
if (http_request(url, data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||
ret = GS_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "HttpsPort", &httpsPortText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
server->httpsPort = atoi(httpsPortText);
|
||||
if (!server->httpsPort)
|
||||
server->httpsPort = 47984;
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// After GFE 2.8, current game remains set even after streaming
|
||||
// has ended. We emulate the old behavior by forcing it to zero
|
||||
// if streaming is not active.
|
||||
server->currentGame = 0;
|
||||
}
|
||||
ret = GS_OK;
|
||||
|
||||
cleanup:
|
||||
if (data != NULL)
|
||||
http_free_data(data);
|
||||
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
if (httpsPortText != NULL)
|
||||
free(httpsPortText);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int load_server_status(PSERVER_DATA server) {
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
/* Fetch the HTTPS port if we don't have one yet */
|
||||
if (!server->httpsPort) {
|
||||
ret = load_serverinfo(server, false);
|
||||
if (ret != GS_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GS_INVALID;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
|
||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// is not already paired. Since we can't pair without knowing the server version, we
|
||||
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
||||
// for everything because it doesn't accurately tell us if we're paired.
|
||||
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? server->httpsPort : server->httpPort, unique_id, uuid_str);
|
||||
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL) {
|
||||
ret = GS_OUT_OF_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
if (http_request(url, data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||
ret = GS_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// After GFE 2.8, current game remains set even after streaming
|
||||
// has ended. We emulate the old behavior by forcing it to zero
|
||||
// if streaming is not active.
|
||||
server->currentGame = 0;
|
||||
}
|
||||
ret = GS_OK;
|
||||
|
||||
cleanup:
|
||||
if (data != NULL)
|
||||
http_free_data(data);
|
||||
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
i++;
|
||||
} while (ret != GS_OK && i < 2);
|
||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// is not already paired. Since we can't pair without knowing the server version, we
|
||||
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
||||
// for everything because it doesn't accurately tell us if we're paired.
|
||||
ret = GS_INVALID;
|
||||
for (i = 0; i < 2 && ret != GS_OK; i++) {
|
||||
ret = load_serverinfo(server, i == 0);
|
||||
}
|
||||
|
||||
if (ret == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
@ -790,7 +809,9 @@ int gs_quit_app(PSERVER_DATA server) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
char* portSeparator;
|
||||
|
||||
mkdirtree(keyDirectory);
|
||||
if (load_unique_id(keyDirectory) != GS_OK)
|
||||
return GS_FAILED;
|
||||
@ -803,7 +824,7 @@ int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int lo
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
server->serverInfo.address = address;
|
||||
server->unsupported = unsupported;
|
||||
server->httpPort = 47989;
|
||||
server->httpsPort = 47984;
|
||||
server->httpPort = httpPort ? httpPort : 47989;
|
||||
server->httpsPort = 0; /* Populated by load_server_status() */
|
||||
return load_server_status(server);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ typedef struct _SERVER_DATA {
|
||||
unsigned short httpsPort;
|
||||
} SERVER_DATA, *PSERVER_DATA;
|
||||
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_init(PSERVER_DATA server, char* address, unsigned short httpPort, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
|
||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
||||
int gs_unpair(PSERVER_DATA server);
|
||||
|
@ -33,6 +33,11 @@
|
||||
|
||||
static AvahiSimplePoll *simple_poll = NULL;
|
||||
|
||||
struct cb_ctx {
|
||||
char* address;
|
||||
unsigned short* port;
|
||||
};
|
||||
|
||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
|
||||
if (state == AVAHI_CLIENT_FAILURE) {
|
||||
gs_error = "Server connection failure";
|
||||
@ -43,12 +48,14 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
|
||||
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) {
|
||||
if (event == AVAHI_RESOLVER_FOUND) {
|
||||
if (userdata != NULL) {
|
||||
avahi_address_snprint(userdata, AVAHI_ADDRESS_STR_MAX, address);
|
||||
struct cb_ctx* ctx = userdata;
|
||||
avahi_address_snprint(ctx->address, AVAHI_ADDRESS_STR_MAX, address);
|
||||
*ctx->port = port;
|
||||
avahi_simple_poll_quit(simple_poll);
|
||||
} else {
|
||||
char strAddress[AVAHI_ADDRESS_STR_MAX];
|
||||
avahi_address_snprint(strAddress, sizeof(strAddress), address);
|
||||
printf(" %s (%s)\n", host_name, strAddress);
|
||||
printf(" %s (%s:%u)\n", host_name, strAddress, port);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +80,7 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
|
||||
}
|
||||
}
|
||||
|
||||
void gs_discover_server(char* dest) {
|
||||
void gs_discover_server(char* dest, unsigned short* port) {
|
||||
AvahiClient *client = NULL;
|
||||
AvahiServiceBrowser *sb = NULL;
|
||||
|
||||
@ -89,7 +96,10 @@ void gs_discover_server(char* dest) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, dest))) {
|
||||
struct cb_ctx ctx;
|
||||
ctx.address = dest;
|
||||
ctx.port = port;
|
||||
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, &ctx))) {
|
||||
gs_error = "Failed to create service browser";
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -21,4 +21,4 @@
|
||||
|
||||
#define MAX_ADDRESS_SIZE 40
|
||||
|
||||
void gs_discover_server(char* dest);
|
||||
void gs_discover_server(char* dest, unsigned short* port);
|
||||
|
@ -73,6 +73,7 @@ static struct option long_options[] = {
|
||||
{"debug", no_argument, NULL, 'Z'},
|
||||
{"nomouseemulation", no_argument, NULL, '4'},
|
||||
{"pin", required_argument, NULL, '5'},
|
||||
{"port", required_argument, NULL, '6'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
@ -248,6 +249,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
case '5':
|
||||
config->pin = atoi(value);
|
||||
break;
|
||||
case '6':
|
||||
config->port = atoi(value);
|
||||
break;
|
||||
case 1:
|
||||
if (config->action == NULL)
|
||||
config->action = value;
|
||||
@ -369,6 +373,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
config->rotate = 0;
|
||||
config->codec = CODEC_UNSPECIFIED;
|
||||
config->pin = 0;
|
||||
config->port = 47989;
|
||||
|
||||
config->inputsCount = 0;
|
||||
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
|
||||
@ -386,7 +391,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
} else {
|
||||
int option_index = 0;
|
||||
int c;
|
||||
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:", long_options, &option_index)) != -1) {
|
||||
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:6:", long_options, &option_index)) != -1) {
|
||||
parse_argument(c, optarg, config);
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ typedef struct _CONFIGURATION {
|
||||
char* inputs[MAX_INPUTS];
|
||||
int inputsCount;
|
||||
enum codecs codec;
|
||||
int pin;
|
||||
int pin;
|
||||
unsigned short port;
|
||||
} CONFIGURATION, *PCONFIGURATION;
|
||||
|
||||
extern bool inputAdded;
|
||||
|
@ -263,7 +263,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
config.address[0] = 0;
|
||||
printf("Searching for server...\n");
|
||||
gs_discover_server(config.address);
|
||||
gs_discover_server(config.address, &config.port);
|
||||
if (config.address[0] == 0) {
|
||||
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
|
||||
exit(-1);
|
||||
@ -279,7 +279,7 @@ int main(int argc, char* argv[]) {
|
||||
printf("Connecting to %s...\n", config.address);
|
||||
|
||||
int ret;
|
||||
if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
||||
if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(-1);
|
||||
} else if (ret == GS_ERROR) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user