Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef5bb72d5c | ||
|
|
3343ebb225 | ||
|
|
05413a554c | ||
|
|
5f015acdaa | ||
|
|
df286ef56d | ||
|
|
47d60b9f24 | ||
|
|
659d3aea23 |
+1
-1
Submodule GS-IPv6-Forwarder updated: bff3c3cf00...d2b927c7b1
+86
-8
@@ -12,6 +12,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "relay.h"
|
||||||
#include "..\version.h"
|
#include "..\version.h"
|
||||||
|
|
||||||
#pragma comment(lib, "miniupnpc.lib")
|
#pragma comment(lib, "miniupnpc.lib")
|
||||||
@@ -53,9 +54,9 @@ static struct port_entry {
|
|||||||
{IPPROTO_UDP, 48010}
|
{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 intClient[16];
|
||||||
char intPort[6];
|
char intPort[6];
|
||||||
@@ -95,17 +96,42 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
if (err == 714) {
|
if (err == 714) {
|
||||||
// NoSuchEntryInArray
|
// NoSuchEntryInArray
|
||||||
printf("NOT FOUND" NL);
|
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) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
// Some routers change the description, so we can't check that here
|
// Some routers change the description, so we can't check that here
|
||||||
if (!strcmp(intClient, myAddr)) {
|
if (!strcmp(intClient, myAddr)) {
|
||||||
if (atoi(leaseDuration) == 0) {
|
if (atoi(leaseDuration) == 0) {
|
||||||
printf("OK (Permanent)" NL);
|
printf("OK (Static)" NL);
|
||||||
|
|
||||||
|
// If we have an existing permanent mapping, we can just leave it alone.
|
||||||
|
if (enable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're just validating, we found an entry, so we're done.
|
||||||
|
if (validationPass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
// This is our entry. Go ahead and nuke it
|
// This is our entry. Go ahead and nuke it
|
||||||
printf("Deleting UPnP mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
printf("Deleting UPnP mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||||
@@ -123,6 +149,11 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
else {
|
else {
|
||||||
printf("CONFLICT: %s %s" NL, intClient, desc);
|
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
|
// 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,
|
// 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
|
// 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 {
|
else {
|
||||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
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
|
// Bail if GameStream is disabled
|
||||||
@@ -160,11 +195,31 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
err = UPNP_AddPortMapping(
|
err = UPNP_AddPortMapping(
|
||||||
urls->controlURL, data->first.servicetype, portStr,
|
urls->controlURL, data->first.servicetype, portStr,
|
||||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
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(
|
err = UPNP_AddPortMapping(
|
||||||
urls->controlURL, data->first.servicetype, portStr,
|
urls->controlURL, data->first.servicetype, portStr,
|
||||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||||
printf("PERMANENT ");
|
printf("STATIC RETRY ");
|
||||||
|
}
|
||||||
|
else if (indefinite) {
|
||||||
|
printf("STATIC ");
|
||||||
|
}
|
||||||
|
if (err == 718 && proto == IPPROTO_UDP) { // 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) {
|
if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
printf("OK" NL);
|
printf("OK" NL);
|
||||||
@@ -279,8 +334,9 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
|||||||
portMappingInternalAddress = localAddress;
|
portMappingInternalAddress = localAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the port mappings
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
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;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,13 +360,28 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
|||||||
char broadcastAddrStr[128];
|
char broadcastAddrStr[128];
|
||||||
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
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 {
|
else {
|
||||||
// When we're mapping the WOL ports upstream of our router, we map directly to
|
// 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).
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,6 +868,13 @@ int Run()
|
|||||||
|
|
||||||
ResetLogFile();
|
ResetLogFile();
|
||||||
|
|
||||||
|
// 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
|
// Create the thread to watch for GameStream state changes
|
||||||
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="miss.cpp" />
|
<ClCompile Include="miss.cpp" />
|
||||||
<ClCompile Include="pcp.cpp" />
|
<ClCompile Include="pcp.cpp" />
|
||||||
|
<ClCompile Include="relay.cpp" />
|
||||||
<ClCompile Include="tracer.cpp" />
|
<ClCompile Include="tracer.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -170,6 +171,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\version.h" />
|
<ClInclude Include="..\version.h" />
|
||||||
|
<ClInclude Include="relay.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
<ClCompile Include="pcp.cpp">
|
<ClCompile Include="pcp.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="relay.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="miss.rc">
|
<ResourceCompile Include="miss.rc">
|
||||||
@@ -34,5 +37,8 @@
|
|||||||
<ClInclude Include="..\version.h">
|
<ClInclude Include="..\version.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="relay.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
#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;
|
||||||
|
fd_set fds;
|
||||||
|
int err;
|
||||||
|
SOCKADDR_IN lastRemoteAddr;
|
||||||
|
|
||||||
|
RtlZeroMemory(&lastRemoteAddr, sizeof(lastRemoteAddr));
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
char buffer[4096];
|
||||||
|
SOCKADDR_IN sourceAddr;
|
||||||
|
int sourceAddrLen;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
|
||||||
|
FD_SET(tuple->socket, &fds);
|
||||||
|
|
||||||
|
err = select(0, &fds, NULL, NULL, NULL);
|
||||||
|
if (err <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceAddrLen = sizeof(sourceAddr);
|
||||||
|
err = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
||||||
|
if (err == SOCKET_ERROR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKADDR_IN destinationAddr;
|
||||||
|
if (RtlEqualMemory(&sourceAddr.sin_addr, &in4addr_loopback, sizeof(sourceAddr.sin_addr))) {
|
||||||
|
// 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 = htons(tuple->port);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendto(tuple->socket, buffer, err, 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);
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <wtsapi32.h>
|
#include <wtsapi32.h>
|
||||||
#include <powerbase.h>
|
#include <powerbase.h>
|
||||||
#include <VersionHelpers.h>
|
#include <VersionHelpers.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
|
||||||
#pragma comment(lib, "miniupnpc.lib")
|
#pragma comment(lib, "miniupnpc.lib")
|
||||||
#pragma comment(lib, "libnatpmp.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()
|
bool IsConsoleSessionActive()
|
||||||
{
|
{
|
||||||
PWTS_SESSION_INFO_1 sessionInfo;
|
PWTS_SESSION_INFO_1 sessionInfo;
|
||||||
@@ -1203,6 +1226,12 @@ int main(int argc, char* argv[])
|
|||||||
return -1;
|
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()) {
|
if (!IsConsoleSessionActive()) {
|
||||||
DisplayMessage("The system display is currently locked. You must sign in to your PC again to use GameStream.\n\n"
|
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.",
|
"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
|
#pragma once
|
||||||
|
|
||||||
#define VER_VERSION 5,1,0,0
|
#define VER_VERSION 5,2,0,0
|
||||||
#define VER_VERSION_STR "5.1.0.0"
|
#define VER_VERSION_STR "5.2.0.0"
|
||||||
|
|
||||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||||
|
|||||||
Reference in New Issue
Block a user