feat(all): add hunting and events, commerce and rewards, binary calendars, GameData caching and login diagnostics
Implement hunting AP and progress, event tasks, exchanges, games, stories and actions; connect battle and reward handling. Add CashShop plugin and installation, server purchases and entitlements, recharge rules and plugin version metadata. Distribute binary calendars independently; cache GameData in memory and fix single-connection query deadlocks. Improve automatic login and recovery diagnostics; document configuration and release flows.
This commit is contained in:
@@ -4,11 +4,8 @@ package schedule
|
||||
|
||||
import (
|
||||
"bd2server/internal/server/versionconfig"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"bd2server/internal/server/wire"
|
||||
)
|
||||
@@ -44,23 +41,6 @@ type Service struct {
|
||||
Regular []RegularSeason `json:"regular"`
|
||||
}
|
||||
|
||||
// Load reads this server's versioned calendar policy. It does not infer live
|
||||
// official seasons from today's clock or from static reward design tables.
|
||||
func Load(path string) (*Service, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("schedule: read seed: %w", err)
|
||||
}
|
||||
var s Service
|
||||
if err := json.Unmarshal(raw, &s); err != nil {
|
||||
return nil, fmt.Errorf("schedule: decode seed: %w", err)
|
||||
}
|
||||
if err := s.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func (s *Service) Validate() error {
|
||||
if s == nil || s.Version != versionconfig.State() || s.CalculateMilliseconds == 0 || s.CalculateMilliseconds > math.MaxInt32 || len(s.Contents) == 0 {
|
||||
return errors.New("schedule: invalid calendar version/calculation interval")
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package schedule
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"bd2server/internal/server/wire"
|
||||
)
|
||||
|
||||
func TestCalendarReloadsChangedIDsAndTimesFromFile(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "schedule.json")
|
||||
func TestCalendarUsesInjectedIDsAndTimes(t *testing.T) {
|
||||
for _, revision := range []struct {
|
||||
id, start, end uint64
|
||||
raw string
|
||||
@@ -17,11 +15,11 @@ func TestCalendarReloadsChangedIDsAndTimesFromFile(t *testing.T) {
|
||||
{709, 101, 303, `{"version":"2.35.10","calculate_milliseconds":17,"contents":[{"id":81,"current":{"id":709,"start_milliseconds":101,"end_milliseconds":303},"next":{"id":710,"start_milliseconds":401,"end_milliseconds":501}}],"regular":[{"content_id":81,"season":9}]}`},
|
||||
{811, 701, 903, `{"version":"2.35.10","calculate_milliseconds":17,"contents":[{"id":81,"current":{"id":811,"start_milliseconds":701,"end_milliseconds":903},"next":{"id":812,"start_milliseconds":1001,"end_milliseconds":1201}}],"regular":[{"content_id":81,"season":11}]}`},
|
||||
} {
|
||||
if err := os.WriteFile(path, []byte(revision.raw), 0600); err != nil {
|
||||
var service Service
|
||||
if err := json.Unmarshal([]byte(revision.raw), &service); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service, err := Load(path)
|
||||
if err != nil {
|
||||
if err := service.Validate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, response, _, err := service.Handle("/ScheduleInfo", wire.AppendVarint(nil, 1, 1))
|
||||
@@ -37,40 +35,6 @@ func TestCalendarReloadsChangedIDsAndTimesFromFile(t *testing.T) {
|
||||
t.Fatalf("season=%d times=%d/%d", id, start, end)
|
||||
}
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(`{"version":"2.35.10","calculate_milliseconds":1,"contents":[{"id":1,"current":{"id":2,"start_milliseconds":4,"end_milliseconds":3}}]}`), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Load(path); err == nil {
|
||||
t.Fatal("reversed date accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion23510ContainsRequiredContentSix(t *testing.T) {
|
||||
service, err := Load("../../../seed/v2_35_10/schedule.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, response, handled, err := service.Handle("/ScheduleInfo", wire.AppendVarint(nil, 1, 1))
|
||||
if err != nil || !handled || code != 117 {
|
||||
t.Fatalf("schedule code=%d handled=%v err=%v", code, handled, err)
|
||||
}
|
||||
found := false
|
||||
if err := wire.Walk(response, func(field wire.Field) error {
|
||||
if field.Number == 2 {
|
||||
id, _, _ := wire.Varint(field.Value, 1)
|
||||
if id == 6 {
|
||||
found = true
|
||||
current, ok, _ := wire.Bytes(field.Value, 2)
|
||||
season, _, _ := wire.Varint(current, 1)
|
||||
if !ok || season != 26 {
|
||||
t.Fatalf("content 6 current season=%d found=%v", season, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil || !found {
|
||||
t.Fatalf("content 6 missing err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScheduleRejectsMissingSequence(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user