Files
bd2/go/internal/server/gamedata/cash_entitlements.go
T
Flechazo d8771caef2 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.
2026-10-05 11:50:31 +08:00

92 lines
2.4 KiB
Go

package gamedata
import (
"database/sql"
"fmt"
"sort"
)
type CashAttendanceReward struct {
ID uint64
Reward BattleReward
}
type CashEntitlementDesign struct {
AvatarSets map[uint64]bool
TicketTypes map[uint64]uint64
Attendance map[uint64][]CashAttendanceReward
AttendanceTypes map[uint64]uint64
}
func LoadCashEntitlementDesign(root, version string) (*CashEntitlementDesign, error) {
db, done, e := openStatDatabase(root, version)
if e != nil {
return nil, e
}
defer done()
return loadCashEntitlementDesign(db)
}
func loadCashEntitlementDesign(db *sql.DB) (*CashEntitlementDesign, error) {
d := &CashEntitlementDesign{AvatarSets: map[uint64]bool{}, TicketTypes: map[uint64]uint64{}, Attendance: map[uint64][]CashAttendanceReward{}, AttendanceTypes: map[uint64]uint64{}}
if e := readCashMetadata(db, "AvatarSetTable", func(raw []byte) error {
v, e := cashScalars(raw, 6)
if e != nil {
return e
}
if v[0] == 0 {
return fmt.Errorf("gamedata: invalid avatar set")
}
d.AvatarSets[v[0]] = true
return nil
}); e != nil {
return nil, e
}
if e := readCashMetadata(db, "ContentTicketTable", func(raw []byte) error {
v, e := cashScalars(raw, 4, 8)
if e != nil {
return e
}
d.TicketTypes[v[0]] = v[1]
return nil
}); e != nil {
return nil, e
}
if e := readCashMetadata(db, "ContentOpenTable", func(raw []byte) error {
v, e := cashScalars(raw, 6, 2)
if e != nil {
return e
}
if d.TicketTypes[v[0]] == 2 {
d.AttendanceTypes[v[0]] = v[1]
}
return nil
}); e != nil {
return nil, e
}
if e := readCashMetadata(db, "AttendanceRewardTable", func(raw []byte) error {
v, e := cashScalars(raw, 1, 2, 5, 4, 3)
if e != nil {
return e
}
if v[0] == 0 || v[1] == 0 || v[2] == 0 || v[4] == 0 {
return fmt.Errorf("gamedata: invalid cash attendance reward")
}
d.Attendance[v[0]] = append(d.Attendance[v[0]], CashAttendanceReward{v[1], BattleReward{Type: v[2], ID: v[3], Count: v[4]}})
return nil
}); e != nil {
return nil, e
}
for ticket, rows := range d.Attendance {
sort.Slice(rows, func(i, j int) bool { return rows[i].ID < rows[j].ID })
for i, r := range rows {
if r.ID != uint64(i+1) {
return nil, fmt.Errorf("gamedata: non-contiguous cash attendance %d", ticket)
}
}
if d.TicketTypes[ticket] != 2 && d.TicketTypes[ticket] != 3 {
return nil, fmt.Errorf("gamedata: missing cash attendance ticket %d", ticket)
}
d.Attendance[ticket] = rows
}
return d, nil
}