Permalink
Browse files

* Detect multiple instance of PPSSPP running and automatically change…

… the IP and MAC of the 2nd (or more) instance of PPSSPP to allow multiplayer on the same computer.

* Added Port-shifting support for games that use privileged ports (< 1024), all players need to have the same port offset.
  TODO: Add GUI for Port Offset to be configurable by players.
  • Loading branch information...
ANR2ME committed Oct 29, 2015
1 parent ab0adc1 commit afa5eb89f1ff061d2d2e5aefc6bae0d564ff68fd
Showing with 313 additions and 30 deletions.
  1. +1 −0 .gitignore
  2. +63 −5 Core/HLE/proAdhoc.cpp
  3. +4 −0 Core/HLE/proAdhoc.h
  4. +43 −8 Core/HLE/proAdhocServer.cpp
  5. +177 −1 Core/HLE/sceNet.cpp
  6. +25 −16 Core/HLE/sceNetAdhoc.cpp
View
@@ -84,3 +84,4 @@ debian/ppsspp/
# YouCompleteMe file
.ycm_extra_conf.pyc
PPSSPPDebug.exe.manifest
View
@@ -21,6 +21,10 @@
// This is a direct port of Coldbird's code from http://code.google.com/p/aemu/
// All credit goes to him!
#if !defined(_MSC_VER)
#include <netdb.h>
#include <unistd.h>
#endif
#include <cstring>
#include "util/text/parsers.h"
#include "Core/Core.h"
@@ -31,6 +35,10 @@
#include "proAdhoc.h"
#include "i18n/i18n.h"
uint16_t portOffset = 0; //5000
bool isLocalServer = false;
uint8_t PPSSPP_ID = 0;
sockaddr localIP;
uint32_t fakePoolSize = 0;
SceNetAdhocMatchingContext * contexts = NULL;
int one = 1;
@@ -54,6 +62,7 @@ recursive_mutex peerlock;
SceNetAdhocPdpStat * pdp[255];
SceNetAdhocPtpStat * ptp[255];
int isLocalMAC(const SceNetEtherAddr * addr) {
SceNetEtherAddr saddr;
getLocalMac(&saddr);
@@ -1289,22 +1298,38 @@ int getActivePeerCount(void) {
int getLocalIp(sockaddr_in * SocketAddress){
#if defined(_MSC_VER)
// Get local host name
char szHostName[128] = "";
char szHostName[256] = ""; //128
if(::gethostname(szHostName, sizeof(szHostName))) {
// Error handling
}
// Get local IP addresses
struct hostent *pHost = 0;
struct hostent *pHost = 0;
pHost = ::gethostbyname(szHostName);
if(pHost) {
memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length);
if (/*PPSSPP_ID > 1 && SocketAddress->sin_addr.S_un.S_un_b.s_b1 == 0x7f*/isLocalServer) {
//SocketAddress->sin_addr.S_un.S_un_b.s_b4 = PPSSPP_ID;
SocketAddress->sin_addr = ((sockaddr_in *)&localIP)->sin_addr;
}
return 0;
}
return -1;
#else
SocketAddress->sin_addr.s_addr = inet_addr("192.168.12.1");
return 0;
char szHostName[256] = "";
gethostname(szHostName, sizeof(szHostName));
struct hostent *pHost = 0;
pHost = gethostbyname(szHostName);
if (pHost) {
memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length);
if (/*PPSSPP_ID > 1 && SocketAddress->sin_addr.S_un.S_un_b.s_b1 == 0x7f*/isLocalServer) {
//SocketAddress->sin_addr.S_un.S_un_b.s_b4 = PPSSPP_ID;
SocketAddress->sin_addr = ((sockaddr_in *)&localIP)->sin_addr;
}
return 0;
}
//SocketAddress->sin_addr.s_addr = inet_addr("127.0.0.1"); //192.168.12.1
return -1;
#endif
}
@@ -1313,12 +1338,20 @@ uint32_t getLocalIp(int sock) {
localAddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrLen = sizeof(localAddr);
getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
if (/*PPSSPP_ID > 1 && localAddr.sin_addr.S_un.S_un_b.s_b1 == 0x7f*/isLocalServer) {
//localAddr.sin_addr.S_un.S_un_b.s_b4 = PPSSPP_ID;
localAddr.sin_addr = ((sockaddr_in *)&localIP)->sin_addr;
}
return localAddr.sin_addr.s_addr;
}
void getLocalMac(SceNetEtherAddr * addr){
// Read MAC Address from config
uint8_t mac[ETHER_ADDR_LEN] = {0};
if (PPSSPP_ID > 1) {
memset(&mac, PPSSPP_ID, sizeof(mac));
}
else
if (!ParseMacAddress(g_Config.sMACAddress.c_str(), mac)) {
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
}
@@ -1430,7 +1463,32 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
break;
}
}
freeaddrinfo(resultAddr);
// If Server is at localhost Try to Bind socket to specific adapter before connecting
// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
if (serverIp.S_un.S_un_b.s_b1 == 0x7f) {
int on = 1;
setsockopt(metasocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
setsockopt(metasocket, SOL_SOCKET, SO_DONTROUTE, (const char*)&on, sizeof(on));
// Prepare Local Address Information
/*
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr = serverIp;
local.sin_addr.S_un.S_un_b.s_b4 = PPSSPP_ID;
//local.sin_port = 0;
*/
((struct sockaddr_in *)&localIP)->sin_port = 0;
// Bind Local Address to Socket
iResult = bind(metasocket, (struct sockaddr *)&localIP, sizeof(sockaddr));
if (iResult == SOCKET_ERROR) {
ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", inet_ntoa(((struct sockaddr_in *)&localIP)->sin_addr), iResult);
}
//serverIp = local.sin_addr;
}
memset(&parameter, 0, sizeof(parameter));
strcpy((char *)&parameter.nickname.data, g_Config.sNickName.c_str());
parameter.channel = 1; // Fake Channel 1
@@ -1479,7 +1537,7 @@ bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {
getLocalIp(&addr);
uint32_t localIp = addr.sin_addr.s_addr;
if (ip == localIp){
if (ip == localIp || ip == ((sockaddr_in *)&localIP)->sin_addr.s_addr){
getLocalMac(mac);
return true;
}
View
@@ -795,6 +795,10 @@ extern SceNetAdhocPdpStat * pdp[255];
extern SceNetAdhocPtpStat * ptp[255];
extern std::map<int, AdhocctlHandler> adhocctlHandlers;
extern uint16_t portOffset;
extern bool isLocalServer;
extern uint8_t PPSSPP_ID;
extern sockaddr localIP;
extern uint32_t fakePoolSize;
extern SceNetAdhocMatchingContext * contexts;
extern int one;
@@ -94,13 +94,13 @@ void login_user_stream(int fd, uint32_t ip)
// Enough Space available
if(_db_user_count < SERVER_USER_MAXIMUM)
{
// Check IP Duplication
// Check IP Duplication as current AdhocServer doesn't receives/broadcasts binded port information having more than 1 players using the same IP will only cause communication interference (sometimes clients bind to specific port on some games) since Reusable port will share the socket buffer when one already read the data the other can't read it anymore (no longer in buffer)
SceNetAdhocctlUserNode * u = _db_user;
while(u != NULL && u->resolver.ip != ip) u = u->next;
if (u != NULL) { // IP Already existed
uint8_t * ip4 = (uint8_t *)&u->resolver.ip;
INFO_LOG(SCENET, "AdhocServer: Already Existing IP: %u.%u.%u.%u\n", ip4[0], ip4[1], ip4[2], ip4[3]);
WARN_LOG(SCENET, "AdhocServer: Already Existing IP: %u.%u.%u.%u\n", ip4[0], ip4[1], ip4[2], ip4[3]);
}
// Unique IP Address
@@ -145,7 +145,7 @@ void login_user_stream(int fd, uint32_t ip)
}
}
// Duplicate IP, Allocation Error or not enough space - Close Stream
// Duplicate IP/MAC, Allocation Error or not enough space - Close Stream
closesocket(fd);
}
@@ -169,6 +169,30 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S
// Valid Packet Data
if(valid_product_code == 1 && memcmp(&data->mac, "\xFF\xFF\xFF\xFF\xFF\xFF", sizeof(data->mac)) != 0 && memcmp(&data->mac, "\x00\x00\x00\x00\x00\x00", sizeof(data->mac)) != 0 && data->name.data[0] != 0)
{
// Check for duplicated MAC as most games identify Players by MAC
SceNetAdhocctlUserNode * u = _db_user;
while (u != NULL && !IsMatch(u->resolver.mac, data->mac)) u = u->next;
if (u != NULL) { // MAC Already existed
/*
u32_le sip = user->resolver.ip;
// 127.0.0.1 should be replaced with LAN/WAN IP whenever available to make sure remote players can communicate with current (local) player
// It might be better if the client connects to AdhocServer using the correct interface instead of automatically changing the IP which might not be accurate on multihomed computer.
if (sip == 0x0100007f) {
char str[256];
gethostname(str, 256);
u8 *pip = (u8*)&sip;
struct hostent *pHost = 0;
pHost = gethostbyname(str);
if (pHost->h_addrtype == AF_INET && pHost->h_addr_list[0] != NULL) pip = (u8*)pHost->h_addr_list[0];
user->resolver.ip = *(u32_le*)pip;
WARN_LOG(SCENET, "AdhocServer: Replacing IP %u.%u.%u.%u with %u.%u.%u.%u", ((u8*)&sip)[0], ((u8*)&sip)[1], ((u8*)&sip)[2], ((u8*)&sip)[3], pip[0], pip[1], pip[2], pip[3]);
}
*/
uint8_t * ip4 = (uint8_t *)&u->resolver.ip;
WARN_LOG(SCENET, "AdhocServer: Already Existing MAC: %02X:%02X:%02X:%02X:%02X:%02X [%u.%u.%u.%u]\n", data->mac.data[0], data->mac.data[1], data->mac.data[2], data->mac.data[3], data->mac.data[4], data->mac.data[5], ip4[0], ip4[1], ip4[2], ip4[3]);
}
// Game Product Override
game_product_override(&data->game);
@@ -1385,9 +1409,16 @@ int create_listen_socket(uint16_t port)
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_addr.s_addr = INADDR_ANY; // bind to all network adapter
local.sin_port = htons(port);
//Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. Doesn't work well when PPSSPP_ID reseted everytime emulation restarted.
/*
if (PPSSPP_ID > 1) {
local.sin_addr = ((sockaddr_in *)&localIP)->sin_addr;
}
*/
// Bind Local Address to Socket
int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local));
@@ -1459,14 +1490,18 @@ int server_loop(int server)
// Login User (Stream)
if (loginresult != -1) {
u32_le sip = addr.sin_addr.s_addr;
if (sip == 0x0100007f) { //127.0.0.1 should be replaced with LAN/WAN IP whenever available
char str[100];
gethostname(str, 100);
/*
if (sip == 0x0100007f) { //127.0.0.1 should be replaced with LAN/WAN IP whenever available to make sure remote players can communicate with current (local) player
char str[256];
gethostname(str, 256);
u8 *pip = (u8*)&sip;
if (gethostbyname(str)->h_addrtype == AF_INET && gethostbyname(str)->h_addr_list[0] != NULL) pip = (u8*)gethostbyname(str)->h_addr_list[0];
struct hostent *pHost = 0;
pHost = gethostbyname(str);
if (pHost->h_addrtype == AF_INET && pHost->h_addr_list[0] != NULL) pip = (u8*)pHost->h_addr_list[0];
sip = *(u32_le*)pip;
WARN_LOG(SCENET, "AdhocServer: Replacing IP %s with %u.%u.%u.%u", inet_ntoa(addr.sin_addr), pip[0], pip[1], pip[2], pip[3]);
}
*/
login_user_stream(loginresult, sip);
}
} while(loginresult != -1);
Oops, something went wrong.

0 comments on commit afa5eb8

Please sign in to comment.