Wizard createObj accepts an unrecognized type glyph and adds an inert KindNone object to the pack #21
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Separate from the out-of-range
Whichcrash (#10, fixed in PR #20), wizardcreateObjstill accepts an unrecognized type glyph.objectKindForGlyphmaps it to
KindNone, and the resulting object is added to the pack.The result is an inert, useless pack entry. It does not panic — with the
inventoryNameguard added by PR #20 it names itself "bizarre thing" — but itoccupies a pack slot, can be dropped, picked up, saved, and restored, and
does nothing.
Found by the implementer of #10, who correctly flagged it rather than fixing
it drive-by.
Faithfulness note — read before "fixing" this
This behavior is faithful to C. C's
create_objstored the raw characterdirectly as
o_typewith no validation, so C would also happily hand you ajunk object. Any change here is therefore a deliberate divergence from
5.4.4, not a bug fix, and needs to be justified as such.
That is why this is filed separately from #10 rather than folded into it: #10
fixed a Go-specific hard crash (C's out-of-bounds read was survivable; Go's
panic is not), which is a clear-cut faithfulness-preserving fix. This one is a
judgment call about whether to be more strict than C in a debug-only command.
Definition of done
out-of-range
Which— consistent with the sibling validation, at thecost of a small deliberate divergence from C; or
createObj, sothe next reader does not mistake it for the oversight that #10 was.
unchanged. Reuse the
mustNotPanic/malformedharness PR #20 added ingame/wizard_test.go.and
ARCHITECTURE.md§9-style documentation notes the deliberatepermissiveness.
KindNoneobject survives a save/restore round tripwithout tripping the
ErrSaveCorruptvalidation PR #20 added — this isthe real risk here. If restore rejects a save containing one, then a
wizard-created junk object silently destroys the player's save file, which
would be a genuine defect introduced by the interaction of the two changes.
make checkfully green.TODO.mdupdated in the same commit — Completed Steps entry, and do notrotate "Next Step" (precedent ratified on PR #9).
(closes #N).Implementation requirements
git show origin/c-master:wizard.c(create_obj) before deciding. DoNOT check out or modify
origin/c-masterormodern-rogue.PR #20 settled on rather than adding a new string.
maketargets only. Do NOT modify.golangci.yml. No Dockerfile/CI/script/.t.Parallel()and the//nolint:testpackageheader.Depends on
PR #20 (#10). Item 4 above is only meaningful once that has landed.
Priority
Low — wizard-only, no crash, and C-faithful as-is. Item 4 is the part worth
checking promptly.
Cross-reference: both the implementer and the reworker of PR #20 independently
surfaced this same loose end and offered to file it. It was already filed here
before either offer arrived, so it is tracked — no second issue needed.
Recording one detail from the rework that sharpens item 4 of the definition of
done above. The reworker confirmed empirically, by replacing the
createObjguard with
if falsein a throwaway copy, that a wand created from'!'comes back with
Which == 202. That is the wrapped value, not a negativeone:
readchar()returns abyte, soint(ch-'a') + 10is byte arithmeticand wraps —
'A'gives 234,'!'gives 202.Why that matters for this issue: the
KindNonecase reaches the pack througha different door than the out-of-range
Whichcase.Whichis now validatedat both boundaries by PR #20, but an unrecognized type glyph still produces
a well-formed object whose
Whichmay be perfectly in range. So thesave/restore question in item 4 is genuinely open —
ErrSaveCorruptvalidatesWhich, notKind, and aKindNoneobject may sail straight through. Verifyit rather than assuming either outcome.
A related trap worth knowing before touching this code, also found during the
rework: Go refuses to compile the constant form of this bug —
byte('A') - 'a'is a hard error (constant -32 of type byte overflows byte) — while silently wrapping the runtime-variable form. That asymmetry isexactly what produced the original false "negative
Which" analysis. If youreason about this arithmetic from a constant expression in a scratch file, the
compiler will mislead you about what the real code does.