Reading headers from lua files

Fix up warnings on Linux
Disabled mod checkboxes when an incompatible one is enabled
Display descriptions when hovering over a mod
This commit is contained in:
MysterD 2022-01-27 23:47:29 -08:00
parent b26f3e43fc
commit 913ea4c5c0
13 changed files with 292 additions and 21 deletions

View File

@ -557,6 +557,9 @@ BACKEND_LDFLAG0S :=
SDL1_USED := 0
SDL2_USED := 0
# suppress warnings
BACKEND_CFLAGS += -Wno-format-truncation
# for now, it's either SDL+GL or DXGI+DirectX, so choose based on WAPI
ifeq ($(WINDOW_API),DXGI)
DXBITS := `cat $(ENDIAN_BITWIDTH) | tr ' ' '\n' | tail -1`

View File

@ -1,3 +1,6 @@
-- name: Character Movesets
-- incompatible: moveset
-- description: Gives each character unique abilities and stats.
gStateExtras = {}
for i=0,(MAX_PLAYERS-1) do

View File

@ -1,3 +1,7 @@
-- name: Extended Moveset
-- incompatible: moveset
-- description: Adds various new moves from games like Sunshine and Odyssey without replacing any existing ones.\n\nOriginal author: TheGag96
------------------------
-- initialize actions --
------------------------

View File

@ -7,18 +7,122 @@
#include "pc/cheats.h"
#include "pc/mod_list.h"
static struct DjuiFlowLayout* sModPanelBody = NULL;
static struct DjuiThreePanel* sDescriptionPanel = NULL;
static struct DjuiText* sTooltip = NULL;
static void djui_panel_host_mods_description_create() {
f32 bodyHeight = 600;
struct DjuiThreePanel* panel = djui_three_panel_create(&gDjuiRoot->base, 64, bodyHeight, 0);
djui_base_set_alignment(&panel->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_CENTER);
djui_base_set_size_type(&panel->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_RELATIVE);
djui_base_set_size(&panel->base, DJUI_DEFAULT_PANEL_WIDTH, 1.0f);
djui_base_set_color(&panel->base, 0, 0, 0, 240);
djui_base_set_border_color(&panel->base, 0, 0, 0, 200);
djui_base_set_border_width(&panel->base, 8);
djui_base_set_padding(&panel->base, 16, 16, 16, 16);
{
struct DjuiFlowLayout* body = djui_flow_layout_create(&panel->base);
djui_base_set_alignment(&body->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER);
djui_base_set_size_type(&body->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
djui_base_set_size(&body->base, 1.0f, 1.0f);
djui_base_set_color(&body->base, 0, 0, 0, 0);
djui_flow_layout_set_margin(body, 16);
djui_flow_layout_set_flow_direction(body, DJUI_FLOW_DIR_DOWN);
struct DjuiText* description = djui_text_create(&panel->base, "");
djui_base_set_size_type(&description->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
djui_base_set_size(&description->base, 1.0f, 1.0f);
djui_base_set_color(&description->base, 222, 222, 222, 255);
djui_text_set_alignment(description, DJUI_HALIGN_LEFT, DJUI_VALIGN_CENTER);
sTooltip = description;
}
sDescriptionPanel = panel;
}
static void djui_mod_checkbox_set_style(struct DjuiBase* base) {
struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base;
int val = base->enabled ? 200 : 100;
djui_base_set_border_color(&checkbox->rect->base, 173, 173, 173, 255);
djui_base_set_color(&checkbox->rect->base, 0, 0, 0, 0);
djui_base_set_color(&checkbox->text->base, val, val, val, 255);
djui_base_set_color(&checkbox->rectValue->base, val, val, val, 255);
}
static void djui_mod_checkbox_on_hover(struct DjuiBase* base) {
struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base;
djui_base_set_border_color(&checkbox->rect->base, 0, 120, 215, 255);
djui_base_set_color(&checkbox->text->base, 229, 241, 251, 255);
djui_base_set_color(&checkbox->rectValue->base, 229, 241, 251, 255);
char* description = "";
if (base->tag >= 0 && base->tag < gModTableLocal.entryCount) {
char* d = gModTableLocal.entries[base->tag].description;
if (d != NULL) {
description = gModTableLocal.entries[base->tag].description;
}
}
djui_text_set_text(sTooltip, description);
}
static void djui_mod_checkbox_on_click(UNUSED struct DjuiBase* base) {
mod_list_update_selectable();
u16 index = 0;
struct DjuiBaseChild* node = sModPanelBody->base.child;
while (node != NULL) {
if (index >= gModTableLocal.entryCount) { break; }
struct ModListEntry* entry = &gModTableLocal.entries[index];
djui_base_set_enabled(node->base, entry->selectable);
djui_mod_checkbox_set_style(node->base);
// iterate
index++;
node = node->next;
}
}
static void djui_mod_checkbox_on_hover_end(struct DjuiBase* base) {
struct DjuiCheckbox* checkbox = (struct DjuiCheckbox*)base;
djui_base_set_border_color(&checkbox->rect->base, 173, 173, 173, 255);
djui_base_set_color(&checkbox->rect->base, 0, 0, 0, 0);
djui_base_set_color(&checkbox->text->base, 200, 200, 200, 255);
djui_base_set_color(&checkbox->rectValue->base, 200, 200, 200, 255);
djui_text_set_text(sTooltip, "");
}
static void djui_panel_host_mods_destroy(struct DjuiBase* base) {
struct DjuiThreePanel* threePanel = (struct DjuiThreePanel*)base;
free(threePanel);
if (sDescriptionPanel != NULL) {
djui_base_destroy(&sDescriptionPanel->base);
sDescriptionPanel = NULL;
}
}
void djui_panel_host_mods_create(struct DjuiBase* caller) {
f32 bodyHeight = 32 * gModTableLocal.entryCount + 64 * 1 + 16 * (gModTableLocal.entryCount + 1);
mod_list_update_selectable();
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);
sModPanelBody = body;
{
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);
struct DjuiCheckbox* checkbox = djui_checkbox_create(&body->base, entry->displayName ? entry->displayName : entry->name, &entry->enabled);
checkbox->base.tag = i;
djui_base_set_size_type(&checkbox->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&checkbox->base, 1.0f, 32);
djui_base_set_enabled(&checkbox->base, entry->selectable);
djui_mod_checkbox_set_style(&checkbox->base);
djui_interactable_hook_hover(&checkbox->base, djui_mod_checkbox_on_hover, djui_mod_checkbox_on_hover_end);
djui_interactable_hook_click(&checkbox->base, djui_mod_checkbox_on_click);
}
struct DjuiButton* button1 = djui_button_create(&body->base, "Back");
@ -29,5 +133,8 @@ void djui_panel_host_mods_create(struct DjuiBase* caller) {
defaultBase = &button1->base;
}
panel->base.destroy = djui_panel_host_mods_destroy;
djui_panel_add(caller, &panel->base, defaultBase);
djui_panel_host_mods_description_create();
}

