diff options
| author | historia <[not public]> | 2026-09-15 15:35:52 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-15 15:35:52 -0400 |
| commit | 0b0b25222b3131ada79ff78c28eb8bf07d977deb (patch) | |
| tree | 2ceaef5203595044f66a8f00064cd8e017f8f48d /internal/game/core_login_account.go | |
| parent | 396c3facb8287b9a34076cae8f3d7b300d366d99 (diff) | |
| download | thehouseoficarus-0b0b25222b3131ada79ff78c28eb8bf07d977deb.tar.gz | |
feat: single-owner game state, transactional lifecycle, durable money on server stop
Diffstat (limited to 'internal/game/core_login_account.go')
| -rw-r--r-- | internal/game/core_login_account.go | 56 |
1 files changed, 27 insertions, 29 deletions
diff --git a/internal/game/core_login_account.go b/internal/game/core_login_account.go index 77b72d3..a846f4d 100644 --- a/internal/game/core_login_account.go +++ b/internal/game/core_login_account.go @@ -2,7 +2,6 @@ package game import ( "fmt" - "os" "strings" "thehouseoficarus/internal/color" @@ -332,34 +331,19 @@ func (g *Game) handleRenameAccount(sess *net.Session, input string) { return } - oldPath := g.AccountStore.AccountPath(oldName) - newPath := g.AccountStore.AccountPath(newName) - if err := os.Rename(oldPath, newPath); err != nil { + // One journal transaction: file move + embedded name rewrite. Any + // failure rolls back, so the account is never half-renamed. + if err := g.renameAccountTxn(oldName, newName); err != nil { sess.WriteLine(fmt.Sprintf("Error renaming account: %v", err)) g.showMenu(sess) return } - // Persist the new name inside the account file itself; otherwise the next - // login adopts the old name again and account lookup drifts apart from - // the filename. Roll the rename back if the file cannot be updated so the - // account never ends up in a half-renamed state. - acc, err := g.AccountStore.LoadAccount(newName) - if err != nil { - sess.WriteLine(fmt.Sprintf("Error renaming account: %v", err)) - os.Rename(newPath, oldPath) - g.showMenu(sess) - return - } - acc.Name = newName - if err := g.AccountStore.SaveAccount(acc); err != nil { - sess.WriteLine(fmt.Sprintf("Error renaming account: %v", err)) - os.Rename(newPath, oldPath) - g.showMenu(sess) - return - } + // Republish account state to EVERY session of this account: other + // authenticated sessions must not retain stale account objects or + // pending menu selections bound to the old name. + g.publishAccountUpdate(newName) - sess.Account.Name = newName sess.WriteLine(fmt.Sprintf("Account renamed to %s.", newName)) g.showMenu(sess) } @@ -373,8 +357,10 @@ func (g *Game) handlePurgeAccount(sess *net.Session, input string) { return } - // Refuse to purge while any character is connected: a live session would - // keep playing (and saving) a character whose file is about to vanish. + // Refuse to purge while any character is connected: a live session + // would keep playing (and saving) a character whose file is about to + // vanish. The online check and the purge run as one owner step, so no + // connection can start a character between check and deletion. for _, name := range sess.Account.Characters { if g.characterOnline(name) { sess.WriteLine(fmt.Sprintf("%s is logged in. Log all characters out before purging.", name)) @@ -383,12 +369,24 @@ func (g *Game) handlePurgeAccount(sess *net.Session, input string) { } } - for _, name := range sess.Account.Characters { - os.Remove(g.AccountStore.CharPath(name)) + if err := g.purgeAccountTxn(sess.Account.Name, sess.Account.Characters); err != nil { + sess.WriteLine(fmt.Sprintf("Error purging account: %v", err)) + g.showMenu(sess) + return } - os.Remove(g.AccountStore.AccountPath(sess.Account.Name)) - sess.WriteLine(fmt.Sprintf("Account %s has been purged. Thanks for playing.", sess.Account.Name)) + // Other authenticated sessions of this account are stale now; tell + // them and close them (they cannot safely keep using cached state). + for _, other := range g.allSessionsOfAccount(sess.Account.Name) { + if other == sess { + continue + } + other.Account = nil + other.PendingChar = "" + other.PendingMenu = nil + other.WriteLine("\nYour account has been purged from another session.") + other.Close() + } sess.Close() } |
