Files
bd2/go/internal/server/gamedata/event_actions.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

151 lines
4.0 KiB
Go

package gamedata
import (
"bd2server/internal/server/wire"
"fmt"
"sort"
)
// EventActionRow preserves current-version design fields without introducing
// hard-coded event IDs. Runtime modules consume the named table and fields.
type EventActionRow struct {
Values map[int]uint64
Rewards []Reward
EventRewards []Reward
Text map[int]string
}
func (r EventActionRow) V(field int) uint64 { return r.Values[field] }
type EventActionsDesign struct {
Tables map[string][]EventActionRow
SpawnRewards map[[2]uint64]Reward
}
func (d *EventActionsDesign) Row(table string, idField int, id uint64) (EventActionRow, bool) {
for _, r := range d.Tables[table] {
if r.V(idField) == id {
return r, true
}
}
return EventActionRow{}, false
}
func LoadEventActionsDesign(root, version string) (*EventActionsDesign, error) {
db, done, e := openStatDatabase(root, version)
if e != nil {
return nil, e
}
defer done()
d := &EventActionsDesign{Tables: map[string][]EventActionRow{}, SpawnRewards: map[[2]uint64]Reward{}}
for _, table := range []string{"FieldEventDefaultTable", "FieldSpawnEventTable", "FieldEventMonsterTable", "FireworksTable", "VotingEventTable", "VotingCandidateTable", "VotingRoundTable", "VotingCountRewardTable", "VotingSeasonTable", "FriendshipSpecialEpisodeTable", "NpcQuizTable", "TacticsBingoGroupTable", "TacticsBingoTable", "CafeteriaDefaultTable", "CafeteriaEventTable"} {
rows, e := db.Query("SELECT ProtoBuf FROM " + table)
if e != nil {
return nil, e
}
for rows.Next() {
var p []byte
if e = rows.Scan(&p); e != nil {
rows.Close()
return nil, e
}
r := EventActionRow{Values: map[int]uint64{}, Text: map[int]string{}}
if e = wire.Walk(p, func(f wire.Field) error {
if f.Type == 0 {
v, _, er := wire.Varint(p, f.Number)
if er != nil {
return er
}
r.Values[f.Number] = v
} else if f.Type == 2 {
r.Text[f.Number] = string(f.Value)
}
return nil
}); e != nil {
rows.Close()
return nil, e
}
var array [3]int
switch table {
case "FriendshipSpecialEpisodeTable":
array = [3]int{9, 8, 7}
case "NpcQuizTable":
array = [3]int{12, 11, 10}
}
if array[0] > 0 {
rs, e := monsterHuntRewardArrays(p, array[0], array[1], array[2])
if e != nil {
rows.Close()
return nil, e
}
for _, x := range rs {
r.Rewards = append(r.Rewards, Reward{x.Type, x.ID, x.Count})
}
}
if table == "FriendshipSpecialEpisodeTable" {
rs, e := monsterHuntRewardArrays(p, 4, 3, 2)
if e != nil {
rows.Close()
return nil, e
}
for _, x := range rs {
r.EventRewards = append(r.EventRewards, Reward{x.Type, x.ID, x.Count})
}
}
if table == "FireworksTable" {
r.Rewards = []Reward{{r.V(9), r.V(8), r.V(7)}}
}
if table == "VotingCountRewardTable" {
r.Rewards = []Reward{{r.V(5), r.V(4), r.V(3)}}
}
d.Tables[table] = append(d.Tables[table], r)
}
e = rows.Err()
rows.Close()
if e != nil {
return nil, e
}
}
packs := map[uint64]bool{}
for _, r := range d.Tables["FieldEventMonsterTable"] {
packs[r.V(5)] = true
}
for pack := range packs {
pdb, closePack, e := openPackDatabase(root, version, int(pack))
if e != nil {
return nil, e
}
for _, r := range d.Tables["FieldEventMonsterTable"] {
if r.V(5) != pack {
continue
}
var p []byte
if e = pdb.QueryRow("SELECT ProtoBuf FROM FieldMonsterTable WHERE id=?", r.V(1)).Scan(&p); e != nil {
closePack()
return nil, fmt.Errorf("event spawn monster %d: %w", r.V(1), e)
}
t, e := optionalScalar(p, 29)
if e != nil {
closePack()
return nil, e
}
id, e := optionalScalar(p, 28)
if e != nil {
closePack()
return nil, e
}
count, e := optionalScalar(p, 27)
if e != nil {
closePack()
return nil, e
}
d.SpawnRewards[[2]uint64{pack, r.V(1)}] = Reward{t, id, count}
}
closePack()
}
for table, rows := range d.Tables {
sort.Slice(rows, func(i, j int) bool { return rows[i].V(2) < rows[j].V(2) })
d.Tables[table] = rows
}
return d, nil
}