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