mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 17:02:42 +00:00
adventure: validate race/class + clamp level in admin char-migrate
Code-review fixes on the stuck-adventurer minting path: - AdminBuildConfirmedCharacter now runs race/class through parseRace/ parseClass (same as !setup), so a typo or non-playable class errors instead of silently minting a 1-HP/AC-10/no-spell sheet; inputs are normalized too. - Clamp level to dndMaxLevel to match the L20 cap enforced elsewhere. - CLI parses/validates every spec before db.Init, so a malformed spec mid-batch no longer leaves earlier specs already committed.
This commit is contained in:
@@ -40,16 +40,31 @@ func main() {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if err := db.Init(*dataDir); err != nil {
|
||||
log.Fatalf("db init: %v", err)
|
||||
// Parse every spec before touching the DB, so a malformed spec in the
|
||||
// middle of the batch fails fast instead of leaving the earlier specs
|
||||
// already committed to gogobee.db.
|
||||
type charSpec struct {
|
||||
uid id.UserID
|
||||
race plugin.DnDRace
|
||||
class plugin.DnDClass
|
||||
level int
|
||||
}
|
||||
|
||||
specs := make([]charSpec, 0, flag.NArg())
|
||||
for _, spec := range flag.Args() {
|
||||
uid, race, class, level, err := parseSpec(spec)
|
||||
if err != nil {
|
||||
log.Fatalf("bad spec %q: %v", spec, err)
|
||||
}
|
||||
c, err := plugin.AdminBuildConfirmedCharacter(uid, race, class, level)
|
||||
specs = append(specs, charSpec{uid, race, class, level})
|
||||
}
|
||||
|
||||
if err := db.Init(*dataDir); err != nil {
|
||||
log.Fatalf("db init: %v", err)
|
||||
}
|
||||
|
||||
for _, s := range specs {
|
||||
uid := s.uid
|
||||
c, err := plugin.AdminBuildConfirmedCharacter(s.uid, s.race, s.class, s.level)
|
||||
if err != nil {
|
||||
log.Fatalf("build %s: %v", uid, err)
|
||||
}
|
||||
|
||||
@@ -31,9 +31,25 @@ import (
|
||||
// SaveDnDCharacter upserts on user_id, so an existing pending draft is replaced.
|
||||
// initResources / ensureSpellsForCharacter are idempotent.
|
||||
func AdminBuildConfirmedCharacter(userID id.UserID, race DnDRace, class DnDClass, level int) (*DnDCharacter, error) {
|
||||
// Validate (and normalize) race/class through the same parsers !setup uses,
|
||||
// so a typo or a non-playable class fails loudly instead of silently minting
|
||||
// a broken sheet — an unknown class falls through classInfo to 1 HP / AC 10
|
||||
// / no spells, which looks "built" but isn't.
|
||||
r, ok := parseRace(string(race))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown race %q", race)
|
||||
}
|
||||
cl, ok := parseClass(string(class))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown or non-playable class %q", class)
|
||||
}
|
||||
race, class = r, cl
|
||||
if level < 1 {
|
||||
level = 1
|
||||
}
|
||||
if level > dndMaxLevel {
|
||||
level = dndMaxLevel
|
||||
}
|
||||
scores := applyRaceMods(race, classStatPriority(class))
|
||||
c := &DnDCharacter{
|
||||
UserID: userID,
|
||||
|
||||
Reference in New Issue
Block a user