Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1ca6a71f0 | ||
|
|
ac92212464 | ||
|
|
dbf43ac7a1 | ||
|
|
893aa76c9c | ||
|
|
21d8c71a2c | ||
|
|
41ef072c9b | ||
|
|
53246bd4c5 | ||
|
|
ac850e79d8 | ||
|
|
944c8993e8 | ||
|
|
b6508d9024 | ||
|
|
ef5bb72d5c | ||
|
|
3343ebb225 | ||
|
|
05413a554c | ||
|
|
5f015acdaa | ||
|
|
df286ef56d | ||
|
|
47d60b9f24 | ||
|
|
659d3aea23 |
+1
-1
Submodule GS-IPv6-Forwarder updated: bff3c3cf00...d2b927c7b1
@@ -80,6 +80,43 @@
|
||||
Name="Moonlight Internet Streaming Tester"/>
|
||||
</File>
|
||||
</Component>
|
||||
<Component Id="GameStreamFirewallRules" Guid="{16CA2511-B533-46F8-8882-F8AEEFDFD7DF}" KeyPath="yes">
|
||||
<fire:FirewallException Id="HttpsFwException"
|
||||
Scope="any"
|
||||
Port="47984"
|
||||
Protocol="tcp"
|
||||
Name="Moonlight - HTTPS"/>
|
||||
<fire:FirewallException Id="HttpFwException"
|
||||
Scope="any"
|
||||
Port="47989"
|
||||
Protocol="tcp"
|
||||
Name="Moonlight - HTTP"/>
|
||||
<fire:FirewallException Id="RtspFwException"
|
||||
Scope="any"
|
||||
Port="48010"
|
||||
Protocol="tcp"
|
||||
Name="Moonlight - RTSP"/>
|
||||
<fire:FirewallException Id="VideoFwException"
|
||||
Scope="any"
|
||||
Port="47998"
|
||||
Protocol="udp"
|
||||
Name="Moonlight - Video"/>
|
||||
<fire:FirewallException Id="ControlFwException"
|
||||
Scope="any"
|
||||
Port="47999"
|
||||
Protocol="udp"
|
||||
Name="Moonlight - Control"/>
|
||||
<fire:FirewallException Id="AudioFwException"
|
||||
Scope="any"
|
||||
Port="48000"
|
||||
Protocol="udp"
|
||||
Name="Moonlight - Audio"/>
|
||||
<fire:FirewallException Id="RtspuFwException"
|
||||
Scope="any"
|
||||
Port="48010"
|
||||
Protocol="udp"
|
||||
Name="Moonlight - RTSPU"/>
|
||||
</Component>
|
||||
<Component Id="Shortcuts" Guid="*">
|
||||
<Shortcut Id="StartMenuShortcut"
|
||||
Name="Moonlight Internet Streaming Tester"
|
||||
|
||||
+114
-29
@@ -12,6 +12,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "relay.h"
|
||||
#include "..\version.h"
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
@@ -53,9 +54,9 @@ static struct port_entry {
|
||||
{IPPROTO_UDP, 48010}
|
||||
};
|
||||
|
||||
static const int k_WolPorts[] = { 9 };
|
||||
static const int k_WolPorts[] = { 9, 47009 };
|
||||
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite)
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite, bool validationPass)
|
||||
{
|
||||
char intClient[16];
|
||||
char intPort[6];
|
||||
@@ -95,15 +96,40 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
if (err == 714) {
|
||||
// NoSuchEntryInArray
|
||||
printf("NOT FOUND" NL);
|
||||
|
||||
if (validationPass) {
|
||||
// On validation, we found a missing entry. Convert this entry to indefinite
|
||||
// to see if it will stick.
|
||||
indefinite = true;
|
||||
}
|
||||
}
|
||||
else if (err == 606) {
|
||||
printf("UNAUTHORIZED" NL);
|
||||
|
||||
// If we're just validating, we're done. We can't know if the entry was
|
||||
// actually applied but we'll return true to avoid false errors if it was.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||
// Some routers change the description, so we can't check that here
|
||||
if (!strcmp(intClient, myAddr)) {
|
||||
if (atoi(leaseDuration) == 0) {
|
||||
printf("OK (Permanent)" NL);
|
||||
printf("OK (Static, Internal port: %s)" NL, intPort);
|
||||
|
||||
// If we have an existing permanent mapping, we can just leave it alone.
|
||||
if (enable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
||||
printf("OK (%s seconds remaining, Internal port: %s)" NL, leaseDuration, intPort);
|
||||
}
|
||||
|
||||
// If we're just validating, we found an entry, so we're done.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
@@ -123,6 +149,11 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
else {
|
||||
printf("CONFLICT: %s %s" NL, intClient, desc);
|
||||
|
||||
// If we're just validating, we found an entry, so we're done.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some UPnP IGDs won't let unauthenticated clients delete other conflicting port mappings
|
||||
// for security reasons, but we will give it a try anyway. If GameStream is not enabled,
|
||||
// we will leave the conflicting entry alone to avoid disturbing another PC's port forwarding
|
||||
@@ -146,6 +177,10 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
}
|
||||
else {
|
||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
||||
|
||||
// If we get a strange error from the router, we'll assume it's some old broken IGDv1
|
||||
// device and only use indefinite lease durations to hopefully avoid confusing it.
|
||||
indefinite = true;
|
||||
}
|
||||
|
||||
// Bail if GameStream is disabled
|
||||
@@ -160,11 +195,31 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
||||
if (err == 725 && !indefinite) { // OnlyPermanentLeasesSupported
|
||||
if (err != UPNPCOMMAND_SUCCESS && !indefinite) {
|
||||
// This may be a broken IGD that doesn't like non-static mappings. Try a static
|
||||
// mapping before finally giving up.
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("PERMANENT ");
|
||||
printf("STATIC RETRY ");
|
||||
}
|
||||
else if (indefinite) {
|
||||
printf("STATIC ");
|
||||
}
|
||||
if (err == 718 && proto == IPPROTO_UDP && port >= 47000) { // ConflictInMappingEntry
|
||||
// Some UPnP implementations incorrectly deduplicate on the internal port instead
|
||||
// of the external port, in violation of the UPnP IGD specification. Since GFE creates
|
||||
// mappings on the same internal port as us, those routers break our mappings. To
|
||||
// work around this issue, we run relays for each of the UDP ports on an alternate
|
||||
// internal port. We'll try the alternate port if we get a conflict for a UDP entry.
|
||||
// Given that these are already horribly non-spec compliant, we won't take any chances
|
||||
// and we'll use an indefinite mapping too.
|
||||
char altPortStr[6];
|
||||
snprintf(altPortStr, sizeof(altPortStr), "%d", port + RELAY_PORT_OFFSET);
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
altPortStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("ALTERNATE ");
|
||||
}
|
||||
if (err == UPNPCOMMAND_SUCCESS) {
|
||||
printf("OK" NL);
|
||||
@@ -279,8 +334,9 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
portMappingInternalAddress = localAddress;
|
||||
}
|
||||
|
||||
// Create the port mappings
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false)) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, false)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -304,13 +360,28 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
char broadcastAddrStr[128];
|
||||
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
||||
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// When we're mapping the WOL ports upstream of our router, we map directly to
|
||||
// the port on the upstream address (likely our router's WAN interface).
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the rules are present and correct if they claimed to be added successfully
|
||||
if (success && enable) {
|
||||
// Wait 10 seconds for the router state to quiesce
|
||||
printf("Waiting before UPnP port validation...");
|
||||
Sleep(10000);
|
||||
printf("done" NL);
|
||||
|
||||
// Perform the validation pass (converting any now missing entries to permanent ones)
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, true)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +415,7 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
||||
lifetime = 604800; // 1 week
|
||||
}
|
||||
else {
|
||||
lifetime = 3600;
|
||||
lifetime = PORT_MAPPING_DURATION_SEC;
|
||||
}
|
||||
|
||||
printf("Updating NAT-PMP port mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", port);
|
||||
@@ -734,25 +805,28 @@ void NETIOAPI_API_ IpInterfaceChangeNotificationCallback(PVOID context, PMIB_IPI
|
||||
SetEvent((HANDLE)context);
|
||||
}
|
||||
|
||||
void ResetLogFile()
|
||||
void ResetLogFile(bool standaloneExe)
|
||||
{
|
||||
char oldLogFilePath[MAX_PATH + 1];
|
||||
char currentLogFilePath[MAX_PATH + 1];
|
||||
char timeString[MAX_PATH + 1] = {};
|
||||
SYSTEMTIME time;
|
||||
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||
if (!standaloneExe) {
|
||||
char oldLogFilePath[MAX_PATH + 1];
|
||||
char currentLogFilePath[MAX_PATH + 1];
|
||||
|
||||
// Close the existing stdout handle. This is important because otherwise
|
||||
// it may still be open as stdout when we try to MoveFileEx below.
|
||||
fclose(stdout);
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||
|
||||
// Rotate the current to the old log file
|
||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||
// Close the existing stdout handle. This is important because otherwise
|
||||
// it may still be open as stdout when we try to MoveFileEx below.
|
||||
fclose(stdout);
|
||||
|
||||
// Redirect stdout to this new file
|
||||
freopen(currentLogFilePath, "w", stdout);
|
||||
// Rotate the current to the old log file
|
||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||
|
||||
// Redirect stdout to this new file
|
||||
freopen(currentLogFilePath, "w", stdout);
|
||||
}
|
||||
|
||||
// Print a log header
|
||||
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
||||
@@ -789,13 +863,24 @@ DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
||||
return err;
|
||||
}
|
||||
|
||||
int Run()
|
||||
int Run(bool standaloneExe)
|
||||
{
|
||||
HANDLE ifaceChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
HANDLE gsChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
HANDLE events[2] = { ifaceChangeEvent, gsChangeEvent };
|
||||
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
// Bump the process priority class to above normal. The UDP relay threads will
|
||||
// further raise their own thread priorities to avoid preemption by other activity.
|
||||
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
|
||||
|
||||
// Create the UDP alternate port relays
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (k_Ports[i].proto == IPPROTO_UDP) {
|
||||
StartUdpRelay(k_Ports[i].port);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the thread to watch for GameStream state changes
|
||||
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
||||
@@ -817,7 +902,7 @@ int Run()
|
||||
ULONGLONG beforeSleepTime = GetTickCount64();
|
||||
DWORD ret = WaitForMultipleObjects(ARRAYSIZE(events), events, false, POLLING_DELAY_SEC * 1000);
|
||||
if (ret == WAIT_OBJECT_0) {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for interface change notification after %lld seconds" NL,
|
||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||
@@ -826,13 +911,13 @@ int Run()
|
||||
Sleep(10000);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 1) {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for GameStream state change notification after %lld seconds" NL,
|
||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||
}
|
||||
else {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for periodic refresh" NL);
|
||||
}
|
||||
@@ -893,7 +978,7 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||
|
||||
// Start the service
|
||||
err = Run();
|
||||
err = Run(false);
|
||||
if (err != 0) {
|
||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
ServiceStatus.dwWin32ExitCode = err;
|
||||
@@ -916,7 +1001,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
||||
Run();
|
||||
Run(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="miss.cpp" />
|
||||
<ClCompile Include="pcp.cpp" />
|
||||
<ClCompile Include="relay.cpp" />
|
||||
<ClCompile Include="tracer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -170,6 +171,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\version.h" />
|
||||
<ClInclude Include="relay.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
<ClCompile Include="pcp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="relay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="miss.rc">
|
||||
@@ -34,5 +37,8 @@
|
||||
<ClInclude Include="..\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="relay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2ipdef.h>
|
||||
|
||||
#include "relay.h"
|
||||
|
||||
typedef struct _UDP_TUPLE {
|
||||
SOCKET socket;
|
||||
unsigned short port;
|
||||
} UDP_TUPLE, *PUDP_TUPLE;
|
||||
|
||||
DWORD
|
||||
WINAPI
|
||||
UdpRelayThreadProc(LPVOID Context)
|
||||
{
|
||||
PUDP_TUPLE tuple = (PUDP_TUPLE)Context;
|
||||
USHORT nboPort = htons(tuple->port);
|
||||
SOCKADDR_IN lastRemoteAddr;
|
||||
|
||||
// Ensure the relay threads aren't preempted by games or other CPU intensive activity
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
|
||||
RtlZeroMemory(&lastRemoteAddr, sizeof(lastRemoteAddr));
|
||||
|
||||
for (;;) {
|
||||
char buffer[4096];
|
||||
SOCKADDR_IN sourceAddr;
|
||||
int sourceAddrLen;
|
||||
int recvLen;
|
||||
|
||||
sourceAddrLen = sizeof(sourceAddr);
|
||||
recvLen = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
||||
if (recvLen == SOCKET_ERROR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SOCKADDR_IN destinationAddr;
|
||||
if (RtlEqualMemory(&sourceAddr.sin_addr, &in4addr_loopback, sizeof(sourceAddr.sin_addr)) && sourceAddr.sin_port == nboPort) {
|
||||
// Traffic incoming from loopback interface - send it to the last remote address
|
||||
destinationAddr = lastRemoteAddr;
|
||||
}
|
||||
else {
|
||||
// Traffic incoming from the remote host - remember the source
|
||||
lastRemoteAddr = sourceAddr;
|
||||
|
||||
// Send it to the normal port via the loopback adapter
|
||||
destinationAddr = sourceAddr;
|
||||
destinationAddr.sin_addr = in4addr_loopback;
|
||||
destinationAddr.sin_port = nboPort;
|
||||
}
|
||||
|
||||
sendto(tuple->socket, buffer, recvLen, 0, (PSOCKADDR)&destinationAddr, sizeof(destinationAddr));
|
||||
}
|
||||
|
||||
closesocket(tuple->socket);
|
||||
free(tuple);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StartUdpRelay(unsigned short Port)
|
||||
{
|
||||
SOCKET sock;
|
||||
SOCKADDR_IN addr;
|
||||
HANDLE thread;
|
||||
PUDP_TUPLE tuple;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
printf("socket() failed: %d\n", WSAGetLastError());
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
// Bind to the alternate port
|
||||
RtlZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(Port + RELAY_PORT_OFFSET);
|
||||
if (bind(sock, (PSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||
printf("bind() failed: %d\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
tuple = (PUDP_TUPLE)malloc(sizeof(*tuple));
|
||||
if (tuple == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
tuple->socket = sock;
|
||||
tuple->port = Port;
|
||||
|
||||
thread = CreateThread(NULL, 0, UdpRelayThreadProc, tuple, 0, NULL);
|
||||
if (thread == NULL) {
|
||||
printf("CreateThread() failed: %d\n", GetLastError());
|
||||
closesocket(sock);
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
CloseHandle(thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define RELAY_PORT_OFFSET -10000
|
||||
|
||||
int StartUdpRelay(unsigned short Port);
|
||||
+30
-1
@@ -9,6 +9,7 @@
|
||||
#include <wtsapi32.h>
|
||||
#include <powerbase.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
#pragma comment(lib, "libnatpmp.lib")
|
||||
@@ -268,6 +269,28 @@ bool IsGameStreamEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCurrentlyStreaming()
|
||||
{
|
||||
bool ret = false;
|
||||
HANDLE processSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
PROCESSENTRY32 procEntry;
|
||||
procEntry.dwSize = sizeof(procEntry);
|
||||
Process32First(processSnapshot, &procEntry);
|
||||
|
||||
do {
|
||||
// If we find nvstreamer.exe running, we're currently streaming
|
||||
if (_stricmp(procEntry.szExeFile, "nvstreamer.exe") == 0) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(processSnapshot, &procEntry));
|
||||
|
||||
CloseHandle(processSnapshot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsConsoleSessionActive()
|
||||
{
|
||||
PWTS_SESSION_INFO_1 sessionInfo;
|
||||
@@ -979,7 +1002,7 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
|
||||
}
|
||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||
if (!strcmp(myAddr, intClient)) {
|
||||
fprintf(LOG_OUT, "OK\n");
|
||||
fprintf(LOG_OUT, "OK (Internal port: %s)\n", intPort);
|
||||
return OK;
|
||||
}
|
||||
else {
|
||||
@@ -1203,6 +1226,12 @@ int main(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IsCurrentlyStreaming()) {
|
||||
DisplayMessage("The test cannot proceed because a GameStream session is currently running on this PC.\n\n"
|
||||
"Quit the currently running app on this host within Moonlight, or reboot your PC.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsConsoleSessionActive()) {
|
||||
DisplayMessage("The system display is currently locked. You must sign in to your PC again to use GameStream.\n\n"
|
||||
"This is most often due to Microsoft Remote Desktop locking the screen. Use an alternate GameStream-compatible remote desktop solution like Chrome Remote Desktop or TeamViewer to unlock the PC and prevent this error in the future.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define VER_VERSION 5,1,0,0
|
||||
#define VER_VERSION_STR "5.1.0.0"
|
||||
#define VER_VERSION 5,3,0,0
|
||||
#define VER_VERSION_STR "5.3.0.0"
|
||||
|
||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||
|
||||
Reference in New Issue
Block a user