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);