Compare commits
1 Commits
3a01283358
...
3bc2e09e24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bc2e09e24 |
@@ -1559,17 +1559,10 @@ yet run `DoDaemons(After)`, `DoFuses(After)` or `ringTurnEffects`. Restoring
|
|||||||
re-enters `playit` at the top of `command` in either case, so the rest of that
|
re-enters `playit` at the top of `command` in either case, so the rest of that
|
||||||
command never runs — its AFTER daemons and fuses and its ring effects are lost —
|
command never runs — its AFTER daemons and fuses and its ring effects are lost —
|
||||||
and the restored game opens with a fresh BEFORE pass on top of the one already
|
and the restored game opens with a fresh BEFORE pass on top of the one already
|
||||||
in the snapshot: `rollwand`, a live BEFORE daemon once `swander` has fired,
|
in the snapshot: `rollwand` ticks again, any BEFORE fuse is decremented again.
|
||||||
ticks again, and any BEFORE fuse is decremented again. Not every consequence of
|
The result is a coherent state one turn's worth of effects off, which is the
|
||||||
that pass is shared by both service points, though. `visuals` returns
|
price of being able to save at all for a player whose line dropped mid-prompt or
|
||||||
immediately unless `g.After`, and `After` is part of the snapshot, so `DVisuals`
|
who is away in a shell.
|
||||||
never re-ticks after a shell-escape save — `shell` sets `g.After = false` as its
|
|
||||||
first statement, before it parks — whereas after a `readchar` save it usually
|
|
||||||
does, because `turnUpkeep` sets `g.After = true` just before the top-of-turn
|
|
||||||
read; the exception is a handler that clears `After` before prompting, as
|
|
||||||
`identifyTrapCommand` does ahead of `promptDirection`. The result is a coherent
|
|
||||||
state one turn's worth of effects off, which is the price of being able to save
|
|
||||||
at all for a player whose line dropped mid-prompt or who is away in a shell.
|
|
||||||
|
|
||||||
The shell escape runs the shell on a helper goroutine so that the game goroutine
|
The shell escape runs the shell on a helper goroutine so that the game goroutine
|
||||||
stays free to answer, but a panic out of `Terminal.ShellEscape` (which is how a
|
stays free to answer, but a panic out of `Terminal.ShellEscape` (which is how a
|
||||||
|
|||||||
38
TODO.md
38
TODO.md
@@ -70,25 +70,25 @@ wizard commands).
|
|||||||
the save, which still reads the old file whole after it. Each was
|
the save, which still reads the old file whole after it. Each was
|
||||||
mutation-proved: reverting `AutoSaveOnSignal` to encode on the calling
|
mutation-proved: reverting `AutoSaveOnSignal` to encode on the calling
|
||||||
goroutine (the pre-fix behavior) makes the turn-loop test fail under `-race`
|
goroutine (the pre-fix behavior) makes the turn-loop test fail under `-race`
|
||||||
with over a hundred reports, and removing each of the three service points
|
with 113 reports, and removing each of the three service points fails exactly
|
||||||
fails exactly the test for that park with its own message. `pendingSaver` now
|
the test for that park with its own message. `pendingSaver` now reads the game
|
||||||
reads the game out from under its mutex instead of delegating with it held,
|
out from under its mutex instead of delegating with it held, because the
|
||||||
because the delegated call blocks until the save is taken — the PR #23
|
delegated call blocks until the save is taken — the PR #23 review's N3 note,
|
||||||
review's N3 note, load-bearing rather than hypothetical, and pinned by a test.
|
load-bearing rather than hypothetical, and pinned by a test. The
|
||||||
The SIGINT/SIGQUIT no-save decision and the single-signal-read ordering
|
SIGINT/SIGQUIT no-save decision and the single-signal-read ordering guarantee
|
||||||
guarantee are untouched; `savesOnSignal`'s third ground ("safety") is
|
are untouched; `savesOnSignal`'s third ground ("safety") is rewritten, since
|
||||||
rewritten, since the corruption window it weighed no longer exists.
|
the corruption window it weighed no longer exists. `MEMORY.md` stops listing
|
||||||
`MEMORY.md` stops listing signal-time autosave among the deliberate `_ =`
|
signal-time autosave among the deliberate `_ =` discards and states the new
|
||||||
discards and states the new discipline; `ARCHITECTURE.md` §5.3, the `Terminal`
|
discipline; `ARCHITECTURE.md` §5.3, the `Terminal` sketch, the C-to-Go mapping
|
||||||
sketch, the C-to-Go mapping row and §9's SIGTSTP paragraph are corrected to
|
row and §9's SIGTSTP paragraph are corrected to match. Two things review
|
||||||
match. Two things review caught and this entry records so they are not undone:
|
caught and this entry records so they are not undone: moving the shell onto a
|
||||||
moving the shell onto a helper goroutine also moved `term.Tcell.ShellEscape`'s
|
helper goroutine also moved `term.Tcell.ShellEscape`'s `panic` on a failed
|
||||||
`panic` on a failed `Screen.Resume` there, and a panic at the top of any
|
`Screen.Resume` there, and a panic at the top of any goroutine kills the
|
||||||
goroutine kills the process without running the deferred calls of the others —
|
process without running the deferred calls of the others — including
|
||||||
including `cmd/rogue/main.go`'s `defer t.Fini()`, so the tty would have been
|
`cmd/rogue/main.go`'s `defer t.Fini()`, so the tty would have been left raw on
|
||||||
left raw on exactly the path where the terminal is already broken (issue #12's
|
exactly the path where the terminal is already broken (issue #12's failure,
|
||||||
failure, reintroduced on a new path). `runShellEscape` recovers the helper's
|
reintroduced on a new path). `runShellEscape` recovers the helper's panic and
|
||||||
panic and re-raises it on the game goroutine, pinned by
|
re-raises it on the game goroutine, pinned by
|
||||||
`TestShellEscapePanicUnwindsTheGameGoroutine`. And the doc comment took two
|
`TestShellEscapePanicUnwindsTheGameGoroutine`. And the doc comment took two
|
||||||
rounds to get right: the first version claimed in four places that nothing is
|
rounds to get right: the first version claimed in four places that nothing is
|
||||||
half-mutated at the `readchar` service point, and the revision that fixed that
|
half-mutated at the `readchar` service point, and the revision that fixed that
|
||||||
|
|||||||
14
game/io.go
14
game/io.go
@@ -174,14 +174,12 @@ func stepOk(ch byte) bool {
|
|||||||
// save runs here, on the game goroutine, before reading again.
|
// save runs here, on the game goroutine, before reading again.
|
||||||
//
|
//
|
||||||
// What that buys is a snapshot taken by the goroutine that owns the
|
// What that buys is a snapshot taken by the goroutine that owns the
|
||||||
// state, so it is internally consistent and restorable. It is never a
|
// state, so it is internally consistent and restorable. It is not
|
||||||
// between-commands snapshot: readchar is reached from readCommand at the
|
// necessarily a between-commands snapshot: readchar is also reached from
|
||||||
// top of a turn that has already run its BEFORE daemons and turnUpkeep,
|
// prompts raised part-way through a command — --More--, askOverwrite,
|
||||||
// and from prompts raised part-way through a command — --More--,
|
// getStr, the direction and pack prompts — and mutation has already
|
||||||
// askOverwrite, getStr, the direction and pack prompts — by which point
|
// happened by then. See serviceAutoSaveRequest (save.go) for what that
|
||||||
// the command has mutated state as well. See serviceAutoSaveRequest
|
// costs the player.
|
||||||
// (save.go) for the full statement of what the handoff guarantees and
|
|
||||||
// what it costs the player.
|
|
||||||
func (g *RogueGame) readchar() byte {
|
func (g *RogueGame) readchar() byte {
|
||||||
for {
|
for {
|
||||||
ch, ok := g.scr.term.ReadChar()
|
ch, ok := g.scr.term.ReadChar()
|
||||||
|
|||||||
13
game/save.go
13
game/save.go
@@ -801,18 +801,7 @@ func (g *RogueGame) AutoSaveOnSignal(timeout time.Duration) bool {
|
|||||||
// fresh BEFORE pass on top of the one already in the snapshot. That
|
// fresh BEFORE pass on top of the one already in the snapshot. That
|
||||||
// second BEFORE pass is not free: rollwand, a live Before daemon once
|
// second BEFORE pass is not free: rollwand, a live Before daemon once
|
||||||
// swander has fired, ticks again and draws from the RNG every fourth
|
// swander has fired, ticks again and draws from the RNG every fourth
|
||||||
// tick, and any Before fuse is decremented again.
|
// tick, and any Before fuse is decremented again. The result is still a
|
||||||
//
|
|
||||||
// Not every consequence of that pass is shared by both, though. visuals
|
|
||||||
// returns immediately unless g.After, and After is part of the snapshot,
|
|
||||||
// so DVisuals never re-ticks after a shell-escape save: shell sets
|
|
||||||
// g.After = false as its first statement, before it parks. After a
|
|
||||||
// readchar save it usually does re-tick, because turnUpkeep sets
|
|
||||||
// g.After = true just before the top-of-turn read; the exception is a
|
|
||||||
// handler that clears After before prompting, as identifyTrapCommand
|
|
||||||
// does ahead of promptDirection.
|
|
||||||
//
|
|
||||||
// The result is still a
|
|
||||||
// coherent game state, one turn's worth of effects off — strictly better
|
// coherent game state, one turn's worth of effects off — strictly better
|
||||||
// than the torn encode this replaced, and the cost of being able to save
|
// than the torn encode this replaced, and the cost of being able to save
|
||||||
// a player whose line dropped mid-prompt, or who is away in a shell, at
|
// a player whose line dropped mid-prompt, or who is away in a shell, at
|
||||||
|
|||||||
Reference in New Issue
Block a user