aboutsummaryrefslogtreecommitdiff
path: root/internal/game/game.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 17:48:26 -0400
committerhistoria <[not public]>2026-09-18 17:48:26 -0400
commit8fe9e3c3fef0049df82281a62afe35dda516dfe2 (patch)
treedd269f2c0710f334e19f00a191260ebc46284a90 /internal/game/game.go
parent194164a77eb4f2479b94c19e6f0e1278ab608781 (diff)
downloadthehouseoficarus-8fe9e3c3fef0049df82281a62afe35dda516dfe2.tar.gz
fix: close combat ghost, admin traversal, login throttle, journal double-pay, and quality/economy gaps
Diffstat (limited to 'internal/game/game.go')
-rw-r--r--internal/game/game.go19
1 files changed, 19 insertions, 0 deletions
diff --git a/internal/game/game.go b/internal/game/game.go
index 5ad1033..af2ff66 100644
--- a/internal/game/game.go
+++ b/internal/game/game.go
@@ -110,6 +110,12 @@ type Game struct {
// Economy is the durable money journal; see economy.go.
Economy *economyJournal
+ // loginThrottle applies an escalating per-(account, IP) delay to failed
+ // interactive password attempts. Lazily created so Game literals built
+ // directly in tests work without it.
+ loginThrottleOnce sync.Once
+ loginThrottle *loginThrottle
+
// owner serializes all live-state mutations; see owner.go.
owner *owner
ownerStop sync.Once
@@ -197,6 +203,13 @@ func (g *Game) SetHub(hub *net.Hub) {
return
}
if p := sess.Player; p != nil {
+ // The session is leaving the world: end any live fight and
+ // cancel a running logout countdown first, so neither keeps
+ // acting on a detached session (ghost combat that saves over
+ // a reconnected character, or a duplicate logout that drops
+ // the reconnected session's registry entry).
+ g.stopCombat(p.Name)
+ g.cancelRest(p.Name)
g.restoreGodPlayer(p)
g.persistLockedOnDisconnect(p)
g.saveCharacter(p)
@@ -443,6 +456,12 @@ func (g *Game) HandleSession(sess *net.Session, input string) {
g.handleDangerConfirm(sess, input)
case net.StateUndigConfirm:
g.handleUndigConfirm(sess, input)
+ default:
+ // A declared state with no handler would otherwise silently swallow
+ // every line. Re-prompt and log so a future menu flow cannot strand
+ // the player on a dead input state.
+ log.Printf("unhandled session state %v", sess.State())
+ sess.WriteLine("Something went wrong; please try again.")
}
}