View File

@ -3,6 +3,7 @@
#include <unistd.h>
#include "mod_list.h"
#include "pc/fs/fs.h"
#include "pc/utils/misc.h"
#include "pc/debuglog.h"
#define MAX_SESSION_CHARS 7
@ -20,7 +21,6 @@ static bool acceptable_file(char* string) {
return (string != NULL && !strcmp(string, ".lua"));
}
static void mod_list_delete_tmp(void) {
struct dirent* dir;
DIR* d = opendir(sTmpPath);
@ -86,7 +86,48 @@ void mod_list_add_tmp(u16 index, u16 remoteIndex, char* name, size_t size) {
entry->remoteIndex = remoteIndex;
entry->complete = false;
entry->enabled = true;
entry->selectable = false;
}
static char* extract_lua_field(char* fieldName, char* buffer) {
size_t length = strlen(fieldName);
if (strncmp(fieldName, buffer, length) == 0) {
char* s = &buffer[length];
while (*s == ' ' || *s == '\t') { s++; }
return s;
}
return NULL;
}
static void extract_lua_fields(struct ModListEntry* entry) {
FILE* f = entry->fp;
char buffer[512] = { 0 };
entry->displayName = NULL;
entry->incompatible = NULL;
entry->description = NULL;
while (!feof(f)) {
file_get_line(buffer, 512, f);
// no longer in header
if (buffer[0] != '-' || buffer[1] != '-') {
return;
}
// extract the field
char* extracted = NULL;
if (entry->displayName == NULL && (extracted = extract_lua_field("-- name:", buffer))) {
entry->displayName = calloc(33, sizeof(char));
snprintf(entry->displayName, 32, "%s", extracted);
} else if (entry->incompatible == NULL && (extracted = extract_lua_field("-- incompatible:", buffer))) {
entry->incompatible = calloc(257, sizeof(char));
snprintf(entry->incompatible, 256, "%s", extracted);
} else if (entry->description == NULL && (extracted = extract_lua_field("-- description:", buffer))) {
entry->description = calloc(513, sizeof(char));
snprintf(entry->description, 512, "%s", extracted);
}
}
}
static void mod_list_add_local(u16 index, const char* path, char* name) {
@ -101,6 +142,8 @@ static void mod_list_add_local(u16 index, const char* path, char* name) {
snprintf(entry->path, PATH_MAX - 1, "%s/%s", path, name);
entry->fp = fopen(entry->path, "rb");
extract_lua_fields(entry);
fseek(entry->fp, 0, SEEK_END);
entry->size = ftell(entry->fp);
table->totalSize += entry->size;
@ -108,6 +151,7 @@ static void mod_list_add_local(u16 index, const char* path, char* name) {
entry->complete = true;
entry->enabled = false;
entry->selectable = true;
}
void mod_table_clear(struct ModTable* table) {
@ -117,16 +161,34 @@ void mod_table_clear(struct ModTable* table) {
free(entry->name);
entry->name = NULL;
}
if (entry->displayName != NULL) {
free(entry->displayName);
entry->displayName = NULL;
}
if (entry->incompatible != NULL) {
free(entry->incompatible);
entry->incompatible = NULL;
}
if (entry->description != NULL) {
free(entry->description);
entry->description = 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;
}
@ -137,6 +199,52 @@ void mod_list_alloc(struct ModTable* table, u16 count) {
table->entries = (struct ModListEntry*)calloc(count, sizeof(struct ModListEntry));
}
static bool mod_list_incompatible_match(struct ModListEntry* a, struct ModListEntry* b) {
if (a->incompatible == NULL || b->incompatible == NULL) {
return false;
}
char* ai = a->incompatible;
char* bi = b->incompatible;
char* atoken = NULL;
char* btoken = NULL;
while ((atoken = strtok(ai, " "))) {
while((btoken = strtok(bi, " "))) {
if (!strcmp(atoken, btoken)) {
return true;
}
}
}
return false;
}
void mod_list_update_selectable(void) {
// reset selectable value
for (int i = 0; i < gModTableLocal.entryCount; i++) {
struct ModListEntry* entry = &gModTableLocal.entries[i];
entry->selectable = true;
}
// figure out which ones to deselect
for (int i = 0; i < gModTableLocal.entryCount; i++) {
struct ModListEntry* entry = &gModTableLocal.entries[i];
if (entry->enabled) { continue; }
for (int j = 0; j < gModTableLocal.entryCount; j++) {
if (j == i) { continue; }
struct ModListEntry* entry2 = &gModTableLocal.entries[j];
if (!entry2->enabled) { continue; }
if (mod_list_incompatible_match(entry, entry2)) {
entry->selectable = false;
break;
}
}
}
}
static void mod_list_load_local(const char* path) {
if (!fs_sys_dir_exists(path)) { return; }
struct ModTable* table = &gModTableLocal;
@ -188,6 +296,8 @@ void mod_list_init(void) {
mod_table_clear(&gModTableLocal);
mod_list_load_local(userModPath);
mod_list_load_local(MOD_PATH);
mod_list_update_selectable();
}
void mod_list_shutdown(void) {

View File

@ -17,9 +17,13 @@ struct ModListEntry {
size_t size;
u64 curOffset;
u16 remoteIndex;
char* displayName;
char* incompatible;
char* description;
bool tmp;
bool complete;
bool enabled;
bool selectable;
};
#pragma pack(1)
@ -37,6 +41,8 @@ void mod_list_add_tmp(u16 index, u16 remoteIndex, char* name, size_t size);
void mod_table_clear(struct ModTable* table);
void mod_list_alloc(struct ModTable* table, u16 count);
void mod_list_update_selectable(void);
void mod_list_init(void);
void mod_list_shutdown(void);

View File

@ -15,7 +15,7 @@ static void on_activity_update_callback(UNUSED void* data, enum EDiscordResult r
}
static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult result, struct DiscordLobby* lobby) {
LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_callback returned %d, lobby %lld, owner %lld", result, lobby->id, lobby->owner_id);
LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_callback returned %d, lobby %ld, owner %ld", result, lobby->id, lobby->owner_id);
DISCORD_REQUIRE(result);
if (gNetworkType != NT_NONE) {
LOGFILE_ERROR(LFT_DISCORD, "Joined lobby when already connected somewhere!");
@ -25,7 +25,7 @@ static void on_activity_join_callback(UNUSED void* data, enum EDiscordResult res
network_init(NT_CLIENT);
gCurActivity.type = DiscordActivityType_Playing;
snprintf(gCurActivity.party.id, 128, "%lld", lobby->id);
snprintf(gCurActivity.party.id, 128, "%ld", lobby->id);
gCurActivity.party.size.current_size = 2;
gCurActivity.party.size.max_size = lobby->capacity;
@ -57,7 +57,7 @@ static void on_activity_join_request_callback(UNUSED void* data, enum EDiscordRe
}
static void on_activity_join_request(UNUSED void* data, struct DiscordUser* user) {
LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request from %lld", user->id);
LOGFILE_INFO(LFT_DISCORD, "> on_activity_join_request from %ld", user->id);
//app.activities->send_request_reply(app.activities, user->id, DiscordActivityJoinRequestReply_Yes, NULL, on_activity_join_request_callback);
}

View File

@ -48,14 +48,14 @@ void ns_discord_save_id(u8 localId, s64 networkId) {
assert(localId > 0);
assert(localId < MAX_PLAYERS);
gNetworkUserIds[localId] = (networkId == 0) ? gNetworkUserIds[0] : networkId;
LOGFILE_INFO(LFT_DISCORD, "saved user id %d == %lld", localId, gNetworkUserIds[localId]);
LOGFILE_INFO(LFT_DISCORD, "saved user id %d == %ld", localId, gNetworkUserIds[localId]);
}
void ns_discord_clear_id(u8 localId) {
if (localId == 0) { return; }
assert(localId < MAX_PLAYERS);
gNetworkUserIds[localId] = 0;
LOGFILE_INFO(LFT_DISCORD, "cleared user id %d == %lld", localId, gNetworkUserIds[localId]);
LOGFILE_INFO(LFT_DISCORD, "cleared user id %d == %ld", localId, gNetworkUserIds[localId]);
}
void discord_network_init(int64_t lobbyId) {
@ -68,5 +68,5 @@ void discord_network_shutdown(void) {
app.lobbies->flush_network(app.lobbies);
if (gCurLobbyId == 0) { return; }
app.lobbies->disconnect_network(app.lobbies, gCurLobbyId);
LOGFILE_INFO(LFT_DISCORD, "shutdown network, lobby = %lld", gCurLobbyId);
LOGFILE_INFO(LFT_DISCORD, "shutdown network, lobby = %ld", gCurLobbyId);
}

View File

@ -35,20 +35,20 @@ static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult resu
}
DISCORD_REQUIRE(result);
LOGFILE_INFO(LFT_DISCORD, "Lobby id: %lld", lobby->id);
LOGFILE_INFO(LFT_DISCORD, "Lobby id: %ld", lobby->id);
LOGFILE_INFO(LFT_DISCORD, "Lobby type: %u", lobby->type);
LOGFILE_INFO(LFT_DISCORD, "Lobby owner id: %lld", lobby->owner_id);
LOGFILE_INFO(LFT_DISCORD, "Lobby owner id: %ld", lobby->owner_id);
LOGFILE_INFO(LFT_DISCORD, "Lobby secret: %s", lobby->secret);
LOGFILE_INFO(LFT_DISCORD, "Lobby capacity: %u", lobby->capacity);
LOGFILE_INFO(LFT_DISCORD, "Lobby locked: %d", lobby->locked);
gCurActivity.type = DiscordActivityType_Playing;
snprintf(gCurActivity.party.id, 128, "%lld", lobby->id);
snprintf(gCurActivity.party.id, 128, "%ld", lobby->id);
gCurActivity.party.size.current_size = 1;
gCurActivity.party.size.max_size = MAX_PLAYERS;
char secretJoin[128] = "";
snprintf(secretJoin, 128, "%lld:%s", lobby->id, lobby->secret);
snprintf(secretJoin, 128, "%ld:%s", lobby->id, lobby->secret);
strcpy(gCurActivity.secrets.join, secretJoin);
isHosting = true;
@ -59,21 +59,21 @@ static void on_lobby_create_callback(UNUSED void* data, enum EDiscordResult resu
}
static void on_lobby_update(UNUSED void* data, int64_t lobbyId) {
LOGFILE_INFO(LFT_DISCORD, "> on_lobby_update id: %lld", lobbyId);
LOGFILE_INFO(LFT_DISCORD, "> on_lobby_update id: %ld", lobbyId);
}
static void on_member_connect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
LOGFILE_INFO(LFT_DISCORD, "> on_member_connect lobby: %lld, user: %lld", lobbyId, userId);
LOGFILE_INFO(LFT_DISCORD, "> on_member_connect lobby: %ld, user: %ld", lobbyId, userId);
gCurActivity.party.size.current_size++;
discord_activity_update(true);
}
static void on_member_update(UNUSED void* data, int64_t lobbyId, int64_t userId) {
LOGFILE_INFO(LFT_DISCORD, "> on_member_update lobby: %lld, user: %lld", lobbyId, userId);
LOGFILE_INFO(LFT_DISCORD, "> on_member_update lobby: %ld, user: %ld", lobbyId, userId);
}
static void on_member_disconnect(UNUSED void* data, int64_t lobbyId, int64_t userId) {
LOGFILE_INFO(LFT_DISCORD, "> on_member_disconnect lobby: %lld, user: %lld", lobbyId, userId);
LOGFILE_INFO(LFT_DISCORD, "> on_member_disconnect lobby: %ld, user: %ld", lobbyId, userId);
u8 localIndex = discord_user_id_to_local_index(userId);
if (localIndex != UNKNOWN_LOCAL_INDEX && gNetworkPlayers[localIndex].connected) {
network_player_disconnected(gNetworkPlayers[localIndex].globalIndex);
@ -108,7 +108,7 @@ void discord_lobby_leave(void) {
app.lobbies->disconnect_lobby(app.lobbies, gCurLobbyId, NULL, on_lobby_leave_callback);
}
LOGFILE_INFO(LFT_DISCORD, "left lobby %lld", gCurLobbyId);
LOGFILE_INFO(LFT_DISCORD, "left lobby %ld", gCurLobbyId);
isHosting = false;
gCurLobbyId = 0;

View File

@ -36,7 +36,7 @@ static int socket_send(SOCKET socket, struct sockaddr_in* addr, u8* buffer, u16
static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer, u16 bufferLength, u16* receiveLength, u8* localIndex) {
*receiveLength = 0;
int rxAddrSize = sizeof(struct sockaddr_in);
unsigned int rxAddrSize = sizeof(struct sockaddr_in);
int rc = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)rxAddr, &rxAddrSize);
for (int i = 1; i < MAX_PLAYERS; i++) {

View File

@ -13,7 +13,7 @@ SOCKET socket_initialize(void) {
// set non-blocking mode
int rc = fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
if (rc == INVALID_SOCKET) {
if (rc == (int)INVALID_SOCKET) {
LOG_ERROR("fcntl failed with error: %d", rc);
return INVALID_SOCKET;
}
@ -24,7 +24,7 @@ SOCKET socket_initialize(void) {
void socket_shutdown(SOCKET socket) {
if (socket == INVALID_SOCKET) { return; }
int rc = closesocket(socket);
if (rc == SOCKET_ERROR) {
if (rc == (int)SOCKET_ERROR) {
LOG_ERROR("closesocket failed with error %d\n", SOCKET_LAST_ERROR);
}
}

View File

@ -77,4 +77,39 @@ f64 clock_elapsed_f64(void) {
u32 clock_elapsed_ticks(void) {
return (clock_elapsed_ns() * 3 / 100000000);
}
}
void file_get_line(char* buffer, size_t maxLength, FILE* fp) {
char* initial = buffer;
char c = fgetc(fp);
while (!feof(fp) && c != '\n') {
// make sure it's printable
if (c < ' ' || c > '~') { goto next_get; }
// parse new line escape code
if (c == '\\') {
c = fgetc(fp);
if (feof(fp)) { break; }
if (c == 'n') {
if ((size_t)(buffer - initial) < (maxLength - 1)) {
*buffer++ = '\n';
}
goto next_get;
}
}
// found new line
if (c == '\n') { break; }
// append to buffer
if ((size_t)(buffer - initial) < (maxLength - 1)) {
*buffer++ = c;
}
next_get:
c = fgetc(fp);
}
*buffer = '\0';
}

View File

@ -1,10 +1,13 @@
#ifndef UTILS_MISC_H
#define UTILS_MISC_H
#include <stdio.h>
float smoothstep(float edge0, float edge1, float x);
void update_all_mario_stars(void);
f32 clock_elapsed(void);
f64 clock_elapsed_f64(void);
u32 clock_elapsed_ticks(void);
void file_get_line(char* buffer, size_t maxLength, FILE* fp);
#endif