mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
205 lines
6.6 KiB
C
205 lines
6.6 KiB
C
#include "socket.h"
|
|
#include "domain_res.h"
|
|
#include <stdio.h>
|
|
#include "pc/configfile.h"
|
|
#include "pc/debuglog.h"
|
|
#include "pc/djui/djui.h"
|
|
|
|
static SOCKET sCurSocket = INVALID_SOCKET;
|
|
static struct sockaddr_in sAddr[MAX_PLAYERS] = { 0 };
|
|
|
|
static int socket_bind(SOCKET socket, unsigned int port) {
|
|
struct sockaddr_in rxAddr;
|
|
rxAddr.sin_family = AF_INET;
|
|
rxAddr.sin_port = htons(port);
|
|
rxAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
int rc = bind(socket, (SOCKADDR*)&rxAddr, sizeof(rxAddr));
|
|
if (rc != 0) {
|
|
LOG_ERROR("bind failed with error %d", SOCKET_LAST_ERROR);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int socket_send(SOCKET socket, struct sockaddr_in* addr, u8* buffer, u16 bufferLength) {
|
|
int addrSize = sizeof(struct sockaddr_in);
|
|
int rc = sendto(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)addr, addrSize);
|
|
if (rc != SOCKET_ERROR) { return NO_ERROR; }
|
|
|
|
int error = SOCKET_LAST_ERROR;
|
|
if (error == SOCKET_EWOULDBLOCK) { return NO_ERROR; }
|
|
|
|
LOG_ERROR("sendto failed with error: %d", error);
|
|
return rc;
|
|
}
|
|
|
|
static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer, u16 bufferLength, u16* receiveLength, u8* localIndex) {
|
|
*receiveLength = 0;
|
|
|
|
RX_ADDR_SIZE_TYPE rxAddrSize = sizeof(struct sockaddr_in);
|
|
int rc = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)rxAddr, &rxAddrSize);
|
|
|
|
for (int i = 1; i < MAX_PLAYERS; i++) {
|
|
if (memcmp(rxAddr, &sAddr[i], sizeof(struct sockaddr_in)) == 0) {
|
|
*localIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rc == SOCKET_ERROR) {
|
|
int error = SOCKET_LAST_ERROR;
|
|
if (error != SOCKET_EWOULDBLOCK && error != SOCKET_ECONNRESET) {
|
|
LOG_ERROR("recvfrom failed with error %d", SOCKET_LAST_ERROR);
|
|
}
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
*receiveLength = rc;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
static bool ns_socket_initialize(enum NetworkType networkType, UNUSED bool reconnecting) {
|
|
|
|
// sanity check port
|
|
unsigned int port = (networkType == NT_CLIENT) ? configJoinPort : configHostPort;
|
|
if (port == 0) { port = DEFAULT_PORT; }
|
|
|
|
// create a receiver socket to receive datagrams
|
|
sCurSocket = socket_initialize();
|
|
if (sCurSocket == INVALID_SOCKET) { return false; }
|
|
|
|
// connect
|
|
if (networkType == NT_SERVER) {
|
|
int reuse = 1;
|
|
if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) {
|
|
LOG_ERROR("setsockopt(SO_REUSEADDR) failed");
|
|
}
|
|
|
|
#ifdef SO_REUSEPORT
|
|
if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) {
|
|
LOG_ERROR("setsockopt(SO_REUSEPORT) failed");
|
|
}
|
|
#endif
|
|
|
|
// bind the socket to any address and the specified port.
|
|
int rc = socket_bind(sCurSocket, port);
|
|
if (rc != NO_ERROR) { return false; }
|
|
LOG_INFO("bound to port %u", port);
|
|
} else {
|
|
// save the port to send to
|
|
sAddr[0].sin_family = AF_INET;
|
|
sAddr[0].sin_port = htons(port);
|
|
domain_resolution();
|
|
sAddr[0].sin_addr.s_addr = inet_addr(configJoinIp);
|
|
LOG_INFO("connecting to %s %u", configJoinIp, port);
|
|
snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", gGetHostName);
|
|
}
|
|
|
|
// kick off first packet
|
|
if (networkType == NT_CLIENT) {
|
|
char joinText[128] = { 0 };
|
|
snprintf(joinText, 63, "%s %d", configJoinIp, configJoinPort);
|
|
djui_connect_menu_open();
|
|
|
|
gNetworkType = NT_CLIENT;
|
|
}
|
|
|
|
LOG_INFO("initialized");
|
|
|
|
if (networkType == NT_CLIENT) {
|
|
network_send_mod_list_request();
|
|
}
|
|
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
static s64 ns_socket_get_id(UNUSED u8 localId) {
|
|
return 0;
|
|
}
|
|
|
|
static char* ns_socket_get_id_str(u8 localId) {
|
|
if (localId == UNKNOWN_LOCAL_INDEX) { localId = 0; }
|
|
static char id_str[INET_ADDRSTRLEN] = { 0 };
|
|
snprintf(id_str, INET_ADDRSTRLEN, "%s", inet_ntoa(sAddr[localId].sin_addr));
|
|
return id_str;
|
|
}
|
|
|
|
static void ns_socket_save_id(u8 localId, UNUSED s64 networkId) {
|
|
SOFT_ASSERT(localId > 0);
|
|
SOFT_ASSERT(localId < MAX_PLAYERS);
|
|
sAddr[localId] = sAddr[0];
|
|
LOG_INFO("saved addr for id %d", localId);
|
|
}
|
|
|
|
static void ns_socket_clear_id(u8 localId) {
|
|
if (localId == 0) { return; }
|
|
SOFT_ASSERT(localId < MAX_PLAYERS);
|
|
memset(&sAddr[localId], 0, sizeof(struct sockaddr_in));
|
|
LOG_INFO("cleared addr for id %d", localId);
|
|
}
|
|
|
|
static void* ns_socket_dup_addr(u8 localIndex) {
|
|
void* address = malloc(sizeof(struct sockaddr_in));
|
|
memcpy(address, &sAddr[localIndex], sizeof(struct sockaddr_in));
|
|
return address;
|
|
}
|
|
|
|
static bool ns_socket_match_addr(void* addr1, void* addr2) {
|
|
return !memcmp(addr1, addr2, sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
static void ns_socket_update(void) {
|
|
if (gNetworkType == NT_NONE) { return; }
|
|
do {
|
|
// receive packet
|
|
u8 data[PACKET_LENGTH + 1];
|
|
u16 dataLength = 0;
|
|
u8 localIndex = UNKNOWN_LOCAL_INDEX;
|
|
int rc = socket_receive(sCurSocket, &sAddr[0], data, PACKET_LENGTH + 1, &dataLength, &localIndex);
|
|
SOFT_ASSERT(dataLength < PACKET_LENGTH);
|
|
if (rc != NO_ERROR) { break; }
|
|
network_receive(localIndex, &sAddr[0], data, dataLength);
|
|
} while (true);
|
|
}
|
|
|
|
static int ns_socket_send(u8 localIndex, void* address, u8* data, u16 dataLength) {
|
|
if (localIndex != 0) {
|
|
if (gNetworkType == NT_SERVER && gNetworkPlayers[localIndex].type != NPT_CLIENT) { return SOCKET_ERROR; }
|
|
if (gNetworkType == NT_CLIENT && gNetworkPlayers[localIndex].type != NPT_SERVER) { return SOCKET_ERROR; }
|
|
}
|
|
|
|
struct sockaddr_in* userAddr = &sAddr[localIndex];
|
|
if (localIndex == 0 && address != NULL) { userAddr = (struct sockaddr_in*)address; }
|
|
|
|
int rc = socket_send(sCurSocket, userAddr, data, dataLength);
|
|
if (rc) {
|
|
LOG_ERROR(" localIndex: %d, packetType: %d, dataLength: %d", localIndex, data[0], dataLength);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static void ns_socket_shutdown(UNUSED bool reconnecting) {
|
|
socket_shutdown(sCurSocket);
|
|
sCurSocket = INVALID_SOCKET;
|
|
for (u16 i = 0; i < MAX_PLAYERS; i++) {
|
|
memset(&sAddr[i], 0, sizeof(struct sockaddr_in));
|
|
}
|
|
LOG_INFO("shutdown");
|
|
}
|
|
|
|
struct NetworkSystem gNetworkSystemSocket = {
|
|
.initialize = ns_socket_initialize,
|
|
.get_id = ns_socket_get_id,
|
|
.get_id_str = ns_socket_get_id_str,
|
|
.save_id = ns_socket_save_id,
|
|
.clear_id = ns_socket_clear_id,
|
|
.dup_addr = ns_socket_dup_addr,
|
|
.match_addr = ns_socket_match_addr,
|
|
.update = ns_socket_update,
|
|
.send = ns_socket_send,
|
|
.shutdown = ns_socket_shutdown,
|
|
.requireServerBroadcast = true,
|
|
.name = "Socket",
|
|
};
|