diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 29a75f56b..f67eaf21a 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -466,7 +466,9 @@
+
+
@@ -931,7 +933,9 @@
+
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index c1a67ee8f..69402c774 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -4854,6 +4854,12 @@
Source Files\src\pc\djui\component\compound
+
+ Source Files\src\pc\djui\panel
+
+
+ Source Files\src\pc\djui\panel
+
@@ -5989,5 +5995,11 @@
Source Files\src\pc\djui\component\compound
+
+ Source Files\src\pc\djui\panel
+
+
+ Source Files\src\pc\djui\panel
+
\ No newline at end of file
diff --git a/src/pc/configfile.c b/src/pc/configfile.c
index f5ef86386..1d30448d8 100644
--- a/src/pc/configfile.c
+++ b/src/pc/configfile.c
@@ -13,6 +13,7 @@
#include "gfx/gfx_window_manager_api.h"
#include "controller/controller_api.h"
#include "fs/fs.h"
+#include "pc/mod_list.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -295,12 +296,25 @@ void configfile_load(const char *filename) {
if (numTokens >= 2) {
const struct ConfigOption *option = NULL;
+ // enable mods
+ if (!strcmp(tokens[0], "enable-mod:")) {
+ for (unsigned int i = 0; i < gModTableLocal.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableLocal.entries[i];
+ if (!strcmp(tokens[1], entry->name)) {
+ entry->enabled = true;
+ break;
+ }
+ }
+ continue;
+ }
+
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
if (strcmp(tokens[0], options[i].name) == 0) {
option = &options[i];
break;
}
}
+
if (option == NULL)
printf("unknown option '%s'\n", tokens[0]);
else {
@@ -384,5 +398,12 @@ void configfile_save(const char *filename) {
}
}
+ // save enabled mods
+ for (unsigned int i = 0; i < gModTableLocal.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableLocal.entries[i];
+ if (!entry->enabled) { continue; }
+ fprintf(file, "%s %s\n", "enable-mod:", entry->name);
+ }
+
fclose(file);
}
diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h
index e3cb8fef1..9c8d049de 100644
--- a/src/pc/djui/djui.h
+++ b/src/pc/djui/djui.h
@@ -37,6 +37,8 @@
#include "djui_panel_debug.h"
#include "djui_panel_main.h"
#include "djui_panel_host.h"
+#include "djui_panel_host_settings.h"
+#include "djui_panel_host_mods.h"
#include "djui_panel_host_save.h"
#include "djui_panel_host_message.h"
#include "djui_panel_join.h"
diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c
index 9d3dc06d1..582427c33 100644
--- a/src/pc/djui/djui_panel_host.c
+++ b/src/pc/djui/djui_panel_host.c
@@ -13,7 +13,6 @@
#endif
struct DjuiInputbox* sInputboxPort = NULL;
-static unsigned int sKnockbackIndex = 0;
static void djui_panel_host_network_system_change(UNUSED struct DjuiBase* base) {
djui_base_set_enabled(&sInputboxPort->base, DJUI_HOST_NS_IS_SOCKET);
@@ -40,14 +39,6 @@ static void djui_panel_host_port_text_change(struct DjuiBase* caller) {
}
}
-static void djui_panel_host_knockback_change(UNUSED struct DjuiBase* caller) {
- switch (sKnockbackIndex) {
- case 0: configPlayerKnockbackStrength = 10; break;
- case 1: configPlayerKnockbackStrength = 25; break;
- default: configPlayerKnockbackStrength = 75; break;
- }
-}
-
static void djui_panel_host_do_host(struct DjuiBase* caller) {
if (!djui_panel_host_port_valid()) {
djui_interactable_set_input_focus(&sInputboxPort->base);
@@ -59,11 +50,7 @@ static void djui_panel_host_do_host(struct DjuiBase* caller) {
}
void djui_panel_host_create(struct DjuiBase* caller) {
-#ifdef DISCORD_SDK
- f32 bodyHeight = 32 * 8 + 64 * 2 + 16 * 10;
-#else
- f32 bodyHeight = 32 * 7 + 64 * 2 + 16 * 9;
-#endif
+ f32 bodyHeight = 32 * 3 + 64 * 3 + 16 * 5;
struct DjuiBase* defaultBase = NULL;
struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\H\\#1be700\\O\\#00b3ff\\S\\#ffef00\\T");
@@ -122,39 +109,17 @@ void djui_panel_host_create(struct DjuiBase* caller) {
djui_interactable_hook_click(&button1->base, djui_panel_host_save_create);
}
- char* iChoices[3] = { "Non-solid", "Solid", "Friendly Fire" };
- struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "Player interaction", iChoices, 3, &configPlayerInteraction);
- djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&selectionbox2->base, 1.0f, 32);
+ struct DjuiButton* button1 = djui_button_create(&body->base, "Settings");
+ 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_CENTER, DJUI_VALIGN_TOP);
+ djui_interactable_hook_click(&button1->base, djui_panel_host_settings_create);
- char* kChoices[3] = { "Weak", "Normal", "Too much" };
- sKnockbackIndex = (configPlayerKnockbackStrength <= 20)
- ? 0
- : ((configPlayerKnockbackStrength <= 40) ? 1 : 2);
- struct DjuiSelectionbox* selectionbox3 = djui_selectionbox_create(&body->base, "Knockback strength", kChoices, 3, &sKnockbackIndex);
- djui_base_set_size_type(&selectionbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&selectionbox3->base, 1.0f, 32);
- djui_interactable_hook_value_change(&selectionbox3->base, djui_panel_host_knockback_change);
-
- struct DjuiCheckbox* checkbox1 = djui_checkbox_create(&body->base, "Stay in level after star", &configStayInLevelAfterStar);
- djui_base_set_size_type(&checkbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&checkbox1->base, 1.0f, 32);
-
- struct DjuiCheckbox* checkbox2 = djui_checkbox_create(&body->base, "Skip intro cutscene", &configSkipIntro);
- djui_base_set_size_type(&checkbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&checkbox2->base, 1.0f, 32);
-
- struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "Share lives", &configShareLives);
- djui_base_set_size_type(&checkbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&checkbox3->base, 1.0f, 32);
-
- struct DjuiCheckbox* checkbox4 = djui_checkbox_create(&body->base, "Enable cheats", &configEnableCheats);
- djui_base_set_size_type(&checkbox4->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&checkbox4->base, 1.0f, 32);
-
- struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Bubble on death", &configBubbleDeath);
- djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
- djui_base_set_size(&checkbox5->base, 1.0f, 32);
+ struct DjuiButton* button2 = djui_button_create(&body->base, "Mods");
+ djui_base_set_size_type(&button2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&button2->base, 1.0f, 64);
+ djui_base_set_alignment(&button2->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_TOP);
+ djui_interactable_hook_click(&button2->base, djui_panel_host_mods_create);
struct DjuiRect* rect3 = djui_rect_create(&body->base);
djui_base_set_size_type(&rect3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
diff --git a/src/pc/djui/djui_panel_host_mods.c b/src/pc/djui/djui_panel_host_mods.c
new file mode 100644
index 000000000..3ed691095
--- /dev/null
+++ b/src/pc/djui/djui_panel_host_mods.c
@@ -0,0 +1,33 @@
+#include
+#include "djui.h"
+#include "game/save_file.h"
+#include "pc/network/network.h"
+#include "pc/utils/misc.h"
+#include "pc/configfile.h"
+#include "pc/cheats.h"
+#include "pc/mod_list.h"
+
+void djui_panel_host_mods_create(struct DjuiBase* caller) {
+ f32 bodyHeight = 32 * gModTableLocal.entryCount + 64 * 1 + 16 * (gModTableLocal.entryCount + 1);
+
+ struct DjuiBase* defaultBase = NULL;
+ struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\M\\#1be700\\O\\#00b3ff\\D\\#ffef00\\S");
+ struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel);
+ {
+ for (int i = 0; i < gModTableLocal.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableLocal.entries[i];
+ struct DjuiCheckbox* checkbox = djui_checkbox_create(&body->base, entry->name, &entry->enabled);
+ djui_base_set_size_type(&checkbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox->base, 1.0f, 32);
+ }
+
+ struct DjuiButton* button1 = djui_button_create(&body->base, "Back");
+ djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&button1->base, 1.0f, 64);
+ djui_button_set_style(button1, 1);
+ djui_interactable_hook_click(&button1->base, djui_panel_menu_back);
+ defaultBase = &button1->base;
+ }
+
+ djui_panel_add(caller, &panel->base, defaultBase);
+}
diff --git a/src/pc/djui/djui_panel_host_mods.h b/src/pc/djui/djui_panel_host_mods.h
new file mode 100644
index 000000000..510290cfc
--- /dev/null
+++ b/src/pc/djui/djui_panel_host_mods.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "djui.h"
+
+void djui_panel_host_mods_create(struct DjuiBase* caller);
diff --git a/src/pc/djui/djui_panel_host_settings.c b/src/pc/djui/djui_panel_host_settings.c
new file mode 100644
index 000000000..e770d57e8
--- /dev/null
+++ b/src/pc/djui/djui_panel_host_settings.c
@@ -0,0 +1,69 @@
+#include
+#include "djui.h"
+#include "game/save_file.h"
+#include "pc/network/network.h"
+#include "pc/utils/misc.h"
+#include "pc/configfile.h"
+#include "pc/cheats.h"
+
+static unsigned int sKnockbackIndex = 0;
+
+static void djui_panel_host_settings_knockback_change(UNUSED struct DjuiBase* caller) {
+ switch (sKnockbackIndex) {
+ case 0: configPlayerKnockbackStrength = 10; break;
+ case 1: configPlayerKnockbackStrength = 25; break;
+ default: configPlayerKnockbackStrength = 75; break;
+ }
+}
+
+void djui_panel_host_settings_create(struct DjuiBase* caller) {
+ f32 bodyHeight = 32 * 7 + 64 * 1 + 16 * 7;
+
+ struct DjuiBase* defaultBase = NULL;
+ struct DjuiThreePanel* panel = djui_panel_menu_create(bodyHeight, "\\#ff0800\\S\\#1be700\\E\\#00b3ff\\T\\#ffef00\\T\\#ff0800\\I\\#1be700\\N\\#00b3ff\\G\\#ffef00\\S");
+ struct DjuiFlowLayout* body = (struct DjuiFlowLayout*)djui_three_panel_get_body(panel);
+ {
+ char* iChoices[3] = { "Non-solid", "Solid", "Friendly Fire" };
+ struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(&body->base, "Player interaction", iChoices, 3, &configPlayerInteraction);
+ djui_base_set_size_type(&selectionbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&selectionbox1->base, 1.0f, 32);
+
+ char* kChoices[3] = { "Weak", "Normal", "Too much" };
+ sKnockbackIndex = (configPlayerKnockbackStrength <= 20)
+ ? 0
+ : ((configPlayerKnockbackStrength <= 40) ? 1 : 2);
+ struct DjuiSelectionbox* selectionbox2 = djui_selectionbox_create(&body->base, "Knockback strength", kChoices, 3, &sKnockbackIndex);
+ djui_base_set_size_type(&selectionbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&selectionbox2->base, 1.0f, 32);
+ djui_interactable_hook_value_change(&selectionbox2->base, djui_panel_host_settings_knockback_change);
+
+ struct DjuiCheckbox* checkbox1 = djui_checkbox_create(&body->base, "Stay in level after star", &configStayInLevelAfterStar);
+ djui_base_set_size_type(&checkbox1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox1->base, 1.0f, 32);
+
+ struct DjuiCheckbox* checkbox2 = djui_checkbox_create(&body->base, "Skip intro cutscene", &configSkipIntro);
+ djui_base_set_size_type(&checkbox2->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox2->base, 1.0f, 32);
+
+ struct DjuiCheckbox* checkbox3 = djui_checkbox_create(&body->base, "Share lives", &configShareLives);
+ djui_base_set_size_type(&checkbox3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox3->base, 1.0f, 32);
+
+ struct DjuiCheckbox* checkbox4 = djui_checkbox_create(&body->base, "Enable cheats", &configEnableCheats);
+ djui_base_set_size_type(&checkbox4->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox4->base, 1.0f, 32);
+
+ struct DjuiCheckbox* checkbox5 = djui_checkbox_create(&body->base, "Bubble on death", &configBubbleDeath);
+ djui_base_set_size_type(&checkbox5->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&checkbox5->base, 1.0f, 32);
+
+ struct DjuiButton* button1 = djui_button_create(&body->base, "Back");
+ djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(&button1->base, 1.0f, 64);
+ djui_button_set_style(button1, 1);
+ djui_interactable_hook_click(&button1->base, djui_panel_menu_back);
+ defaultBase = &button1->base;
+ }
+
+ djui_panel_add(caller, &panel->base, defaultBase);
+}
diff --git a/src/pc/djui/djui_panel_host_settings.h b/src/pc/djui/djui_panel_host_settings.h
new file mode 100644
index 000000000..b3be52fa6
--- /dev/null
+++ b/src/pc/djui/djui_panel_host_settings.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "djui.h"
+
+void djui_panel_host_settings_create(struct DjuiBase* caller);
diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c
index 2d01076be..83c3808f2 100644
--- a/src/pc/lua/smlua.c
+++ b/src/pc/lua/smlua.c
@@ -92,8 +92,10 @@ void smlua_init(void) {
// load scripts
LOG_INFO("Loading scripts:");
- for (int i = 0; i < gModEntryCount; i++) {
- struct ModListEntry* entry = &gModEntries[i];
+ struct ModTable* table = (gNetworkType == NT_SERVER) ? &gModTableLocal : &gModTableRemote;
+ for (int i = 0; i < table->entryCount; i++) {
+ struct ModListEntry* entry = &table->entries[i];
+ if (!entry->enabled) { continue; }
LOG_INFO(" %s", entry->path);
smlua_load_script(entry->path);
}
diff --git a/src/pc/mod_list.c b/src/pc/mod_list.c
index a3e97a615..ec3f7b0b6 100644
--- a/src/pc/mod_list.c
+++ b/src/pc/mod_list.c
@@ -6,9 +6,8 @@
#define MAX_SESSION_CHARS 7
-struct ModListEntry* gModEntries = NULL;
-u16 gModEntryCount = 0;
-u64 gModTotalSize = 0;
+struct ModTable gModTableLocal = { .entries = NULL, .entryCount = 0, .totalSize = 0, .isRemote = false };
+struct ModTable gModTableRemote = { .entries = NULL, .entryCount = 0, .totalSize = 0, .isRemote = true };
static char sTmpSession[MAX_SESSION_CHARS] = { 0 };
static char sTmpPath[PATH_MAX] = { 0 };
@@ -18,82 +17,6 @@ static bool acceptable_file(char* string) {
return (string != NULL && !strcmp(string, ".lua"));
}
-void mod_list_alloc(u16 count) {
- mod_list_clear();
- gModEntryCount = count;
- gModEntries = (struct ModListEntry*)calloc(gModEntryCount, sizeof(struct ModListEntry));
-}
-
-void mod_list_add(u16 index, char* name, size_t size, bool tmpFile) {
- if (!acceptable_file(name)) { return; }
- struct ModListEntry* entry = &gModEntries[index];
- entry->name = name;
- entry->size = size;
- gModTotalSize += size;
-
- if (tmpFile) {
- snprintf(entry->path, PATH_MAX - 1, "%s/%s-%s", sTmpPath, sTmpSession, name);
- } else {
- snprintf(entry->path, PATH_MAX - 1, "%s/%s", MOD_PATH, name);
- }
-
- entry->fp = fopen(entry->path, tmpFile ? "wb" : "rb");
-
- if (!tmpFile) {
- fseek(entry->fp, 0, SEEK_END);
- entry->size = ftell(entry->fp);
- fseek(entry->fp, 0, SEEK_SET);
- }
-
- entry->complete = !tmpFile;
-}
-
-void mod_list_load(void) {
- struct dirent* dir;
- DIR* d = opendir(MOD_PATH);
- if (!d) { closedir(d); return; }
-
- u16 count = 0;
- while ((dir = readdir(d)) != NULL) {
- if (!acceptable_file(dir->d_name)) { continue; }
- count++;
- }
-
- mod_list_alloc(count);
-
- rewinddir(d);
- u16 index = 0;
-
- LOG_INFO("Loading mods:");
- gModTotalSize = 0;
- while ((dir = readdir(d)) != NULL) {
- if (!acceptable_file(dir->d_name)) { continue; }
- LOG_INFO(" %s", dir->d_name);
- mod_list_add(index++, strdup(dir->d_name), 0, false);
- }
-
- closedir(d);
-}
-
-void mod_list_clear(void) {
- for (int i = 0; i < gModEntryCount; i++) {
- struct ModListEntry* entry = &gModEntries[i];
- if (entry->name != NULL) {
- free(entry->name);
- entry->name = NULL;
- }
- if (entry->fp != NULL) {
- fclose(entry->fp);
- entry->fp = NULL;
- }
- entry->size = 0;
- }
- if (gModEntries != NULL) {
- free(gModEntries);
- gModEntries = NULL;
- }
- gModEntryCount = 0;
-}
static void mod_list_delete_tmp(void) {
struct dirent* dir;
@@ -122,13 +45,98 @@ static void mod_list_delete_tmp(void) {
closedir(d);
}
+////////////////////////////////////////////////
+
+void mod_list_add(u16 index, char* name, size_t size, bool tmpFile) {
+ if (!acceptable_file(name)) { return; }
+ struct ModTable* table = tmpFile ? &gModTableRemote : &gModTableLocal;
+
+ struct ModListEntry* entry = &table->entries[index];
+ entry->name = name;
+ entry->size = size;
+ table->totalSize += size;
+
+ if (tmpFile) {
+ snprintf(entry->path, PATH_MAX - 1, "%s/%s-%s", sTmpPath, sTmpSession, name);
+ }
+ else {
+ snprintf(entry->path, PATH_MAX - 1, "%s/%s", MOD_PATH, name);
+ }
+
+ entry->fp = fopen(entry->path, tmpFile ? "wb" : "rb");
+
+ if (!tmpFile) {
+ fseek(entry->fp, 0, SEEK_END);
+ entry->size = ftell(entry->fp);
+ fseek(entry->fp, 0, SEEK_SET);
+ }
+
+ entry->complete = !tmpFile;
+ entry->enabled = false;
+}
+
+void mod_table_clear(struct ModTable* table) {
+ for (int i = 0; i < table->entryCount; i++) {
+ struct ModListEntry* entry = &table->entries[i];
+ if (entry->name != NULL) {
+ free(entry->name);
+ entry->name = NULL;
+ }
+ if (entry->fp != NULL) {
+ fclose(entry->fp);
+ entry->fp = NULL;
+ }
+ entry->size = 0;
+ }
+ if (table->entries != NULL) {
+ free(table->entries);
+ table->entries = NULL;
+ }
+ table->entryCount = 0;
+ table->totalSize = 0;
+}
+
+void mod_list_alloc(struct ModTable* table, u16 count) {
+ mod_table_clear(table);
+ table->entryCount = count;
+ table->entries = (struct ModListEntry*)calloc(count, sizeof(struct ModListEntry));
+}
+
+static void mod_list_load_local(void) {
+ struct dirent* dir;
+ DIR* d = opendir(MOD_PATH);
+ if (!d) { closedir(d); return; }
+
+ u16 count = 0;
+ while ((dir = readdir(d)) != NULL) {
+ if (!acceptable_file(dir->d_name)) { continue; }
+ count++;
+ }
+
+ mod_list_alloc(&gModTableLocal, count);
+
+ rewinddir(d);
+ u16 index = 0;
+
+ LOG_INFO("Loading mods:");
+ while ((dir = readdir(d)) != NULL) {
+ if (!acceptable_file(dir->d_name)) { continue; }
+ LOG_INFO(" %s", dir->d_name);
+ mod_list_add(index++, strdup(dir->d_name), 0, false);
+ }
+
+ closedir(d);
+}
+
void mod_list_init(void) {
snprintf(sTmpSession, MAX_SESSION_CHARS, "%06X", (u32)(rand() % 0xFFFFFF));
snprintf(sTmpPath, PATH_MAX - 1, "%s", fs_get_write_path("tmp"));
if (!fs_sys_dir_exists(sTmpPath)) { fs_sys_mkdir(sTmpPath); }
+ mod_list_load_local();
}
void mod_list_shutdown(void) {
- mod_list_clear();
+ mod_table_clear(&gModTableLocal);
+ mod_table_clear(&gModTableRemote);
mod_list_delete_tmp();
-}
\ No newline at end of file
+}
diff --git a/src/pc/mod_list.h b/src/pc/mod_list.h
index 49dbe840a..181d67880 100644
--- a/src/pc/mod_list.h
+++ b/src/pc/mod_list.h
@@ -8,24 +8,33 @@
#define MOD_PATH "./mods"
+#pragma pack(1)
struct ModListEntry {
char* name;
FILE* fp;
char path[PATH_MAX];
size_t size;
u64 curOffset;
+ u16 remoteIndex;
bool tmp;
bool complete;
+ bool enabled;
};
-extern struct ModListEntry* gModEntries;
-extern u16 gModEntryCount;
-extern u64 gModTotalSize;
-void mod_list_alloc(u16 count);
+#pragma pack(1)
+struct ModTable {
+ struct ModListEntry* entries;
+ u16 entryCount;
+ u8 isRemote;
+ u64 totalSize;
+};
+
+extern struct ModTable gModTableLocal;
+extern struct ModTable gModTableRemote;
+
void mod_list_add(u16 index, char* name, size_t size, bool tmpFile);
-
-void mod_list_load(void);
-void mod_list_clear(void);
+void mod_table_clear(struct ModTable* table);
+void mod_list_alloc(struct ModTable* table, u16 count);
void mod_list_init(void);
void mod_list_shutdown(void);
diff --git a/src/pc/network/network.c b/src/pc/network/network.c
index 10c93b7c0..8991db987 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -97,7 +97,6 @@ bool network_init(enum NetworkType inNetworkType) {
gNetworkType = inNetworkType;
if (gNetworkType == NT_SERVER) {
- mod_list_load();
smlua_init();
network_player_connected(NPT_LOCAL, 0, configPlayerModel, configPlayerPalette, configPlayerName);
diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h
index 4822017d5..e91ca022b 100644
--- a/src/pc/network/packets/packet.h
+++ b/src/pc/network/packets/packet.h
@@ -289,14 +289,15 @@ void network_receive_player_settings(struct Packet* p);
// packet_mod_list.c
void network_send_mod_list_request(void);
-void network_receive_mod_list_request(struct Packet* p);
+void network_receive_mod_list_request(UNUSED struct Packet* p);
void network_send_mod_list(void);
void network_receive_mod_list(struct Packet* p);
// packet_download.c
-void network_send_download_request(u16 index, u64 offset);
+void network_send_next_download_request(void);
+void network_send_download_request(u16 clientIndex, u16 serverIndex, u64 offset);
void network_receive_download_request(struct Packet* p);
-void network_send_download(u16 index, u64 offset);
+void network_send_download(u16 clientIndex, u16 serverIndex, u64 offset);
void network_receive_download(struct Packet* p);
diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c
index fb313c3c6..158f936be 100644
--- a/src/pc/network/packets/packet_download.c
+++ b/src/pc/network/packets/packet_download.c
@@ -11,32 +11,33 @@ static bool sWaitingForOffset[OFFSET_COUNT] = { 0 };
u64 sTotalDownloadBytes = 0;
extern float gDownloadProgress;
-static void network_send_next_download_request(void) {
+void network_send_next_download_request(void) {
SOFT_ASSERT(gNetworkType == NT_CLIENT);
- for (int i = 0; i < gModEntryCount; i++) {
- struct ModListEntry* entry = &gModEntries[i];
+ for (int i = 0; i < gModTableRemote.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableRemote.entries[i];
if (entry->complete) { continue; }
- network_send_download_request(i, entry->curOffset);
+ network_send_download_request(i, entry->remoteIndex, entry->curOffset);
return;
}
network_send_join_request();
}
-void network_send_download_request(u16 index, u64 offset) {
+void network_send_download_request(u16 clientIndex, u16 serverIndex, u64 offset) {
SOFT_ASSERT(gNetworkType == NT_CLIENT);
struct Packet p = { 0 };
packet_init(&p, PACKET_DOWNLOAD_REQUEST, true, PLMT_NONE);
- packet_write(&p, &index, sizeof(u16));
+ packet_write(&p, &clientIndex, sizeof(u16));
+ packet_write(&p, &serverIndex, sizeof(u16));
packet_write(&p, &offset, sizeof(u64));
- if (index == 0 && offset == 0) {
+ if (clientIndex == 0 && offset == 0) {
sTotalDownloadBytes = 0;
gDownloadProgress = 0;
}
- struct ModListEntry* entry = &gModEntries[index];
+ struct ModListEntry* entry = &gModTableRemote.entries[clientIndex];
for (int i = 0; i < OFFSET_COUNT; i++) {
sOffset[i] = offset + CHUNK_SIZE * i;
sWaitingForOffset[i] = (sOffset[i] < entry->size);
@@ -48,40 +49,42 @@ void network_send_download_request(u16 index, u64 offset) {
void network_receive_download_request(struct Packet* p) {
SOFT_ASSERT(gNetworkType == NT_SERVER);
- u16 index;
+ u16 clientIndex;
+ u16 serverIndex;
u64 offset;
- packet_read(p, &index, sizeof(u16));
+ packet_read(p, &clientIndex, sizeof(u16));
+ packet_read(p, &serverIndex, sizeof(u16));
packet_read(p, &offset, sizeof(u64));
- struct ModListEntry* entry = &gModEntries[index];
- if (index >= gModEntryCount) {
- LOG_ERROR("Requested download of invalid index %u:%llu", index, offset);
+ struct ModListEntry* entry = &gModTableLocal.entries[serverIndex];
+ if (serverIndex >= gModTableLocal.entryCount) {
+ LOG_ERROR("Requested download of invalid index %u:%llu", serverIndex, offset);
return;
}
for (int i = 0; i < OFFSET_COUNT; i++) {
u64 o = offset + CHUNK_SIZE * i;
if (o >= entry->size) { break; }
- network_send_download(index, o);
+ network_send_download(clientIndex, serverIndex, o);
}
}
-void network_send_download(u16 index, u64 offset) {
+void network_send_download(u16 clientIndex, u16 serverIndex, u64 offset) {
SOFT_ASSERT(gNetworkType == NT_SERVER);
- if (index >= gModEntryCount) {
- LOG_ERROR("Requested download of invalid index %u:%llu", index, offset);
+ if (serverIndex >= gModTableLocal.entryCount) {
+ LOG_ERROR("Requested download of invalid index %u:%llu", serverIndex, offset);
return;
}
- struct ModListEntry* entry = &gModEntries[index];
+ struct ModListEntry* entry = &gModTableLocal.entries[serverIndex];
if (offset >= entry->size) {
- LOG_ERROR("Requested download of invalid offset %u:%llu", index, offset);
+ LOG_ERROR("Requested download of invalid offset %u:%llu", serverIndex, offset);
return;
}
if (entry->fp == NULL) {
- LOG_ERROR("Requested download of invalid file pointer %u:%llu", index, offset);
+ LOG_ERROR("Requested download of invalid file pointer %u:%llu", serverIndex, offset);
return;
}
@@ -97,7 +100,8 @@ void network_send_download(u16 index, u64 offset) {
struct Packet p = { 0 };
packet_init(&p, PACKET_DOWNLOAD, true, PLMT_NONE);
- packet_write(&p, &index, sizeof(u16));
+ packet_write(&p, &clientIndex, sizeof(u16));
+ packet_write(&p, &serverIndex, sizeof(u16));
packet_write(&p, &offset, sizeof(u64));
packet_write(&p, &chunkSize, sizeof(u16));
packet_write(&p, chunk, chunkSize * sizeof(u8));
@@ -112,34 +116,36 @@ void network_receive_download(struct Packet* p) {
return;
}
- u16 index;
+ u16 clientIndex;
+ u16 serverIndex;
u64 offset;
u16 chunkSize;
u8 chunk[400] = { 0 };
- packet_read(p, &index, sizeof(u16));
+ packet_read(p, &clientIndex, sizeof(u16));
+ packet_read(p, &serverIndex, sizeof(u16));
packet_read(p, &offset, sizeof(u64));
packet_read(p, &chunkSize, sizeof(u16));
packet_read(p, chunk, chunkSize * sizeof(u8));
- if (index >= gModEntryCount) {
- LOG_ERROR("Received download of invalid index %u:%llu", index, offset);
+ if (clientIndex >= gModTableRemote.entryCount) {
+ LOG_ERROR("Received download of invalid index %u:%llu", clientIndex, offset);
return;
}
- struct ModListEntry* entry = &gModEntries[index];
+ struct ModListEntry* entry = &gModTableRemote.entries[clientIndex];
if (offset >= entry->size) {
- LOG_ERROR("Received download of invalid offset %u:%llu", index, offset);
+ LOG_ERROR("Received download of invalid offset %u:%llu", clientIndex, offset);
return;
}
if (entry->fp == NULL) {
- LOG_ERROR("Received download of invalid file pointer %u:%llu", index, offset);
+ LOG_ERROR("Received download of invalid file pointer %u:%llu", clientIndex, offset);
return;
}
if ((offset + chunkSize) > entry->size) {
- LOG_ERROR("Received download of invalid chunk size %u:%llu:%u", index, offset, chunkSize);
+ LOG_ERROR("Received download of invalid chunk size %u:%llu:%u", clientIndex, offset, chunkSize);
return;
}
@@ -165,7 +171,7 @@ void network_receive_download(struct Packet* p) {
// update progress
sTotalDownloadBytes += chunkSize;
- gDownloadProgress = (float)sTotalDownloadBytes / (float)gModTotalSize;
+ gDownloadProgress = (float)sTotalDownloadBytes / (float)gModTableRemote.totalSize;
if (!waiting) {
// check if we're finished with this file
diff --git a/src/pc/network/packets/packet_mod_list.c b/src/pc/network/packets/packet_mod_list.c
index debb1eebd..4656e18ec 100644
--- a/src/pc/network/packets/packet_mod_list.c
+++ b/src/pc/network/packets/packet_mod_list.c
@@ -1,40 +1,24 @@
#include
#include "../network.h"
#include "pc/mod_list.h"
+#include "pc/djui/djui.h"
#include "pc/debuglog.h"
void network_send_mod_list_request(void) {
SOFT_ASSERT(gNetworkType == NT_CLIENT);
- mod_list_shutdown();
+ mod_table_clear(&gModTableRemote);
struct Packet p = { 0 };
packet_init(&p, PACKET_MOD_LIST_REQUEST, true, PLMT_NONE);
- char version[MAX_VERSION_LENGTH] = { 0 };
- snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
- packet_write(&p, &version, sizeof(u8) * MAX_VERSION_LENGTH);
-
network_send_to((gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0, &p);
LOG_INFO("sending mod list request");
}
-void network_receive_mod_list_request(struct Packet* p) {
+void network_receive_mod_list_request(UNUSED struct Packet* p) {
SOFT_ASSERT(gNetworkType == NT_SERVER);
LOG_INFO("received mod list request");
- char version[MAX_VERSION_LENGTH] = { 0 };
- snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
-
- char remoteVersion[MAX_VERSION_LENGTH] = { 0 };
- packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH);
- LOG_INFO("client has version: %s", remoteVersion);
-
- if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) {
- LOG_INFO("client version mismatch: %s != %s", remoteVersion, version);
- // TODO: send version mismatch packet
- return;
- }
-
network_send_mod_list();
}
@@ -44,11 +28,24 @@ void network_send_mod_list(void) {
struct Packet p = { 0 };
packet_init(&p, PACKET_MOD_LIST, true, PLMT_NONE);
- packet_write(&p, &gModEntryCount, sizeof(u16));
- LOG_INFO("sent mod list (%u):", gModEntryCount);
- for (int i = 0; i < gModEntryCount; i++) {
- struct ModListEntry* entry = &gModEntries[i];
+ char version[MAX_VERSION_LENGTH] = { 0 };
+ snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
+ LOG_INFO("sending version: %s", version);
+ packet_write(&p, &version, sizeof(u8) * MAX_VERSION_LENGTH);
+
+ u16 activeCount = 0;
+ for (u16 i = 0; i < gModTableLocal.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableLocal.entries[i];
+ if (entry->enabled) { activeCount++; }
+ }
+
+ packet_write(&p, &activeCount, sizeof(u16));
+ LOG_INFO("sent mod list (%u):", gModTableLocal.entryCount);
+ for (u16 i = 0; i < gModTableLocal.entryCount; i++) {
+ struct ModListEntry* entry = &gModTableLocal.entries[i];
+ if (!entry->enabled) { continue; }
u16 nameLength = strlen(entry->name);
+ packet_write(&p, &i, sizeof(u16));
packet_write(&p, &nameLength, sizeof(u16));
packet_write(&p, entry->name, sizeof(u8) * nameLength);
packet_write(&p, &entry->size, sizeof(u16));
@@ -60,7 +57,7 @@ void network_send_mod_list(void) {
void network_receive_mod_list(struct Packet* p) {
SOFT_ASSERT(gNetworkType == NT_CLIENT);
SOFT_ASSERT(p->localIndex == UNKNOWN_LOCAL_INDEX);
- if (gModEntries != NULL) {
+ if (gModTableRemote.entries != NULL) {
LOG_INFO("received mod list after allocating");
return;
}
@@ -69,13 +66,32 @@ void network_receive_mod_list(struct Packet* p) {
gNetworkServerAddr = network_duplicate_address(0);
}
+ char version[MAX_VERSION_LENGTH] = { 0 };
+ snprintf(version, MAX_VERSION_LENGTH, "%s", get_version());
+ LOG_INFO("client has version: %s", version);
+
+ // verify version
+ char remoteVersion[MAX_VERSION_LENGTH] = { 0 };
+ 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");
+ 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;
+ }
+
u16 modEntryCount = 0;
packet_read(p, &modEntryCount, sizeof(u16));
- mod_list_alloc(modEntryCount);
+ mod_list_alloc(&gModTableRemote, modEntryCount);
LOG_INFO("received mod list (%u):", modEntryCount);
- gModTotalSize = 0;
for (int i = 0; i < modEntryCount; i++) {
+ u16 remoteIndex = 0;
+ packet_read(p, &remoteIndex, sizeof(u16));
+
u16 nameLength = 0;
packet_read(p, &nameLength, sizeof(u16));
@@ -86,12 +102,11 @@ void network_receive_mod_list(struct Packet* p) {
packet_read(p, &size, sizeof(u16));
mod_list_add(i, name, size, true);
+ gModTableRemote.entries[i].enabled = true;
+ gModTableRemote.entries[i].remoteIndex = remoteIndex;
+
LOG_INFO(" '%s': %u", name, size);
}
- if (modEntryCount <= 0) {
- network_send_join_request();
- } else {
- network_send_download_request(0, 0);
- }
+ network_send_next_download_request();
}
diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c
index 9c6bd2d01..d15de6d05 100644
--- a/src/pc/pc_main.c
+++ b/src/pc/pc_main.c
@@ -221,6 +221,7 @@ void main_func(void) {
const char *userpath = gCLIOpts.SavePath[0] ? gCLIOpts.SavePath : sys_user_path();
fs_init(sys_ropaths, gamedir, userpath);
+ mod_list_init();
configfile_load(configfile_name());
if (configPlayerModel >= CT_MAX) { configPlayerModel = 0; }
if (configPlayerPalette >= 16) { configPlayerPalette = 0; }
@@ -307,7 +308,6 @@ void main_func(void) {
audio_init();
sound_init();
- mod_list_init();
network_player_init();
thread5_game_loop(NULL);