diff --git a/Makefile b/Makefile index d5e53987..405e61e5 100644 --- a/Makefile +++ b/Makefile @@ -534,7 +534,7 @@ SRC_DIRS := src src/engine src/game src/audio src/bass_audio src/menu src/buffer BIN_DIRS := bin bin/$(VERSION) # PC files -SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/mods src/pc/network src/pc/network/packets src/pc/network/socket src/pc/utils src/pc/utils/miniz src/pc/djui src/pc/lua src/pc/lua/utils +SRC_DIRS += src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes src/pc/mods src/pc/network src/pc/network/packets src/pc/network/socket src/pc/network/coopnet src/pc/utils src/pc/utils/miniz src/pc/djui src/pc/lua src/pc/lua/utils #ifeq ($(DISCORDRPC),1) # SRC_DIRS += src/pc/discord @@ -794,7 +794,7 @@ INCLUDE_DIRS := include $(BUILD_DIR) $(BUILD_DIR)/include src . ifeq ($(TARGET_N64),1) INCLUDE_DIRS += include/libc else - INCLUDE_DIRS += sound lib/lua/include $(EXTRA_INCLUDES) + INCLUDE_DIRS += sound lib/lua/include lib/coopnet/include $(EXTRA_INCLUDES) endif # Connfigure backend flags @@ -971,6 +971,26 @@ else LDFLAGS += -Llib/lua/linux -l:liblua53.a -ldl endif +# coopnet +LDFLAGS += -Llib/coopnet/linux -l:libcoopnet.a -l:libjuice.a +#ifeq ($(WINDOWS_BUILD),1) +# ifeq ($(TARGET_BITS), 32) +# LDFLAGS += -Llib/coopnet/win32 -l:libcoopnet.a +# else +# LDFLAGS += -Llib/coopnet/win64 -l:libcoopnet.a +# endif +#else ifeq ($(OSX_BUILD),1) +# LDFLAGS += -L./lib/coopnet/mac/ -l coopnet +#else ifeq ($(TARGET_RPI),1) +# ifneq (,$(findstring aarch64,$(machine))) +# LDFLAGS += -Llib/coopnet/linux -l:libcoopnet-arm64.a +# else +# LDFLAGS += -Llib/coopnet/linux -l:libcoopnet-arm.a +# endif +#else +# LDFLAGS += -Llib/coopnet/linux -l:libcoopnet.a +#endif + # Network/Discord/Bass (ugh, needs cleanup) ifeq ($(WINDOWS_BUILD),1) LDFLAGS += -L"ws2_32" -lwsock32 diff --git a/lang/English.ini b/lang/English.ini index 20d539ca..40fcadd6 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -176,6 +176,7 @@ SERVER_TITLE = "SERVER" HOST_TITLE = "HOST" DISCORD = "Discord" DIRECT_CONNECTION = "Direct Connection" +COOPNET = "CoopNet" NETWORK_SYSTEM = "Network system" PORT = "Port" SAVE_SLOT = "Save Slot" diff --git a/lang/French.ini b/lang/French.ini index 13657930..0e092ba4 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -176,6 +176,7 @@ SERVER_TITLE = "SERVEUR" HOST_TITLE = "HÉBERGER" DISCORD = "Discord" DIRECT_CONNECTION = "Connexion Directe" +COOPNET = "CoopNet" NETWORK_SYSTEM = "Mode d'hébergement" PORT = "Port" SAVE_SLOT = "Sauvegarde" diff --git a/lang/German.ini b/lang/German.ini index 56695824..8c667f0f 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -176,6 +176,7 @@ SERVER_TITLE = "SERVER" HOST_TITLE = "VERANSTALTEN" DISCORD = "Discord" DIRECT_CONNECTION = "Direkte Verbindung" +COOPNET = "CoopNet" NETWORK_SYSTEM = "Netzwerk-System" PORT = "Port" SAVE_SLOT = "Save Slot" diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index d4ccaf90..60be7ea1 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -175,6 +175,7 @@ AMOUNT_OF_PLAYERS = "Quantidade de jogadores" SERVER_TITLE = "SERVIDOR" HOST_TITLE = "HOSTEAR" DISCORD = "Discord" +COOPNET = "CoopNet" DIRECT_CONNECTION = "Conexão Direta" NETWORK_SYSTEM = "Sistema de Rede" PORT = "Porta" diff --git a/lang/Spanish.ini b/lang/Spanish.ini index e0f832fa..19397856 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -175,6 +175,7 @@ AMOUNT_OF_PLAYERS = "Número de jugadores" SERVER_TITLE = "SERVIDOR" HOST_TITLE = "ALOJAR" DISCORD = "Discord" +COOPNET = "CoopNet" DIRECT_CONNECTION = "Conexión Directa" NETWORK_SYSTEM = "Modo de conexión" PORT = "Puerto" diff --git a/lib/coopnet/include/libcoopnet.h b/lib/coopnet/include/libcoopnet.h new file mode 100644 index 00000000..3a760e06 --- /dev/null +++ b/lib/coopnet/include/libcoopnet.h @@ -0,0 +1,61 @@ +#ifndef LIBCOOPNET_H +#define LIBCOOPNET_H + +#if defined(__cplusplus) +#include +extern "C" { +#endif + +#include +#include + +typedef enum { + COOPNET_OK, + COOPNET_FAILED, + COOPNET_DISCONNECTED, +} CoopNetRc; + +enum MPacketErrorNumber { + MERR_NONE, + MERR_LOBBY_NOT_FOUND, + MERR_LOBBY_JOIN_FULL, + MERR_LOBBY_JOIN_FAILED, + MERR_MAX, +}; + +typedef struct { + void (*OnConnected)(uint64_t aUserId); + void (*OnDisconnected)(void); + void (*OnLobbyCreated)(uint64_t aLobbyId, const char* aGame, const char* aVersion, const char* aTitle, uint16_t aMaxConnections); + void (*OnLobbyJoined)(uint64_t aLobbyId, uint64_t aUserId, uint64_t aOwnerId); + void (*OnLobbyLeft)(uint64_t aLobbyId, uint64_t aUserId); + void (*OnLobbyListGot)(uint64_t aLobbyId, uint64_t aOwnerId, uint16_t aConnections, uint16_t aMaxConnections, const char* aGame, const char* aVersion, const char* aTitle); + void (*OnReceive)(uint64_t aFromUserId, const uint8_t* aData, uint64_t aSize); + void (*OnError)(enum MPacketErrorNumber aErrorNumber); + void (*OnPeerConnected)(uint64_t aPeerId); + void (*OnPeerDisconnected)(uint64_t aPeerId); +} CoopNetCallbacks; + +typedef struct { + bool SkipWinsockInit; +} CoopNetSettings; + +extern CoopNetCallbacks gCoopNetCallbacks; +extern CoopNetSettings gCoopNetSettings; + +bool coopnet_is_connected(void); +CoopNetRc coopnet_begin(const char* aHost, uint32_t aPort); +CoopNetRc coopnet_shutdown(void); +CoopNetRc coopnet_update(void); +CoopNetRc coopnet_lobby_create(const char* aGame, const char* aVersion, const char* aTitle, uint16_t aMaxConnections); +CoopNetRc coopnet_lobby_join(uint64_t aLobbyId); +CoopNetRc coopnet_lobby_leave(uint64_t aLobbyId); +CoopNetRc coopnet_lobby_list_get(const char* aGame); +CoopNetRc coopnet_send(const uint8_t* aData, uint64_t aDataLength); +CoopNetRc coopnet_send_to(uint64_t aPeerId, const uint8_t* aData, uint64_t aDataLength); +CoopNetRc coopnet_unpeer(uint64_t aPeerId); + +#if defined(__cplusplus) +} +#endif +#endif \ No newline at end of file diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c index 857adae5..98ee7c72 100644 --- a/src/pc/djui/djui_panel_host.c +++ b/src/pc/djui/djui_panel_host.c @@ -12,16 +12,16 @@ #include "pc/configfile.h" #include "pc/cheats.h" -#ifdef DISCORD_SDK -#define DJUI_HOST_NS_IS_SOCKET (configNetworkSystem == 1) -#else -#define DJUI_HOST_NS_IS_SOCKET (true) -#endif - struct DjuiInputbox* sInputboxPort = NULL; static void djui_panel_host_network_system_change(UNUSED struct DjuiBase* base) { - djui_base_set_enabled(&sInputboxPort->base, DJUI_HOST_NS_IS_SOCKET); +#ifndef DISCORD_SDK + struct DjuiSelectionbox* selectionbox = (struct DjuiSelectionbox*) base; + if (selectionbox->value == NS_DISCORD) { + selectionbox->value = NS_SOCKET; + } +#endif + djui_base_set_enabled(&sInputboxPort->base, (configNetworkSystem == NS_SOCKET)); } static bool djui_panel_host_port_valid(void) { @@ -77,15 +77,12 @@ void djui_panel_host_create(struct DjuiBase* caller) { : DLANG(HOST, HOST_TITLE)); struct DjuiBase* body = djui_three_panel_get_body(panel); { -#ifdef DISCORD_SDK - char* nChoices[2] = { DLANG(HOST, DISCORD), DLANG(HOST, DIRECT_CONNECTION) }; - struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(body, DLANG(HOST, NETWORK_SYSTEM), nChoices, 2, &configNetworkSystem, djui_panel_host_network_system_change); + char* nChoices[] = { DLANG(HOST, DISCORD), DLANG(HOST, DIRECT_CONNECTION), DLANG(HOST, COOPNET) }; + struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(body, DLANG(HOST, NETWORK_SYSTEM), nChoices, 3, &configNetworkSystem, djui_panel_host_network_system_change); if (gNetworkType == NT_SERVER) { djui_base_set_enabled(&selectionbox1->base, false); } -#endif - struct DjuiRect* rect1 = djui_rect_container_create(body, 32); { struct DjuiText* text1 = djui_text_create(&rect1->base, DLANG(HOST, PORT)); @@ -107,10 +104,8 @@ void djui_panel_host_create(struct DjuiBase* caller) { djui_interactable_hook_value_change(&inputbox1->base, djui_panel_host_port_text_change); if (gNetworkType == NT_SERVER) { djui_base_set_enabled(&inputbox1->base, false); -#ifdef DISCORD_SDK } else { - djui_base_set_enabled(&inputbox1->base, DJUI_HOST_NS_IS_SOCKET); -#endif + djui_base_set_enabled(&inputbox1->base, (configNetworkSystem == NS_SOCKET)); } sInputboxPort = inputbox1; } diff --git a/src/pc/djui/djui_panel_host_message.c b/src/pc/djui/djui_panel_host_message.c index c7850494..da44d9c9 100644 --- a/src/pc/djui/djui_panel_host_message.c +++ b/src/pc/djui/djui_panel_host_message.c @@ -22,15 +22,10 @@ void djui_panel_do_host(void) { update_all_mario_stars(); #ifndef DISCORD_SDK - configNetworkSystem = 1; - network_set_system(NS_SOCKET); -#else - if (configNetworkSystem == 0) { - network_set_system(NS_DISCORD); - } else { - network_set_system(NS_SOCKET); - } + if (configNetworkSystem == NS_DISCORD) { configNetworkSystem = NS_COOPNET; } #endif + if (configNetworkSystem >= NS_MAX) { configNetworkSystem = NS_MAX; } + network_set_system(configNetworkSystem); network_init(NT_SERVER); djui_panel_modlist_create(NULL); @@ -56,14 +51,11 @@ void djui_panel_host_message_create(struct DjuiBase* caller) { char* warningMessage = NULL; bool hideHostButton = false; -#ifdef DISCORD_SDK - if (!configNetworkSystem) { + if (configNetworkSystem == NS_DISCORD) { warningLines = gDiscordFailed ? 5 : 13; warningMessage = gDiscordFailed ? DLANG(HOST_MESSAGE, WARN_DISCORD2) : DLANG(HOST_MESSAGE, WARN_DISCORD); hideHostButton = gDiscordFailed; - } else -#endif - { + } else { warningLines = 5; warningMessage = calloc(256, sizeof(char)); sprintf(warningMessage, DLANG(HOST_MESSAGE, WARN_SOCKET), configHostPort); @@ -93,10 +85,7 @@ void djui_panel_host_message_create(struct DjuiBase* caller) { } djui_panel_add(caller, panel, NULL); -#ifdef DISCORD_SDK - if (configNetworkSystem) -#endif - { + if (configNetworkSystem != NS_DISCORD) { free(warningMessage); } } diff --git a/src/pc/djui/djui_panel_join.c b/src/pc/djui/djui_panel_join.c index b9ddbf59..bdd7fb01 100644 --- a/src/pc/djui/djui_panel_join.c +++ b/src/pc/djui/djui_panel_join.c @@ -143,7 +143,7 @@ void djui_panel_join_do_join(struct DjuiBase* caller) { } network_reset_reconnect_and_rehost(); djui_panel_join_ip_text_set_new(); - network_set_system(NS_SOCKET); + network_set_system(NS_COOPNET); // DO NOT COMMIT network_init(NT_CLIENT); djui_panel_join_message_create(caller); } diff --git a/src/pc/network/coopnet/coopnet.c b/src/pc/network/coopnet/coopnet.c new file mode 100644 index 00000000..7d5cc828 --- /dev/null +++ b/src/pc/network/coopnet/coopnet.c @@ -0,0 +1,152 @@ +#include "libcoopnet.h" +#include "coopnet.h" +#include "pc/network/network.h" +#include "pc/debuglog.h" + +#define HOST "localhost" +#define PORT 34197 + +static uint64_t sLocalUserId = 0; +static uint64_t sLocalLobbyId = 0; +static uint64_t sLocalLobbyOwnerId = 0; +static uint64_t sNetworkUserIds[MAX_PLAYERS] = { 0 }; +static enum NetworkType sNetworkType; + +static u8 coopnet_user_id_to_local_index(uint64_t userId) { + for (int i = 1; i < MAX_PLAYERS; i++) { + if (gNetworkPlayers[i].connected && sNetworkUserIds[i] == userId) { + return i; + } + } + return UNKNOWN_LOCAL_INDEX; +} + +static void coopnet_on_connected(uint64_t userId) { + sLocalUserId = userId; + if (sNetworkType == NT_SERVER) { + coopnet_lobby_create("sm64ex-coop", "beta 999", "n/a", 16); + } else { + coopnet_lobby_join(1); + } +} + +static void coopnet_on_peer_connected(UNUSED uint64_t peerId) { +} + +static void coopnet_on_peer_disconnected(uint64_t peerId) { + u8 localIndex = coopnet_user_id_to_local_index(peerId); + if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) { + network_player_disconnected(gNetworkPlayers[localIndex].globalIndex); + } +} + +static void coopnet_on_receive(uint64_t userId, const uint8_t* data, uint64_t dataLength) { + sNetworkUserIds[0] = userId; + u8 localIndex = coopnet_user_id_to_local_index(userId); + network_receive(localIndex, &userId, (u8*)data, dataLength); +} + +static void coopnet_on_lobby_joined(uint64_t lobbyId, uint64_t userId, uint64_t ownerId) { + LOG_INFO("coopnet_on_lobby_joined!"); + sNetworkUserIds[0] = ownerId; + sLocalLobbyId = lobbyId; + sLocalLobbyOwnerId = ownerId; + if (userId == sLocalUserId && sNetworkType == NT_CLIENT) { + network_send_mod_list_request(); + } +} + + +static void coopnet_on_lobby_left(uint64_t lobbyId, uint64_t userId) { + LOG_INFO("coopnet_on_lobby_left!"); + if (lobbyId == sLocalLobbyId && userId == sLocalUserId) { + network_shutdown(false, false, true); + } +} + +static bool ns_coopnet_initialize(enum NetworkType networkType) { + if (coopnet_is_connected()) { + coopnet_shutdown(); + } + + gCoopNetCallbacks.OnConnected = coopnet_on_connected; + gCoopNetCallbacks.OnReceive = coopnet_on_receive; + gCoopNetCallbacks.OnLobbyJoined = coopnet_on_lobby_joined; + gCoopNetCallbacks.OnLobbyLeft = coopnet_on_lobby_left; + gCoopNetCallbacks.OnPeerConnected = coopnet_on_peer_connected; + gCoopNetCallbacks.OnPeerDisconnected = coopnet_on_peer_disconnected; + sNetworkType = networkType; + + return (coopnet_begin(HOST, PORT) == COOPNET_OK); +} + +static s64 ns_coopnet_get_id(u8 localIndex) { + if (localIndex == 0) { return (s64)sLocalUserId; } + return (s64)sNetworkUserIds[localIndex]; +} + +static char* ns_coopnet_get_id_str(u8 localIndex) { + static char id_str[22] = { 0 }; + if (localIndex == UNKNOWN_LOCAL_INDEX) { + snprintf(id_str, 22, "???"); + } else { + snprintf(id_str, 22, "%lld", (long long int)ns_coopnet_get_id(localIndex)); + } + return id_str; +} + +static void ns_coopnet_save_id(u8 localIndex, s64 networkId) { + SOFT_ASSERT(localIndex > 0); + SOFT_ASSERT(localIndex < MAX_PLAYERS); + sNetworkUserIds[localIndex] = (networkId == 0) ? sNetworkUserIds[0] : (u64)networkId; +} + +static void ns_coopnet_clear_id(u8 localIndex) { + if (localIndex == 0) { return; } + SOFT_ASSERT(localIndex < MAX_PLAYERS); + sNetworkUserIds[localIndex] = 0; +} + +static void* ns_coopnet_dup_addr(u8 localIndex) { + void* address = malloc(sizeof(u64)); + memcpy(address, &sNetworkUserIds[localIndex], sizeof(u64)); + return address; +} + +static bool ns_coopnet_match_addr(void* addr1, void* addr2) { + return !memcmp(addr1, addr2, sizeof(u64)); +} + +static void ns_coopnet_update(void) { + coopnet_update(); +} + +static int ns_coopnet_network_send(u8 localIndex, void* address, u8* data, u16 dataLength) { + if (!coopnet_is_connected()) { return 1; } + //if (gCurLobbyId == 0) { return 2; } + + u64 userId = sNetworkUserIds[localIndex]; + if (localIndex == 0 && address != NULL) { userId = *(u64*)address; } + coopnet_send_to(userId, data, dataLength); + + return 0; +} + +static void ns_coopnet_shutdown(void) { + coopnet_shutdown(); +} + +struct NetworkSystem gNetworkSystemCoopNet = { + .initialize = ns_coopnet_initialize, + .get_id = ns_coopnet_get_id, + .get_id_str = ns_coopnet_get_id_str, + .save_id = ns_coopnet_save_id, + .clear_id = ns_coopnet_clear_id, + .dup_addr = ns_coopnet_dup_addr, + .match_addr = ns_coopnet_match_addr, + .update = ns_coopnet_update, + .send = ns_coopnet_network_send, + .shutdown = ns_coopnet_shutdown, + .requireServerBroadcast = false, + .name = "CoopNet", +}; diff --git a/src/pc/network/coopnet/coopnet.h b/src/pc/network/coopnet/coopnet.h new file mode 100644 index 00000000..92cf1a36 --- /dev/null +++ b/src/pc/network/coopnet/coopnet.h @@ -0,0 +1,6 @@ +#ifndef COOPNET_H +#define COOPNET_H + +extern struct NetworkSystem gNetworkSystemCoopNet; + +#endif \ No newline at end of file diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 8e705f17..77d62668 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -1,4 +1,5 @@ #include "socket/socket.h" +#include "coopnet/coopnet.h" #include #include "network.h" #include "object_fields.h" @@ -89,6 +90,7 @@ void network_set_system(enum NetworkSystemType nsType) { #ifdef DISCORD_SDK case NS_DISCORD: gNetworkSystem = &gNetworkSystemDiscord; break; #endif + case NS_COOPNET: gNetworkSystem = &gNetworkSystemCoopNet; break; default: LOG_ERROR("Unknown network system: %d", nsType); } } diff --git a/src/pc/network/network.h b/src/pc/network/network.h index b2f15471..7af402ce 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -37,6 +37,8 @@ extern struct MarioState gMarioStates[]; enum NetworkSystemType { NS_SOCKET, NS_DISCORD, + NS_COOPNET, + NS_MAX, }; struct NetworkSystem {