diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index f14a61b4d..3238b1914 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3948,6 +3948,7 @@ + @@ -4036,6 +4037,7 @@ + @@ -4388,6 +4390,7 @@ + @@ -4426,6 +4429,7 @@ + diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index 2fde8f968..afc2678a0 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -15246,6 +15246,12 @@ Source Files\src\pc\djui\panel + + Source Files\src\pc\djui\panel + + + Source Files\src\pc\utils + @@ -16312,5 +16318,11 @@ Source Files\src\pc\djui\panel + + Source Files\src\pc\djui\panel + + + Source Files\src\pc\utils + \ No newline at end of file diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h index b47956a6e..4e0736d85 100644 --- a/src/pc/djui/djui.h +++ b/src/pc/djui/djui.h @@ -35,6 +35,7 @@ #include "djui_panel_host.h" #include "djui_panel_host_message.h" #include "djui_panel_join.h" +#include "djui_panel_join_message.h" #include "djui_panel_options.h" #include "djui_panel_camera.h" #include "djui_panel_controls.h" diff --git a/src/pc/djui/djui_panel.c b/src/pc/djui/djui_panel.c index 1b9a3765a..962c7d7b4 100644 --- a/src/pc/djui/djui_panel.c +++ b/src/pc/djui/djui_panel.c @@ -15,6 +15,7 @@ static f32 sMoveAmount = 0; void djui_panel_add(struct DjuiBase* caller, struct DjuiBase* panelBase, struct DjuiBase* defaultElementBase) { bool firstPanel = (sPanelList == NULL); + gDjuiPanelJoinMessageVisible = false; // remember element that triggered this panel add if (sPanelList != NULL) { @@ -76,6 +77,8 @@ void djui_panel_back(void) { // play a sound play_sound(SOUND_MENU_CLICK_FILE_SELECT, gDefaultSoundArgs); + + gDjuiPanelJoinMessageVisible = false; } void djui_panel_update(void) { @@ -130,5 +133,7 @@ void djui_panel_shutdown(void) { sPanelRemoving = NULL; sMoveAmount = 0; gInteractableOverridePad = false; + gDjuiPanelJoinMessageVisible = false; + gDjuiPanelMainCreated = false; djui_cursor_set_visible(false); } \ No newline at end of file diff --git a/src/pc/djui/djui_panel_join.c b/src/pc/djui/djui_panel_join.c index 4d442499e..3c45adeb9 100644 --- a/src/pc/djui/djui_panel_join.c +++ b/src/pc/djui/djui_panel_join.c @@ -16,9 +16,9 @@ Enter \\#d0d0ff\\direct connection\\#c8c8c8\\ IP and port:\ "; void djui_panel_join_do_join(struct DjuiBase* caller) { - djui_panel_shutdown(); network_set_system(NS_SOCKET); network_init(NT_CLIENT); + djui_panel_join_message_create(caller); } void djui_panel_join_create(struct DjuiBase* caller) { diff --git a/src/pc/djui/djui_panel_join_message.c b/src/pc/djui/djui_panel_join_message.c new file mode 100644 index 000000000..8a1ddb530 --- /dev/null +++ b/src/pc/djui/djui_panel_join_message.c @@ -0,0 +1,75 @@ +#include "djui.h" +#include "src/pc/network/network.h" +#include "src/pc/utils/misc.h" +#include "src/pc/configfile.h" + +#define DJUI_JOIN_MESSAGE_ELAPSE 60 +bool gDjuiPanelJoinMessageVisible = false; +static struct DjuiText* sPanelText = NULL; +static bool sDisplayingError = false; + +void djui_panel_join_message_error(char* message) { + djui_panel_join_message_create(NULL); + sDisplayingError = true; + djui_text_set_text(sPanelText, message); +} + +void djui_panel_join_message_cancel(struct DjuiBase* caller) { + network_shutdown(true); + djui_panel_menu_back(caller); +} + +void djui_panel_join_message_render_pre(struct DjuiBase* base, UNUSED bool* unused) { + if (sDisplayingError) { return; } + struct DjuiText* text1 = (struct DjuiText*)base; + u16 lastElapse = (base->tag / DJUI_JOIN_MESSAGE_ELAPSE); + base->tag = (base->tag + 1) % (DJUI_JOIN_MESSAGE_ELAPSE * 3); + u16 elapse = (base->tag / DJUI_JOIN_MESSAGE_ELAPSE); + if (lastElapse != elapse) { + switch (base->tag / DJUI_JOIN_MESSAGE_ELAPSE) { + case 0: djui_text_set_text(text1, "..."); break; + case 1: djui_text_set_text(text1, "."); break; + default: djui_text_set_text(text1, ".."); break; + } + } +} + +void djui_panel_join_message_create(struct DjuiBase* caller) { + // make sure main panel was created + if (!gDjuiPanelMainCreated) { djui_panel_main_create(caller); } + + // don't recreate panel if it's already visible + if (gDjuiPanelJoinMessageVisible) { return; } + + f32 bodyHeight = 64 + 16; + + u16 directLines = 8; + f32 directTextHeight = 32 * 0.8125f * directLines + 8; + bodyHeight += directTextHeight + 16; + + struct DjuiBase* defaultBase = NULL; + struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\J\\#1be700\\O\\#00b3ff\\I\\#ffef00\\N\\#1be700\\I\\#00b3ff\\N\\#ffef00\\G"); + struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel); + { + struct DjuiText* text1 = djui_text_create(&body->base, "..."); + djui_base_set_size_type(&text1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&text1->base, 1.0f, directTextHeight); + djui_base_set_color(&text1->base, 200, 200, 200, 255); + djui_text_set_alignment(text1, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + text1->base.tag = 0; + text1->base.on_render_pre = djui_panel_join_message_render_pre; + sPanelText = text1; + + struct DjuiButton* button1 = djui_button_create(&body->base, "Cancel"); + djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&button1->base, 1.0f, 64); + djui_base_set_alignment(&button1->base, DJUI_HALIGN_LEFT, DJUI_VALIGN_TOP); + djui_button_set_style(button1, 1); + djui_interactable_hook_click(&button1->base, djui_panel_join_message_cancel); + defaultBase = &button1->base; + } + + djui_panel_add(caller, &panel->base, defaultBase); + gDjuiPanelJoinMessageVisible = true; + sDisplayingError = false; +} diff --git a/src/pc/djui/djui_panel_join_message.h b/src/pc/djui/djui_panel_join_message.h new file mode 100644 index 000000000..2a6d7d5b5 --- /dev/null +++ b/src/pc/djui/djui_panel_join_message.h @@ -0,0 +1,7 @@ +#pragma once +#include "djui.h" + +extern bool gDjuiPanelJoinMessageVisible; + +void djui_panel_join_message_error(char* message); +void djui_panel_join_message_create(struct DjuiBase* caller); diff --git a/src/pc/djui/djui_panel_main.c b/src/pc/djui/djui_panel_main.c index 795a0e13b..5c2622186 100644 --- a/src/pc/djui/djui_panel_main.c +++ b/src/pc/djui/djui_panel_main.c @@ -1,6 +1,8 @@ #include "djui.h" #include "src/pc/controller/controller_sdl.h" +bool gDjuiPanelMainCreated = false; + void djui_panel_main_create(struct DjuiBase* caller) { f32 bodyHeight = 64 * 4 + 16 * 3; @@ -43,4 +45,5 @@ void djui_panel_main_create(struct DjuiBase* caller) { djui_panel_add(caller, &panel->base, defaultBase); gInteractableOverridePad = true; + gDjuiPanelMainCreated = true; } diff --git a/src/pc/djui/djui_panel_main.h b/src/pc/djui/djui_panel_main.h index c96a1b7e3..e15eeea67 100644 --- a/src/pc/djui/djui_panel_main.h +++ b/src/pc/djui/djui_panel_main.h @@ -1,4 +1,6 @@ #pragma once #include "djui.h" +extern bool gDjuiPanelMainCreated; + void djui_panel_main_create(struct DjuiBase* caller); diff --git a/src/pc/network/discord/discord.c b/src/pc/network/discord/discord.c index 430ad38ae..014efc8f7 100644 --- a/src/pc/network/discord/discord.c +++ b/src/pc/network/discord/discord.c @@ -148,7 +148,7 @@ static bool ns_discord_initialize(enum NetworkType networkType) { DISCORD_REQUIRE(rc); } else if (rc) { LOG_ERROR("DiscordCreate failed: %d", rc); - djui_show_popup("Could not detect Discord.\n\nTry closing the game,\nrestarting Discord,\nand opening the game again."); + djui_show_popup("\\#ffa0a0\\Error:\\#c8c8c8\\ Could not detect Discord.\n\nTry closing the game, restarting Discord, and opening the game again."); gDiscordFailed = true; return false; } diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 8c97e69dd..79594db34 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -262,12 +262,12 @@ void network_register_mod(char* modName) { string_linked_list_append(&gRegisteredMods, modName); } -void network_shutdown(void) { +void network_shutdown(bool sendLeaving) { network_forget_all_reliable(); if (gNetworkType == NT_NONE) { return; } if (gNetworkSystem == NULL) { LOG_ERROR("no network system attached"); return; } - if (gNetworkPlayerLocal != NULL) { network_send_leaving(gNetworkPlayerLocal->globalIndex); } + if (gNetworkPlayerLocal != NULL && sendLeaving) { network_send_leaving(gNetworkPlayerLocal->globalIndex); } network_player_shutdown(); gNetworkSystem->shutdown(); diff --git a/src/pc/network/network.h b/src/pc/network/network.h index 60b4e7ded..99e114ee4 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -99,7 +99,7 @@ void network_send(struct Packet* p); void network_receive(u8 localIndex, u8* data, u16 dataLength); void network_update(void); void network_register_mod(char* modName); -void network_shutdown(void); +void network_shutdown(bool sendLeaving); // TODO: replace void chat_add_message(char* message); diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index 70025721b..0ae0653d5 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -106,7 +106,7 @@ void network_player_update(void) { } } if (!connectionAlive) { - network_shutdown(); + network_shutdown(true); } } #endif @@ -194,7 +194,7 @@ u8 network_player_disconnected(u8 globalIndex) { LOG_ERROR("player disconnected, but it's local.. this shouldn't happen!"); return UNKNOWN_GLOBAL_INDEX; } else { - network_shutdown(); + network_shutdown(true); } } diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 8510da5c8..56a4c0bdb 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -72,8 +72,11 @@ void packet_receive(struct Packet* p) { u8 packetType = (u8)p->buffer[0]; // refuse packets from unknown players other than join request - if (gNetworkType == NT_SERVER && p->localIndex == UNKNOWN_LOCAL_INDEX && packetType != PACKET_JOIN_REQUEST) { - network_send_kick(EKT_CLOSE_CONNECTION); + if (gNetworkType == NT_SERVER && p->localIndex == UNKNOWN_LOCAL_INDEX && packetType != PACKET_JOIN_REQUEST && packetType != PACKET_ACK) { + if (packetType != PACKET_PLAYER) { + LOG_INFO("closing connection for packetType: %d", packetType); + network_send_kick(EKT_CLOSE_CONNECTION); + } return; } diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index 84754ec28..173eadf6b 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -10,10 +10,11 @@ #include "src/pc/fs/fs.h" #include "PR/os_eeprom.h" #include "pc/network/version.h" +#include "pc/djui/djui.h" +#include "pc/utils/string_builder.h" #define DISABLE_MODULE_LOG 1 #include "pc/debuglog.h" - extern u8* gOverrideEeprom; static u8 eeprom[512] = { 0 }; @@ -108,17 +109,11 @@ void network_receive_join(struct Packet* p) { packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH); LOG_INFO("server has version: %s", version); if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) { + network_shutdown(true); LOG_ERROR("version mismatch"); - - // todo: hack: remove me in the future - // needed because the old style only had 8 characters for the version - if (strcmp("beta", remoteVersion) != 0) { - remoteVersion[8] = '\0'; - } - - char mismatchMessage[128] = { 0 }; - snprintf(mismatchMessage, 128, "Version mismatch.\n\nYour version - %s\nTheir version - %s\n\nSomeone is out of date!\n", version, remoteVersion); - djui_show_popup(mismatchMessage); + char mismatchMessage[256] = { 0 }; + snprintf(mismatchMessage, 256, "\\#ffa0a0\\Error:\\#c8c8c8\\ Version mismatch.\n\nYour version: \\#a0a0ff\\%s\\#c8c8c8\\\nTheir version: \\#a0a0ff\\%s\\#c8c8c8\\\n\nSomeone is out of date!\n", version, remoteVersion); + djui_panel_join_message_error(mismatchMessage); return; } @@ -142,8 +137,36 @@ void network_receive_join(struct Packet* p) { } if (string_linked_list_mismatch(&gRegisteredMods, &head)) { + network_shutdown(true); + + struct StringBuilder* builder = string_builder_create(512); + string_builder_append(builder, "\\#ffa0a0\\Error:\\#c8c8c8\\ mods don't match.\n\n"); + + string_builder_append(builder, "\\#c8c8c8\\Yours: "); + struct StringLinkedList* node = &gRegisteredMods; + bool first = true; + while (node != NULL) { + string_builder_append(builder, first ? "\\#%s\\%s" : ", \\#%s\\%s", + string_linked_list_contains(&head, node->string) ? "a0ffa0" : "ffa0a0" + , node->string); + first = false; + node = node->next; + } + + string_builder_append(builder, "\n\n\\#c8c8c8\\Theirs: "); + node = &head; + first = true; + while (node != NULL) { + string_builder_append(builder, first ? "\\#%s\\%s" : ", \\#%s\\%s", + string_linked_list_contains(&gRegisteredMods, node->string) ? "a0ffa0" : "ffa0a0" + , node->string); + first = false; + node = node->next; + } + + djui_panel_join_message_error(builder->string); + string_builder_destroy(builder); string_linked_list_free(&head); - djui_show_popup("Your mods don't match!"); return; } string_linked_list_free(&head); @@ -153,6 +176,7 @@ void network_receive_join(struct Packet* p) { save_file_load_all(TRUE); + djui_panel_shutdown(); extern s16 gChangeLevel; gChangeLevel = 16; } diff --git a/src/pc/network/packets/packet_kick.c b/src/pc/network/packets/packet_kick.c index 57d9da47a..53c8c5d7b 100644 --- a/src/pc/network/packets/packet_kick.c +++ b/src/pc/network/packets/packet_kick.c @@ -1,6 +1,7 @@ #include #include "../network.h" #include "pc/debuglog.h" +#include "pc/djui/djui.h" void network_send_kick(enum KickReasonType kickReason) { u8 kickReasonType = kickReason; @@ -26,8 +27,8 @@ void network_receive_kick(struct Packet* p) { enum KickReasonType kickReason = kickReasonType; switch (kickReason) { - case EKT_FULL_PARTY: djui_show_popup("The party is full."); break; - default: djui_show_popup("Host has closed the connection."); break; + case EKT_FULL_PARTY: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ The party is full."); break; + default: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ Host has closed the connection."); break; } - network_shutdown(); + network_shutdown(false); } diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 18eff5ce1..67ea52450 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -158,7 +158,7 @@ void game_deinit(void) { controller_shutdown(); audio_shutdown(); gfx_shutdown(); - network_shutdown(); + network_shutdown(true); inited = false; } diff --git a/src/pc/utils/string_builder.c b/src/pc/utils/string_builder.c new file mode 100644 index 000000000..6e829bbe9 --- /dev/null +++ b/src/pc/utils/string_builder.c @@ -0,0 +1,16 @@ +#include +#include +#include "string_builder.h" + +struct StringBuilder* string_builder_create(int bufferLength) { + struct StringBuilder* builder = malloc(sizeof(struct StringBuilder)); + builder->string = malloc(sizeof(char) * bufferLength); + builder->string[0] = '\0'; + builder->bufferLength = bufferLength; + return builder; +} + +void string_builder_destroy(struct StringBuilder* builder) { + free(builder->string); + free(builder); +} diff --git a/src/pc/utils/string_builder.h b/src/pc/utils/string_builder.h new file mode 100644 index 000000000..da6084e00 --- /dev/null +++ b/src/pc/utils/string_builder.h @@ -0,0 +1,15 @@ +#ifndef STRING_BUILDER_H +#define STRING_BUILDER_H + +#include + +struct StringBuilder { + char* string; + int bufferLength; +}; + +struct StringBuilder* string_builder_create(int bufferLength); +#define string_builder_append(_builder, ...) snprintf((_builder->string + strlen(_builder->string)), (_builder->bufferLength - strlen(_builder->string)), __VA_ARGS__) +void string_builder_destroy(struct StringBuilder* builder); + +#endif \ No newline at end of file