81 lines
1.9 KiB
Go
81 lines
1.9 KiB
Go
// Package runner orchestrates the once-a-day simulation against the store.
|
|
package runner
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"wh/config"
|
|
"wh/sim"
|
|
"wh/store"
|
|
)
|
|
|
|
const (
|
|
MetaConfig = "config"
|
|
MetaDay = "day"
|
|
MetaMarket = "market"
|
|
)
|
|
|
|
// RunNextDay simulates the next day atomically: everything is committed
|
|
// together or not at all, so a crashed run can simply be repeated.
|
|
func RunNextDay(ctx context.Context, s *store.Store, cfg config.Config) (sim.DayResult, error) {
|
|
var res sim.DayResult
|
|
cfgJSON, err := json.Marshal(cfg)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
err = s.WithTx(ctx, func(q *store.Q) error {
|
|
if err := q.LockWorld(ctx); err != nil {
|
|
return err
|
|
}
|
|
// Refuse to continue a world with different parameters.
|
|
if prev, ok, err := q.GetMeta(ctx, MetaConfig); err != nil {
|
|
return err
|
|
} else if ok && prev != string(cfgJSON) {
|
|
return fmt.Errorf("config differs from the one this world was created with:\n stored: %s\n current: %s", prev, cfgJSON)
|
|
}
|
|
|
|
st, err := q.LoadState(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if st == nil {
|
|
st = sim.NewState(cfg)
|
|
if err := q.SetMeta(ctx, MetaConfig, string(cfgJSON)); err != nil {
|
|
return err
|
|
}
|
|
if err := q.SaveState(ctx, st); err != nil { // day 0 snapshot
|
|
return err
|
|
}
|
|
}
|
|
|
|
var in sim.DayInput
|
|
if in.Launches, err = q.PendingLaunches(ctx); err != nil {
|
|
return err
|
|
}
|
|
if in.Uplinks, err = q.PendingUplinks(ctx); err != nil {
|
|
return err
|
|
}
|
|
if res, err = st.RunDay(cfg, in); err != nil {
|
|
return err
|
|
}
|
|
if err := q.SaveState(ctx, st); err != nil {
|
|
return err
|
|
}
|
|
if err := q.RecordRun(ctx, res, time.Now().UTC().Format(time.RFC3339)); err != nil {
|
|
return err
|
|
}
|
|
if err := q.AfterRun(ctx, st); err != nil {
|
|
return err
|
|
}
|
|
mk, _ := json.Marshal(st.Market.Prices)
|
|
if err := q.SetMeta(ctx, MetaMarket, string(mk)); err != nil {
|
|
return err
|
|
}
|
|
return q.SetMeta(ctx, MetaDay, fmt.Sprint(st.Day))
|
|
})
|
|
return res, err
|
|
}
|