aboutsummaryrefslogtreecommitdiff
path: root/internal/game/core_login_account.go
diff options
context:
space:
mode:
authorworkhorse <workhorse@localhost.localdomain>2026-06-19 20:24:52 -0400
committerworkhorse <workhorse@localhost.localdomain>2026-06-19 20:24:52 -0400
commit8ee8982b8759bcae116647cc993867f4c8b0a6ce (patch)
treeee01f7b1d745cbc94d9981108a1209527487b3e5 /internal/game/core_login_account.go
parent2bec24859af8f03c80a0a58e3240519942450167 (diff)
downloadthehouseoficarus-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.go351
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()
+}