aboutsummaryrefslogtreecommitdiff
path: root/internal/game/core_login_account.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/core_login_account.go')
-rw-r--r--internal/game/core_login_account.go56
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()
}