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.
104 lines
3.4 KiB
Go
104 lines
3.4 KiB
Go
package gamedata
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"bd2server/internal/server/wire"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
// BattleReward is a static victory reward from BattleDeckTable in a pack DB.
|
|
// It is a design definition, not a player's owned item or server-generated ID.
|
|
type BattleReward struct {
|
|
Type uint64
|
|
ID uint64
|
|
Count uint64
|
|
}
|
|
|
|
// BattleDeckRewards uses the enemy battle deck actually chosen by the client
|
|
// (BattleEnter field 4); a monster may have several different battle decks.
|
|
func BattleDeckRewards(root, version string, packID int, deckID uint64) ([]BattleReward, error) {
|
|
if packID <= 0 || deckID == 0 || deckID > uint64(^uint(0)>>1) {
|
|
return nil, fmt.Errorf("gamedata: invalid battle pack/deck %d/%d", packID, deckID)
|
|
}
|
|
return readBattleRewards(root, version, packID, int(deckID), 0)
|
|
}
|
|
|
|
// BattleRewards is a legacy convenience for a monster with exactly one deck.
|
|
// Call BattleDeckRewards for a real battle, where the chosen deck is known.
|
|
func BattleRewards(root, version string, packID, monsterID int) ([]BattleReward, error) {
|
|
if packID <= 0 || monsterID <= 0 {
|
|
return nil, fmt.Errorf("gamedata: invalid battle pack/monster %d/%d", packID, monsterID)
|
|
}
|
|
return readBattleRewards(root, version, packID, 0, monsterID)
|
|
}
|
|
|
|
func readBattleRewards(root, version string, packID, deckID, monsterID int) ([]BattleReward, error) {
|
|
db, release, err := OpenDatabase(root, version, fmt.Sprintf("pack%d", packID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer release()
|
|
if monsterID != 0 {
|
|
var monster []byte
|
|
if err := db.QueryRow("SELECT ProtoBuf FROM FieldMonsterTable WHERE id=?", monsterID).Scan(&monster); err != nil {
|
|
return nil, fmt.Errorf("gamedata: monster %d: %w", monsterID, err)
|
|
}
|
|
deckIDs, err := packedInts(monster, 2)
|
|
if err != nil || len(deckIDs) != 1 || deckIDs[0] == 0 || deckIDs[0] > uint64(^uint(0)>>1) {
|
|
return nil, fmt.Errorf("gamedata: monster %d requires a selected battle deck (found %v): %w", monsterID, deckIDs, err)
|
|
}
|
|
deckID = int(deckIDs[0])
|
|
}
|
|
var design []byte
|
|
if err := db.QueryRow("SELECT ProtoBuf FROM BattleDeckTable WHERE id=?", deckID).Scan(&design); err != nil {
|
|
return nil, fmt.Errorf("gamedata: battle deck %d: %w", deckID, err)
|
|
}
|
|
types, err := packedInts(design, 36)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ids, err := packedInts(design, 34)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
counts, err := packedInts(design, 33)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(types) != len(ids) || len(ids) != len(counts) {
|
|
return nil, fmt.Errorf("gamedata: battle deck %d reward arrays differ in length", deckID)
|
|
}
|
|
rewards := make([]BattleReward, len(types))
|
|
for i := range rewards {
|
|
if types[i] == 0 || ids[i] == 0 || counts[i] == 0 {
|
|
return nil, fmt.Errorf("gamedata: invalid battle deck %d reward", deckID)
|
|
}
|
|
rewards[i] = BattleReward{Type: types[i], ID: ids[i], Count: counts[i]}
|
|
}
|
|
return rewards, nil
|
|
}
|
|
|
|
func packedInts(proto []byte, number int) ([]uint64, error) {
|
|
var out []uint64
|
|
err := wire.Walk(proto, func(f wire.Field) error {
|
|
if f.Number != number {
|
|
return nil
|
|
}
|
|
if f.Type != 0 && f.Type != 2 {
|
|
return fmt.Errorf("gamedata: field %d has type %d", number, f.Type)
|
|
}
|
|
for data := f.Value; len(data) > 0; {
|
|
value, n := binary.Uvarint(data)
|
|
if n <= 0 {
|
|
return fmt.Errorf("gamedata: invalid packed field %d", number)
|
|
}
|
|
out = append(out, value)
|
|
data = data[n:]
|
|
}
|
|
return nil
|
|
})
|
|
return out, err
|
|
}
|