diff options
| author | workhorse <workhorse@localhost.localdomain> | 2026-06-19 20:24:52 -0400 |
|---|---|---|
| committer | workhorse <workhorse@localhost.localdomain> | 2026-06-19 20:24:52 -0400 |
| commit | 8ee8982b8759bcae116647cc993867f4c8b0a6ce (patch) | |
| tree | ee01f7b1d745cbc94d9981108a1209527487b3e5 /internal/game/core_login_account.go | |
| parent | 2bec24859af8f03c80a0a58e3240519942450167 (diff) | |
| download | thehouseoficarus-8ee8982b8759bcae116647cc993867f4c8b0a6ce.tar.gz | |
reorganized items, objects, and rooms in directories. oranized module names. added startup checks.
Diffstat (limited to 'internal/game/core_login_account.go')
| -rw-r--r-- | internal/game/core_login_account.go | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/internal/game/core_login_account.go b/internal/game/core_login_account.go new file mode 100644 index 0000000..87f8c62 --- /dev/null +++ b/internal/game/core_login_account.go @@ -0,0 +1,351 @@ +package game + +import ( + "fmt" + "os" + "strings" + + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) handleAccountName(sess *net.Session, input string) { + name := strings.TrimSpace(input) + if name == "" { + sess.Write("Account name: ") + return + } + actualName, err := g.AccountStore.FindAccount(name) + if err == nil { + sess.Account = &player.Account{Name: actualName} + sess.State = net.StatePassword + sess.Conn.SetEcho(false) + sess.Write("Password: ") + } else { + if verr := player.ValidName(name); verr != nil { + sess.WriteLine(verr.Error()) + sess.Write("Account name: ") + return + } + sess.Account = &player.Account{Name: name} + sess.State = net.StateNewAccountPass + sess.Conn.SetEcho(false) + sess.WriteLine("\r\nOh, a new visitor to this rock!") + sess.Write("Choose password: ") + } +} + +func (g *Game) handlePassword(sess *net.Session, input string) { + if input == "" { + sess.Write("Password: ") + return + } + if len(input) > 128 { + sess.Conn.SetEcho(true) + sess.WriteLine("Wrong password.") + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + acc, err := g.AccountStore.LoadAccount(sess.Account.Name) + if err != nil { + sess.Conn.SetEcho(true) + sess.WriteLine("Error loading account.") + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + if !player.CheckPassword(input, acc.PasswordHash) { + sess.Conn.SetEcho(true) + sess.WriteLine("Wrong password.") + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + sess.Conn.SetEcho(true) + sess.Account = &player.Account{ + Name: acc.Name, + PasswordHash: acc.PasswordHash, + Characters: acc.Characters, + Aliases: acc.Aliases, + Colors: acc.Colors, + Options: acc.Options, + } + if sess.Account.Aliases == nil { + sess.Account.Aliases = make(map[string]string) + } + if sess.Account.Colors == nil { + sess.Account.Colors = make(map[string]string) + } + if sess.Account.Options == nil { + sess.Account.Options = make(map[string]any) + } + g.showMenu(sess) +} + +func (g *Game) handleNewAccountPass(sess *net.Session, input string) { + input = strings.TrimSpace(input) + if input == "" { + sess.Conn.SetEcho(true) + sess.Account = nil + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + if verr := player.ValidPassword(input); verr != nil { + sess.WriteLine(verr.Error()) + sess.Write("Choose password: ") + return + } + sess.PendingPass = input + sess.State = net.StateNewAccountConfirm + sess.Write("Confirm password: ") +} + +func (g *Game) handleNewAccountConfirm(sess *net.Session, input string) { + input = strings.TrimSpace(input) + if input == "" { + sess.Conn.SetEcho(true) + sess.Account = nil + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + if input != sess.PendingPass { + sess.Conn.SetEcho(true) + sess.WriteLine("Passwords do not match.") + sess.Account = nil + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + + hash, err := player.HashPassword(input) + if err != nil { + sess.Conn.SetEcho(true) + sess.WriteLine(fmt.Sprintf("Error creating account: %v", err)) + sess.Account = nil + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + + acc := &player.Account{ + Name: sess.Account.Name, + PasswordHash: hash, + } + if err := g.AccountStore.SaveAccount(acc); err != nil { + sess.Conn.SetEcho(true) + sess.WriteLine(fmt.Sprintf("Error creating account: %v", err)) + sess.Account = nil + sess.State = net.StateAccountName + sess.Write("Account name: ") + return + } + + sess.Conn.SetEcho(true) + sess.Account = &player.Account{ + Name: acc.Name, + PasswordHash: acc.PasswordHash, + Aliases: make(map[string]string), + Colors: make(map[string]string), + Options: make(map[string]any), + } + sess.PendingPass = "" + sess.State = net.StateColorChoice + sess.Write("\nShould I disable color? [y/N] ") +} + +func (g *Game) handleColorChoice(sess *net.Session, input string) { + input = strings.ToLower(strings.TrimSpace(input)) + if input == "y" || input == "yes" { + sess.Account.Options["color"] = "none" + acc, err := g.AccountStore.LoadAccount(sess.Account.Name) + if err == nil { + if acc.Options == nil { + acc.Options = make(map[string]any) + } + acc.Options["color"] = "none" + g.AccountStore.SaveAccount(acc) + } + } + g.showMenu(sess) +} + +func (g *Game) showMenu(sess *net.Session) { + sess.State = net.StateMenu + lines := []string{ + "", + fmt.Sprintf("Welcome back to Gaia 04, %s.", sess.Account.Name), + "", + } + if len(sess.Account.Characters) > 0 { + lines = append(lines, + " (C)onnect character to THOI", + "", + " (L)ist characters", + " (R)ename character", + " (D)elete character", + ) + } + lines = append(lines, + " (N)ew character", + " (P)urge account", + " (A)ccount rename", + " (Q)uit", + "", + ) + sess.WriteLines(lines...) + sess.Write("> ") +} + +func (g *Game) handleMenu(sess *net.Session, input string) { + switch strings.ToLower(strings.TrimSpace(input)) { + case "": + sess.Write("> ") + case "c": + if len(sess.Account.Characters) == 0 { + sess.WriteLine("\nHey, make a character with 'N' first!") + sess.Write("\n> ") + return + } + if len(sess.Account.Characters) == 1 { + g.connectCharacter(sess, sess.Account.Characters[0]) + return + } + sess.WriteLine("\nSelect character:") + for i, name := range sess.Account.Characters { + sess.WriteLine(fmt.Sprintf(" %d) %s", i+1, name)) + } + sess.State = net.StateNewCharName + sess.PendingChar = "connect" + sess.Write("\n> ") + + case "l": + if len(sess.Account.Characters) == 0 { + sess.WriteLine("\nYou don't have any characters! Make one with 'N'!") + } else { + sess.WriteLine("\nCharacters:") + for _, name := range sess.Account.Characters { + sess.WriteLine(fmt.Sprintf(" - %s", name)) + } + } + sess.Write("\n> ") + + case "n": + sess.State = net.StateNewCharName + sess.PendingChar = "" + sess.Write("What's your character name?: ") + + case "a": + sess.State = net.StateRenameAccount + sess.Write("New account name: ") + + case "r": + if len(sess.Account.Characters) == 0 { + sess.WriteLine("\nRename who? You don't have any characters!") + sess.Write("\n> ") + return + } + if len(sess.Account.Characters) == 1 { + sess.PendingChar = sess.Account.Characters[0] + sess.State = net.StateRenameCharName + sess.WriteLine(fmt.Sprintf("\nRenaming %s.", sess.PendingChar)) + sess.Write("New name: ") + return + } + sess.State = net.StateRenameChar + sess.WriteLine("\nRename which character?") + for i, name := range sess.Account.Characters { + sess.WriteLine(fmt.Sprintf(" %d) %s", i+1, name)) + } + sess.Write("\n> ") + + case "d": + if len(sess.Account.Characters) == 0 { + sess.WriteLine("\nNo characters to delete.") + sess.Write("\n> ") + return + } + if len(sess.Account.Characters) == 1 { + sess.PendingChar = sess.Account.Characters[0] + } else { + sess.State = net.StateDeleteChar + sess.WriteLine("\nDelete which character?") + for i, name := range sess.Account.Characters { + sess.WriteLine(fmt.Sprintf(" %d) %s", i+1, name)) + } + sess.Write("\n> ") + return + } + g.showDeleteConfirm(sess) + + case "p": + sess.State = net.StatePurgeAccount + sess.WriteLine(fmt.Sprintf("\nPurge account %s and all characters?\nTo be clear you are about to PERMANENTLY DELETE EVERYTHING!\nType PURGE %s to confirm, or anything else to cancel.", sess.Account.Name, sess.Account.Name)) + sess.Write("\n> ") + + case "q": + sess.WriteLine("Later.") + sess.Close() + return + + default: + sess.Write("> ") + } +} + +func (g *Game) handleRenameAccount(sess *net.Session, input string) { + newName := strings.TrimSpace(input) + if newName == "" { + sess.Write("New account name: ") + return + } + if verr := player.ValidName(newName); verr != nil { + sess.WriteLine(verr.Error()) + sess.Write("New account name: ") + return + } + oldName := sess.Account.Name + if newName == oldName { + sess.WriteLine("That's already your account name.") + g.showMenu(sess) + return + } + if g.AccountStore.AccountExists(newName) { + sess.WriteLine("An account with that name already exists.") + sess.Write("New account name: ") + return + } + + oldPath := g.AccountStore.AccountPath(oldName) + newPath := g.AccountStore.AccountPath(newName) + if err := os.Rename(oldPath, newPath); err != nil { + sess.WriteLine(fmt.Sprintf("Error renaming account: %v", err)) + g.showMenu(sess) + return + } + + sess.Account.Name = newName + sess.WriteLine(fmt.Sprintf("Account renamed to %s.", newName)) + g.showMenu(sess) +} + +func (g *Game) handlePurgeAccount(sess *net.Session, input string) { + input = strings.TrimSpace(input) + expected := "PURGE " + sess.Account.Name + if strings.ToUpper(input) != strings.ToUpper(expected) { + sess.WriteLine("Purge cancelled.") + g.showMenu(sess) + return + } + + for _, name := range sess.Account.Characters { + os.Remove(g.AccountStore.CharPath(name)) + } + + os.Remove(g.AccountStore.AccountPath(sess.Account.Name)) + + sess.WriteLine(fmt.Sprintf("Account %s has been purged. Thanks for playing.", sess.Account.Name)) + sess.Close() +} |
