Files
2025-11-24 14:19:51 +05:30

709 lines
21 KiB
C++

/*
* Original work: Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2016-2018, SLikeSoft UG (haftungsbeschränkt)
*
* This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
* license found in the license.txt file in the root directory of this source tree.
*/
#include "slikenet/peerinterface.h"
#include "slikenet/sleep.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits> // used for std::numeric_limits
#include "slikenet/Kbhit.h"
#include "slikenet/MessageIdentifiers.h"
#include "slikenet/BitStream.h"
#include "slikenet/sleep.h"
#include "slikenet/UDPProxyServer.h"
#include "slikenet/UDPProxyCoordinator.h"
#include "slikenet/NatPunchthroughServer.h"
#include "slikenet/NatTypeDetectionServer.h"
#include "slikenet/SocketLayer.h"
#include "slikenet/Getche.h"
#include "slikenet/Gets.h"
#include "CloudServerHelper.h"
#include "slikenet/CloudClient.h"
#include "slikenet/statistics.h"
#include "slikenet/RelayPlugin.h"
#include "slikenet/linux_adapter.h"
#include "slikenet/osx_adapter.h"
//#define VERBOSE_LOGGING
using namespace SLNet;
enum FeatureSupport
{
SUPPORTED,
UNSUPPORTED,
QUERY
};
enum FeatureList
{
NAT_TYPE_DETECTION_SERVER,
NAT_PUNCHTHROUGH_SERVER,
RELAY_PLUGIN,
UDP_PROXY_COORDINATOR,
UDP_PROXY_SERVER,
CLOUD_SERVER,
FEATURE_LIST_COUNT,
};
static unsigned short DEFAULT_RAKPEER_PORT=61111;
#define NatTypeDetectionServerFramework_Supported QUERY
#define NatPunchthroughServerFramework_Supported QUERY
#define RelayPlugin_Supported QUERY
#define UDPProxyCoordinatorFramework_Supported UNSUPPORTED
#define UDPProxyServerFramework_Supported UNSUPPORTED
#define CloudServerFramework_Supported QUERY
struct SampleFramework
{
virtual const char * QueryName(void)=0;
virtual const char * QueryRequirements(void)=0;
virtual const char * QueryFunction(void)=0;
virtual void Init(SLNet::RakPeerInterface *rakPeer)=0;
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)=0;
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)=0;
FeatureSupport isSupported;
};
struct NatTypeDetectionServerFramework : public SampleFramework
{
NatTypeDetectionServerFramework() {isSupported=NatTypeDetectionServerFramework_Supported; ntds=0;}
virtual const char * QueryName(void) {return "NatTypeDetectionServer";}
virtual const char * QueryRequirements(void) {return "Requires 4 IP addresses";}
virtual const char * QueryFunction(void) {return "Determines router type to filter by connectable systems.\nOne instance needed, multiple instances may exist to spread workload.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
ntds = new NatTypeDetectionServer;
rakPeer->AttachPlugin(ntds);
if (rakPeer->GetNumberOfAddresses() < 4)
{
printf("Failed. Not enough IP addresses to bind to.\n");
rakPeer->DetachPlugin(ntds);
delete ntds;
ntds=0;
isSupported=UNSUPPORTED;
return;
}
ntds->Startup(rakPeer->GetLocalIP(1), rakPeer->GetLocalIP(2), rakPeer->GetLocalIP(3));
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
// unused parameters
(void)rakPeer;
(void)packet;
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (ntds)
{
rakPeer->DetachPlugin(ntds);
delete ntds;
}
ntds=0;
}
NatTypeDetectionServer *ntds;
};
struct NatPunchthroughServerFramework : public SampleFramework, public NatPunchthroughServerDebugInterface_Printf
{
NatPunchthroughServerFramework() {isSupported=NatPunchthroughServerFramework_Supported; nps=0;}
virtual const char * QueryName(void) {return "NatPunchthroughServerFramework";}
virtual const char * QueryRequirements(void) {return "None";}
virtual const char * QueryFunction(void) {return "Coordinates NATPunchthroughClient.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
nps = new NatPunchthroughServer;
rakPeer->AttachPlugin(nps);
#ifdef VERBOSE_LOGGING
nps->SetDebugInterface(this);
#endif
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
// unused parameters
(void)rakPeer;
(void)packet;
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (nps)
{
rakPeer->DetachPlugin(nps);
delete nps;
}
nps=0;
}
NatPunchthroughServer *nps;
};
struct RelayPluginFramework : public SampleFramework
{
RelayPluginFramework() {relayPlugin=nullptr;isSupported=RelayPlugin_Supported;}
virtual const char * QueryName(void) {return "RelayPlugin";}
virtual const char * QueryRequirements(void) {return "None.";}
virtual const char * QueryFunction(void) {return "Relays messages between named connections.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
relayPlugin = new RelayPlugin;
rakPeer->AttachPlugin(relayPlugin);
relayPlugin->SetAcceptAddParticipantRequests(true);
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
// unused parameters
(void)rakPeer;
(void)packet;
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (relayPlugin)
{
rakPeer->DetachPlugin(relayPlugin);
delete relayPlugin;
relayPlugin=0;
}
}
RelayPlugin *relayPlugin;
};
struct UDPProxyCoordinatorFramework : public SampleFramework
{
UDPProxyCoordinatorFramework() {udppc=0; isSupported=UDPProxyCoordinatorFramework_Supported;}
virtual const char * QueryName(void) {return "UDPProxyCoordinator";}
virtual const char * QueryRequirements(void) {return "Bandwidth to handle a few hundred bytes per game session.";}
virtual const char * QueryFunction(void) {return "Coordinates UDPProxyClient to find available UDPProxyServer.\nExactly one instance required.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
udppc = new UDPProxyCoordinator;
rakPeer->AttachPlugin(udppc);
char password[512];
printf("Create password for UDPProxyCoordinator: ");
Gets(password,sizeof(password));
if (password[0]==0)
{
password[0]='a';
password[1]=0;
}
udppc->SetRemoteLoginPassword(password);
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
// unused parameters
(void)rakPeer;
(void)packet;
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (udppc)
{
rakPeer->DetachPlugin(udppc);
delete udppc;
udppc=0;
}
}
UDPProxyCoordinator *udppc;
};
SystemAddress SelectAmongConnectedSystems(SLNet::RakPeerInterface *rakPeer, const char *hostName)
{
DataStructures::List<SystemAddress> addresses;
DataStructures::List<RakNetGUID> guids;
rakPeer->GetSystemList(addresses, guids);
if (addresses.Size()==0)
{
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
if (addresses.Size()>1)
{
printf("Select IP address for %s.\n", hostName);
char buff[64];
for (unsigned int i=0; i < addresses.Size(); i++)
{
addresses[i].ToString(true, buff, static_cast<size_t>(64));
printf("%i. %s\n", i+1, buff);
}
Gets(buff,sizeof(buff));
if (buff[0]==0)
{
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
unsigned int idx = atoi(buff);
if (idx<=0 || idx > addresses.Size())
{
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
return addresses[idx-1];
}
else
return addresses[0];
};
SystemAddress ConnectBlocking(SLNet::RakPeerInterface *rakPeer, const char *hostName)
{
SystemAddress returnvalue = SLNet::UNASSIGNED_SYSTEM_ADDRESS;
char ipAddr[64];
printf("Enter IP of system %s is running on: ", hostName);
Gets(ipAddr,sizeof(ipAddr));
if (ipAddr[0]==0)
{
printf("Failed. Not connected to %s.\n", hostName);
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
char port[64];
printf("Enter port of system %s is running on: ", hostName);
Gets(port, sizeof(port));
if (port[0]==0)
{
printf("Failed. Not connected to %s.\n", hostName);
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
const int intPort = atoi(port);
if ((intPort < 0) || (intPort > std::numeric_limits<unsigned short>::max())) {
printf("Failed. Specified port %d is outside valid bounds [0, %u]", intPort, std::numeric_limits<unsigned short>::max());
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
if (rakPeer->Connect(ipAddr, static_cast<unsigned short>(intPort), 0, 0)!= SLNet::CONNECTION_ATTEMPT_STARTED)
{
printf("Failed connect call for %s.\n", hostName);
return SLNet::UNASSIGNED_SYSTEM_ADDRESS;
}
printf("Connecting...\n");
SLNet::Packet *packet;
// #med - review --- at least we'd add a sleep interval here - also review whether the behavior is correct to only check the very first received packet (old RakNet code was bogus in this regards)
do {
packet = rakPeer->Receive();
} while (packet == nullptr);
if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED)
returnvalue = packet->systemAddress;
rakPeer->DeallocatePacket(packet);
return returnvalue;
}
struct UDPProxyServerFramework : public SampleFramework, public UDPProxyServerResultHandler
{
UDPProxyServerFramework() {udpps=0; isSupported=UDPProxyServerFramework_Supported;}
virtual const char * QueryName(void) {return "UDPProxyServer";}
virtual const char * QueryRequirements(void) {return "Bandwidth to handle forwarded game traffic.";}
virtual const char * QueryFunction(void) {return "Allows game clients to forward network traffic transparently.\nOne or more instances required, can be added at runtime.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
printf("Logging into UDPProxyCoordinator...\n");
SystemAddress coordinatorAddress=SelectAmongConnectedSystems(rakPeer, "UDPProxyCoordinator");
if (coordinatorAddress== SLNet::UNASSIGNED_SYSTEM_ADDRESS)
{
printf("Warning: RakPeer is not currently connected to any system.\nEnter option:\n(1). UDPProxyCoordinator is on localhost\n(2). Connect to a remote system\n(3). Fail.\nOption: ");
int ch=_getche();
printf("\n");
if (ch=='1' || ch==13) // 13 is just pressing return
{
coordinatorAddress=rakPeer->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS,0);
}
else if (ch=='2')
{
coordinatorAddress=ConnectBlocking(rakPeer, "UDPProxyCoordinator");
if (coordinatorAddress== SLNet::UNASSIGNED_SYSTEM_ADDRESS)
{
printf("Failed to connect.\n");
isSupported=QUERY;
return;
}
}
else
{
printf("Failed. Not connected to UDPProxyCoordinator.\n");
isSupported=QUERY;
return;
}
}
char password[512];
printf("Enter password used with UDPProxyCoordinator: ");
Gets(password,sizeof(password));
if (password[0]==0)
{
password[0]='a';
password[1]=0;
}
udpps = new UDPProxyServer;
udpps->SetResultHandler(this);
rakPeer->AttachPlugin(udpps);
if (udpps->LoginToCoordinator(password, coordinatorAddress)==false)
{
printf("LoginToCoordinator call failed.\n");
isSupported=QUERY;
rakPeer->DetachPlugin(udpps);
delete udpps;
udpps=0;
return;
}
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
// unused parameters
(void)rakPeer;
(void)packet;
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (udpps)
{
rakPeer->DetachPlugin(udpps);
delete udpps;
udpps=0;
}
}
virtual void OnLoginSuccess(SLNet::RakString usedPassword, SLNet::UDPProxyServer *proxyServerPlugin)
{
// unused parameters
(void)proxyServerPlugin;
printf("%s logged into UDPProxyCoordinator.\n", QueryName());
}
virtual void OnAlreadyLoggedIn(SLNet::RakString usedPassword, SLNet::UDPProxyServer *proxyServerPlugin)
{
// unused parameters
(void)proxyServerPlugin;
printf("%s already logged into UDPProxyCoordinator.\n", QueryName());
}
virtual void OnNoPasswordSet(SLNet::RakString usedPassword, SLNet::UDPProxyServer *proxyServerPlugin)
{
// unused parameters
(void)proxyServerPlugin;
printf("%s failed login to UDPProxyCoordinator. No password set.\n", QueryName());
isSupported=QUERY;
delete udpps;
udpps=0;
}
virtual void OnWrongPassword(SLNet::RakString usedPassword, SLNet::UDPProxyServer *proxyServerPlugin)
{
// unused parameters
(void)proxyServerPlugin;
printf("%s failed login to UDPProxyCoordinator. %s was the wrong password.\n", QueryName(), usedPassword.C_String());
isSupported=QUERY;
delete udpps;
udpps=0;
}
UDPProxyServer *udpps;
};
struct CloudServerFramework : public SampleFramework
{
CloudServerFramework() {cloudServer=nullptr;isSupported=CloudServerFramework_Supported;}
virtual const char * QueryName(void) {return "CloudServer";}
virtual const char * QueryRequirements(void) {return "None.";}
virtual const char * QueryFunction(void) {return "Single instance cloud server that maintains connection counts\nUseful as a directory server to find other client instances.";}
virtual void Init(SLNet::RakPeerInterface *rakPeer)
{
if (isSupported==SUPPORTED)
{
cloudServer = new CloudServer;
rakPeer->AttachPlugin(cloudServer);
cloudClient = new CloudClient;
rakPeer->AttachPlugin(cloudClient);
cloudServerHelperFilter = new CloudServerHelperFilter;
cloudServer->AddQueryFilter(cloudServerHelperFilter);
cloudServer->SetMaxUploadBytesPerClient(65535);
cloudServerHelper.OnConnectionCountChange(rakPeer, cloudClient);
}
}
virtual void ProcessPacket(SLNet::RakPeerInterface *rakPeer, Packet *packet)
{
if (isSupported!=SUPPORTED)
return;
switch (packet->data[0])
{
case ID_NEW_INCOMING_CONNECTION:
#ifdef VERBOSE_LOGGING
printf("Got connection to %s\n", packet->systemAddress.ToString(true));
#endif
cloudServerHelper.OnConnectionCountChange(rakPeer, cloudClient);
break;
case ID_CONNECTION_LOST:
case ID_DISCONNECTION_NOTIFICATION:
#ifdef VERBOSE_LOGGING
printf("Lost connection to %s\n", packet->systemAddress.ToString(true));
#endif
cloudServerHelper.OnConnectionCountChange(rakPeer, cloudClient);
break;
}
}
virtual void Shutdown(SLNet::RakPeerInterface *rakPeer)
{
if (cloudServer)
{
rakPeer->DetachPlugin(cloudServer);
delete cloudServer;
cloudServer=0;
rakPeer->DetachPlugin(cloudClient);
delete cloudClient;
cloudClient=0;
delete cloudServerHelperFilter;
cloudServerHelperFilter=0;
}
}
SLNet::CloudServer *cloudServer;
SLNet::CloudClient *cloudClient;
SLNet::CloudServerHelperFilter *cloudServerHelperFilter;
SLNet::CloudServerHelper cloudServerHelper;
};
int main(int argc, char **argv)
{
SLNet::RakPeerInterface *rakPeer= SLNet::RakPeerInterface::GetInstance();
SystemAddress ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ];
printf("IPs:\n");
unsigned int i;
for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) {
ipList[i]=rakPeer->GetLocalIP(i);
if (ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS)
printf("%i. %s\n", i+1, ipList[i].ToString(false));
else
break;
}
if (i == 0 && argc <= 3) {
printf("Could not determine any local IP address.\n");
return 3;
}
// If RakPeer is started on 2 IP addresses, NATPunchthroughServer supports port stride detection, improving success rate
int sdLen=1;
SLNet::SocketDescriptor sd[2];
if (argc>1)
{
const int intPeerPort = atoi(argv[1]);
if ((intPeerPort < 0) || (intPeerPort > std::numeric_limits<unsigned short>::max())) {
printf("Specified peer port %d is outside valid bounds [0, %u]", intPeerPort, std::numeric_limits<unsigned short>::max());
return 2;
}
DEFAULT_RAKPEER_PORT = static_cast<unsigned short>(intPeerPort);
}
// set the first IP address
sd[0].port = DEFAULT_RAKPEER_PORT;
// #med - improve the logic here to simplify the handling...
if (argc > 2)
strcpy_s(sd[0].hostAddress, argv[2]);
// #high - improve determining the proper IP addresses
// - filter between IPv4/IPv6 and only use either of these
// - fallback to other IP addresses, if a given one failed to be bound
// allow enforcing single IP address mode by specifying second/third argument to the same IP address
if ((i >= 2 && argc <= 3) || (argc > 3)) {
const char *ipAddress1 = (argc > 2) ? argv[2] : ipList[0].ToString(false);
const char *ipAddress2 = (argc > 3) ? argv[3] : ipList[1].ToString(false);
strcpy_s(sd[0].hostAddress, ipAddress1);
sd[1].port = DEFAULT_RAKPEER_PORT+1;
strcpy_s(sd[1].hostAddress, ipAddress2);
printf("Dual IP address mode.\nFirst IP Address: '%s' (port: %u)\nSecond IP Address: '%s' (port: %u)\n", ipAddress1, sd[0].port, ipAddress2, sd[1].port);
sdLen = 2;
}
else {
printf("Single IP address mode.\nUsing port %i\n", sd[0].port);
}
const StartupResult success = rakPeer->Startup(8096, sd, sdLen);
if (success != SLNet::RAKNET_STARTED)
{
printf("Failed to start rakPeer! Quitting - error code: %d\n", success);
SLNet::RakPeerInterface::DestroyInstance(rakPeer);
return 1;
}
rakPeer->SetTimeoutTime(5000, UNASSIGNED_SYSTEM_ADDRESS);
printf("Started on %s\n\n", rakPeer->GetMyBoundAddress().ToString(true));
rakPeer->SetMaximumIncomingConnections(8096);
SampleFramework *samples[FEATURE_LIST_COUNT];
i=0;
samples[i++] = new NatTypeDetectionServerFramework;
samples[i++] = new NatPunchthroughServerFramework;
samples[i++] = new RelayPluginFramework;
samples[i++] = new UDPProxyCoordinatorFramework;
samples[i++] = new UDPProxyServerFramework;
samples[i++] = new CloudServerFramework;
assert(i==FEATURE_LIST_COUNT);
bool isFirstPrint=true;
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
if (samples[i]->isSupported==QUERY)
{
if (isFirstPrint)
{
printf("NAT traversal server.\nSee http://www.dx.net/raknet_dx.php for discounted server hosting\nSelect which features to support.\n");
isFirstPrint=false;
}
printf("\n%s\nRequirements: %s\nDescription: %s\n", samples[i]->QueryName(), samples[i]->QueryRequirements(), samples[i]->QueryFunction());
printf("Support %s? (y/n): ", samples[i]->QueryName());
int supported=_getche();
if (supported=='y' || supported=='Y' || supported==13) // 13 is just pressing return
{
samples[i]->isSupported=SUPPORTED;
}
printf("\n");
}
}
printf("\n");
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
if (samples[i]->isSupported==SUPPORTED)
{
printf("Starting %s...\n", samples[i]->QueryName());
samples[i]->Init(rakPeer);
if (samples[i]->isSupported!=SUPPORTED)
{
printf("Failed to start %s.", samples[i]->QueryName());
if (samples[i]->isSupported==QUERY)
{
printf(" Retry? (y/n): ");
int supported=_getche();
if (supported=='y' || supported=='Y')
{
samples[i]->isSupported=SUPPORTED;
i--;
}
}
else
printf("\n");
printf("\n");
}
else
printf("Success.\n\n");
}
}
bool anySupported=false;
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
if (samples[i]->isSupported==SUPPORTED)
{
anySupported=true;
break;
}
}
if (anySupported==false)
{
printf("No features supported! Quitting.\n");
rakPeer->Shutdown(100);
SLNet::RakPeerInterface::DestroyInstance(rakPeer);
return 1;
}
bool firstComma=true;
printf("Supported features: ");
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
if (samples[i]->isSupported==SUPPORTED)
{
if (firstComma==false)
printf(", ");
else
firstComma=false;
printf("%s", samples[i]->QueryName());
}
}
printf("\nEntering update loop. Press 'q' to quit.\n");
SLNet::Packet *packet;
bool quit=false;
while (!quit)
{
for (packet=rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet=rakPeer->Receive())
{
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
samples[i]->ProcessPacket(rakPeer, packet);
}
}
if (_kbhit())
{
int ch = _getch();
if (ch=='q')
{
quit=true;
}
else if (ch==' ')
{
RakNetStatistics rns;
char message[2048];
bool hasStatistics = rakPeer->GetStatistics(0, &rns);
if (hasStatistics)
{
StatisticsToString(&rns, message, 2048, 2);
printf("SYSTEM 0:\n%s\n", message);
memset(&rns, 0, sizeof(RakNetStatistics));
rakPeer->GetStatistics(UNASSIGNED_SYSTEM_ADDRESS, &rns);
StatisticsToString(&rns, message, 2048, 2);
printf("STAT SUM:\n%s\n", message);
}
else
{
printf("No system 0\n");
}
DataStructures::List<SystemAddress> addresses;
DataStructures::List<RakNetGUID> guids;
rakPeer->GetSystemList(addresses, guids);
printf("%i systems connected\n", addresses.Size());
}
}
RakSleep(30);
}
printf("Quitting.\n");
for (i=0; i < FEATURE_LIST_COUNT; i++)
{
samples[i]->Shutdown(rakPeer);
}
rakPeer->Shutdown(100);
SLNet::RakPeerInterface::DestroyInstance(rakPeer);
return 0;
}