feat: gamemaster
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// Package scheduler exposes the next-tick computation Game Master uses
|
||||
// to advance `runtime_records.next_generation_at` after a successful
|
||||
// turn generation. It is a thin, stateless wrapper over
|
||||
// `domain/schedule.Schedule.Next` with the force-next-turn skip rule
|
||||
// baked in via the `skipNextTick` parameter.
|
||||
//
|
||||
// Two callers consume the wrapper today:
|
||||
//
|
||||
// - `service/turngeneration` recomputes the next tick after a
|
||||
// successful (non-finished) generation;
|
||||
// - `service/adminforce` (Stage 17) reuses the same instance so the
|
||||
// skip rule lives in exactly one place.
|
||||
//
|
||||
// The package depends only on `domain/schedule` and stdlib `time`. It
|
||||
// holds no clock and no logger; callers pass `after` explicitly.
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"galaxy/gamemaster/internal/domain/schedule"
|
||||
)
|
||||
|
||||
// Service computes the next scheduler-driven turn-generation tick.
|
||||
type Service struct{}
|
||||
|
||||
// New constructs a stateless Service value. Returning a pointer keeps
|
||||
// the construction shape consistent with the other GM services even
|
||||
// though Service has no dependencies.
|
||||
func New() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// ComputeNext parses turnSchedule and returns the next firing time
|
||||
// strictly after `after`, applying the force-next-turn skip rule when
|
||||
// skipNextTick is true.
|
||||
//
|
||||
// When skipNextTick is true the wrapper computes the immediate next
|
||||
// cron step and then advances by one further step, so the inter-turn
|
||||
// spacing is never shorter than one schedule interval. The returned
|
||||
// `skipConsumed` flag reports whether the wrapper consumed the skip
|
||||
// (true when skipNextTick was true).
|
||||
//
|
||||
// On parse error ComputeNext returns the zero time, false, and the
|
||||
// error wrapped from `schedule.Parse`. The caller is responsible for
|
||||
// mapping it to the orchestrator-level `invalid_request` code.
|
||||
func (service *Service) ComputeNext(turnSchedule string, after time.Time, skipNextTick bool) (time.Time, bool, error) {
|
||||
if service == nil {
|
||||
return time.Time{}, false, errors.New("scheduler compute next: nil service")
|
||||
}
|
||||
parsed, err := schedule.Parse(strings.TrimSpace(turnSchedule))
|
||||
if err != nil {
|
||||
return time.Time{}, false, err
|
||||
}
|
||||
next, skipConsumed := parsed.Next(after, skipNextTick)
|
||||
return next, skipConsumed, nil
|
||||
}
|
||||
Reference in New Issue
Block a user