From a60cf8066a3d341f7ef468b1bdc8fae2e83eb4bd Mon Sep 17 00:00:00 2001 From: kinther Date: Fri, 26 Dec 2025 10:54:47 -0800 Subject: [PATCH] Accounts update 2 --- .gitignore | 17 +++++++++ src/accounts.c | 90 +++++++++++++++++++++++++++++++++++++++++++++-- src/accounts.h | 9 +++++ src/handler.c | 5 ++- src/interpreter.c | 87 +++++++++++++++++++++++++++++++++++++++++---- src/protocol.h | 2 +- src/structs.h | 5 ++- 7 files changed, 203 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 3bf3f9c..fe2e157 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,23 @@ build/* log/* syslog* +# Do not commit files from accounts +lib/acctfiles/A-E/* +lib/acctfiles/F-J/* +lib/acctfiles/K-O/* +lib/acctfiles/P-T/* +lib/acctfiles/U-Z/* +lib/acctfiles/ZZZ/* +lib/acctfiles/index + +# but do commit the placeholders +!lib/acctfiles/A-E/00 +!lib/acctfiles/F-J/00 +!lib/acctfiles/K-O/00 +!lib/acctfiles/P-T/00 +!lib/acctfiles/U-Z/00 +!lib/acctfiles/ZZZ/00 + # Do not commit files from players lib/plrfiles/A-E/* lib/plrfiles/F-J/* diff --git a/src/accounts.c b/src/accounts.c index a3a48f1..da4efa4 100644 --- a/src/accounts.c +++ b/src/accounts.c @@ -1,3 +1,10 @@ +/** +* @file accounts.h +* Account loading/saving and utility routines. +* +* This set of code was not originally part of the circlemud distribution. +*/ + #include "conf.h" #include "sysdep.h" @@ -21,6 +28,41 @@ static void set_account_name(struct account_data *account, const char *name) account->name = strdup(tmp); } +int account_has_pc(const struct account_data *account, const char *pc_name) +{ + int i; + + if (!account || !pc_name || !*pc_name) + return 0; + + for (i = 0; i < account->pc_count; i++) { + if (!str_cmp(account->pc_list[i], pc_name)) + return 1; + } + + return 0; +} + +void account_add_pc(struct account_data *account, const char *pc_name) +{ + char tmp[MAX_INPUT_LENGTH]; + int i; + + if (!account || !pc_name || !*pc_name) + return; + + strlcpy(tmp, pc_name, sizeof(tmp)); + CAP(tmp); + + if (account_has_pc(account, tmp)) + return; + + i = account->pc_count; + RECREATE(account->pc_list, char *, account->pc_count + 1); + account->pc_list[i] = strdup(tmp); + account->pc_count++; +} + struct account_data *account_create(const char *name) { struct account_data *account; @@ -37,6 +79,7 @@ struct account_data *account_load(const char *name) char filename[PATH_MAX]; char line[MAX_INPUT_LENGTH + 1]; char tag[6]; + int i; if (!name || !*name) return NULL; @@ -61,7 +104,9 @@ struct account_data *account_load(const char *name) if (account->email) free(account->email); account->email = strdup(line); - } else if (!strcmp(tag, "Char")) { + } else if (!strcmp(tag, "Char")) + account_add_pc(account, line); + else if (!strcmp(tag, "Curr")) { if (account->pc_name) free(account->pc_name); account->pc_name = strdup(line); @@ -73,6 +118,19 @@ struct account_data *account_load(const char *name) if (!account->name) set_account_name(account, name); + if (account->pc_name && account->pc_count == 0) + account_add_pc(account, account->pc_name); + + if (!account->pc_name && account->pc_count > 0) { + for (i = account->pc_count - 1; i >= 0; i--) { + int pfilepos = get_ptable_by_name(account->pc_list[i]); + if (pfilepos >= 0 && !IS_SET(player_table[pfilepos].flags, PINDEX_DELETED)) { + account->pc_name = strdup(account->pc_list[i]); + break; + } + } + } + return account; } @@ -80,6 +138,7 @@ int account_save(const struct account_data *account) { FILE *fl; char filename[PATH_MAX]; + int i; if (!account || !account->name || !*account->name) return 0; @@ -97,7 +156,9 @@ int account_save(const struct account_data *account) if (account->email && *account->email) fprintf(fl, "Mail: %s\n", account->email); if (account->pc_name && *account->pc_name) - fprintf(fl, "Char: %s\n", account->pc_name); + fprintf(fl, "Curr: %s\n", account->pc_name); + for (i = 0; i < account->pc_count; i++) + fprintf(fl, "Char: %s\n", account->pc_list[i]); fclose(fl); return 1; @@ -105,6 +166,8 @@ int account_save(const struct account_data *account) void account_free(struct account_data *account) { + int i; + if (!account) return; @@ -114,6 +177,11 @@ void account_free(struct account_data *account) free(account->email); if (account->pc_name) free(account->pc_name); + if (account->pc_list) { + for (i = 0; i < account->pc_count; i++) + free(account->pc_list[i]); + free(account->pc_list); + } free(account); } @@ -149,6 +217,7 @@ void account_set_pc(struct account_data *account, const char *pc_name) strlcpy(tmp, pc_name, sizeof(tmp)); CAP(tmp); account->pc_name = strdup(tmp); + account_add_pc(account, tmp); } } @@ -164,12 +233,27 @@ void account_clear_pc(struct account_data *account) void account_refresh_pc(struct account_data *account) { + int i; + if (!account || !account->pc_name || !*account->pc_name) - return; + goto ensure_active; if (account_has_alive_pc(account)) return; account_clear_pc(account); account_save(account); + +ensure_active: + if (!account || account->pc_name || account->pc_count == 0) + return; + + for (i = account->pc_count - 1; i >= 0; i--) { + int pfilepos = get_ptable_by_name(account->pc_list[i]); + if (pfilepos >= 0 && !IS_SET(player_table[pfilepos].flags, PINDEX_DELETED)) { + account->pc_name = strdup(account->pc_list[i]); + account_save(account); + break; + } + } } diff --git a/src/accounts.h b/src/accounts.h index 004e229..d990d1d 100644 --- a/src/accounts.h +++ b/src/accounts.h @@ -1,3 +1,10 @@ +/** +* @file accounts.h +* ASCII account file header. +* +* This set of code was not originally part of the circlemud distribution. +*/ + #ifndef _ACCOUNTS_H_ #define _ACCOUNTS_H_ @@ -7,8 +14,10 @@ struct account_data *account_load(const char *name); struct account_data *account_create(const char *name); int account_save(const struct account_data *account); void account_free(struct account_data *account); +int account_has_pc(const struct account_data *account, const char *pc_name); int account_has_alive_pc(const struct account_data *account); void account_set_pc(struct account_data *account, const char *pc_name); +void account_add_pc(struct account_data *account, const char *pc_name); void account_clear_pc(struct account_data *account); void account_refresh_pc(struct account_data *account); diff --git a/src/handler.c b/src/handler.c index d792a79..b23111e 100644 --- a/src/handler.c +++ b/src/handler.c @@ -990,7 +990,10 @@ void extract_char_final(struct char_data *ch) STATE(d) = CON_CLOSE; } if (GET_POS(ch) == POS_DEAD) { - STATE(ch->desc) = CON_CLOSE; + STATE(ch->desc) = CON_ACCOUNT_MENU; + send_account_menu(ch->desc); + ch->desc->character = NULL; + ch->desc = NULL; } else { STATE(ch->desc) = CON_ACCOUNT_MENU; send_account_menu(ch->desc); diff --git a/src/interpreter.c b/src/interpreter.c index c128b70..33f0938 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1036,6 +1036,31 @@ static int _parse_name(char *arg, char *name) return (0); } +static void show_account_character_list(struct descriptor_data *d) +{ + int i; + + if (!d || !d->account) + return; + + write_to_output(d, "\r\nCharacter history:\r\n"); + if (d->account->pc_count == 0) { + write_to_output(d, " (none)\r\n"); + return; + } + + for (i = 0; i < d->account->pc_count; i++) { + int pfilepos = get_ptable_by_name(d->account->pc_list[i]); + const char *status = "dead"; + + if (pfilepos >= 0 && !IS_SET(player_table[pfilepos].flags, PINDEX_DELETED)) + status = "alive"; + + write_to_output(d, " %s (%s)\r\n", d->account->pc_list[i], status); + } + write_to_output(d, "\r\n*** PRESS RETURN: "); +} + void send_account_menu(struct descriptor_data *d) { int has_pc; @@ -1046,12 +1071,34 @@ void send_account_menu(struct descriptor_data *d) account_refresh_pc(d->account); has_pc = account_has_alive_pc(d->account); - write_to_output(d, "\r\nAccount: %s\r\n", d->account->name); + write_to_output(d, + "\r\n" + " .\r\n" + " /=\\\\\r\n" + " /===\\ \\\r\n" + " /=====\\' \\\r\n" + " /=======\\'' \\\r\n" + " /=========\\ ' '\\\r\n" + " /===========\\'' \\\r\n" + " /=============\\ ' ' \\\r\n" + " /===============\\ '' \\\r\n" + " /=================\\' ' ' ' \\\r\n" + " /===================\\' ' ' ' \\\r\n" + " /=====================\\' ' ' ' \\\r\n" + " /=======================\\ ' ' /\r\n" + " /=========================\\ ' /\r\n" + " /===========================\\' /\r\n" + " /=============| |=============\\/\r\n" + "\r\n" + " -Pyramid of Ikaros, current day.\r\n" + "\r\n"); + write_to_output(d, "\r\n Account: %s\r\n", d->account->name); if (has_pc) - write_to_output(d, "\t(1\t)) Connect to %s.\r\n", d->account->pc_name); + write_to_output(d, "\t( C\t)) Connect to %s.\r\n", d->account->pc_name); else - write_to_output(d, "\t(1\t)) Create a new PC.\r\n"); - write_to_output(d, "\t(0\t)) Exit from tbaMUD.\r\n\r\n" + write_to_output(d, "\t( R\t)) Create a new PC.\r\n"); + write_to_output(d, "\t( L\t)) List previous characters.\r\n"); + write_to_output(d, "\t( X\t)) Exit from Miranthas.\r\n\r\n" " Make your choice: "); } @@ -1537,6 +1584,10 @@ void nanny(struct descriptor_data *d, char *arg) write_to_output(d, "Invalid name, please try another.\r\nName: "); return; } + if (account_has_pc(d->account, tmp_name)) { + write_to_output(d, "That name has already been used, try something else.\r\nName: "); + return; + } if ((player_i = get_ptable_by_name(tmp_name)) >= 0) { if (IS_SET(player_table[player_i].flags, PINDEX_DELETED)) { @@ -2030,13 +2081,20 @@ case CON_QCLASS: has_pc = account_has_alive_pc(d->account); switch (*arg) { - case '0': + case 'X': + case 'x': write_to_output(d, "Goodbye.\r\n"); if (d->character) add_llog_entry(d->character, LAST_QUIT); STATE(d) = CON_CLOSE; break; - case '1': + case 'L': + case 'l': + show_account_character_list(d); + STATE(d) = CON_ACCOUNT_LIST; + break; + case 'C': + case 'c': if (has_pc) { if (d->character) { free_char(d->character); @@ -2115,6 +2173,18 @@ case CON_QCLASS: write_to_output(d, "\r\n*** PRESS RETURN: "); STATE(d) = CON_RMOTD; + } else { + write_to_output(d, "You do not have a character to connect.\r\n"); + send_account_menu(d); + STATE(d) = CON_ACCOUNT_MENU; + } + break; + case 'R': + case 'r': + if (has_pc) { + write_to_output(d, "You already have a character. Delete it before creating another.\r\n"); + send_account_menu(d); + STATE(d) = CON_ACCOUNT_MENU; } else { if (d->character) { free_char(d->character); @@ -2143,6 +2213,11 @@ case CON_QCLASS: break; } + case CON_ACCOUNT_LIST: + send_account_menu(d); + STATE(d) = CON_ACCOUNT_MENU; + break; + case CON_CHPWD_GETOLD: if (strncmp(CRYPT(arg, GET_PASSWD(d->character)), GET_PASSWD(d->character), MAX_PWD_LENGTH)) { echo_on(d); diff --git a/src/protocol.h b/src/protocol.h index 7afdf51..a647a7c 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -9,7 +9,7 @@ Set your MUD_NAME, and change descriptor_t if necessary. ******************************************************************************/ -#define MUD_NAME "tbaMUD" +#define MUD_NAME "MiranthasMUD" typedef struct descriptor_data descriptor_t; diff --git a/src/structs.h b/src/structs.h index 55c681b..47ed124 100644 --- a/src/structs.h +++ b/src/structs.h @@ -348,6 +348,7 @@ #define CON_ACCOUNT_CNFPASSWD 39 /**< New account, confirm password */ #define CON_ACCOUNT_EMAIL 40 /**< New account, optional email */ #define CON_ACCOUNT_MENU 41 /**< Account main menu */ +#define CON_ACCOUNT_LIST 42 /**< Viewing account character list */ /* OLC States range - used by IS_IN_OLC and IS_PLAYING */ #define FIRST_OLC_STATE CON_OEDIT /**< The first CON_ state that is an OLC */ @@ -1040,7 +1041,9 @@ struct account_data char *name; /**< Account username */ char passwd[MAX_PWD_LENGTH+1]; /**< Account password (hashed) */ char *email; /**< Optional email address */ - char *pc_name; /**< Attached PC name, if any */ + char *pc_name; /**< Active PC name, if any */ + char **pc_list; /**< Ordered list of PCs created by this account */ + int pc_count; /**< Number of PCs in list */ }; /** Special data used by NPCs, not PCs */