mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Add authentication server, dev CLI, Docker multi-service setup, and cross-platform improvements
This commit is contained in:
@@ -3,6 +3,7 @@ package database
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -40,3 +41,9 @@ func Open(path string) (*sql.DB, error) {
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func Checkpoint(db *sql.DB) {
|
||||
if _, err := db.Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil {
|
||||
log.Printf("WAL checkpoint: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/service"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type diffUserDataGetter interface {
|
||||
GetDiffUserData() map[string]*pb.DiffData
|
||||
}
|
||||
|
||||
var (
|
||||
diffFieldCache sync.Map // map[reflect.Type]bool
|
||||
diffDataMapTyp = reflect.TypeFor[map[string]*pb.DiffData]()
|
||||
)
|
||||
|
||||
func hasDiffField(resp any) bool {
|
||||
t := reflect.TypeOf(resp)
|
||||
if cached, ok := diffFieldCache.Load(t); ok {
|
||||
return cached.(bool)
|
||||
}
|
||||
elem := t
|
||||
if elem.Kind() == reflect.Pointer {
|
||||
elem = elem.Elem()
|
||||
}
|
||||
f, ok := elem.FieldByName("DiffUserData")
|
||||
result := ok && f.Type == diffDataMapTyp
|
||||
diffFieldCache.Store(t, result)
|
||||
return result
|
||||
}
|
||||
|
||||
func NewDiffInterceptor(
|
||||
users store.UserRepository,
|
||||
sessions store.SessionRepository,
|
||||
) grpc.UnaryServerInterceptor {
|
||||
return func(
|
||||
ctx context.Context,
|
||||
req any,
|
||||
info *grpc.UnaryServerInfo,
|
||||
handler grpc.UnaryHandler,
|
||||
) (any, error) {
|
||||
if skipDiffForMethod(info.FullMethod) {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
userId := service.CurrentUserId(ctx, users, sessions)
|
||||
if userId == 0 {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
before, err := users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
resp, handlerErr := handler(ctx, req)
|
||||
if handlerErr != nil || resp == nil {
|
||||
return resp, handlerErr
|
||||
}
|
||||
|
||||
if !hasDiffField(resp) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if getter, ok := resp.(diffUserDataGetter); ok {
|
||||
if existing := getter.GetDiffUserData(); len(existing) > 0 {
|
||||
setUpdateNamesTrailer(ctx, existing)
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
after, err := users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
changed := userdata.ChangedTables(&before, &after)
|
||||
if len(changed) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
diff := userdata.ComputeDelta(&before, &after, changed)
|
||||
reflect.ValueOf(resp).Elem().FieldByName("DiffUserData").Set(reflect.ValueOf(diff))
|
||||
setUpdateNamesTrailer(ctx, diff)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func skipDiffForMethod(method string) bool {
|
||||
switch method {
|
||||
case "/apb.api.user.UserService/Auth",
|
||||
"/apb.api.user.UserService/RegisterUser",
|
||||
"/apb.api.user.UserService/TransferUser",
|
||||
"/apb.api.user.UserService/TransferUserByFacebook",
|
||||
"/apb.api.config.ConfigService/GetConfig",
|
||||
"/apb.api.data.DataService/GetLatestMasterDataVersion",
|
||||
"/apb.api.data.DataService/GetUserDataNameV2",
|
||||
"/apb.api.data.DataService/GetUserData":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setUpdateNamesTrailer(ctx context.Context, diff map[string]*pb.DiffData) {
|
||||
if len(diff) == 0 {
|
||||
return
|
||||
}
|
||||
keys := make([]string, 0, len(diff))
|
||||
for key := range diff {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
value := strings.Join(keys, ",")
|
||||
if err := grpc.SetTrailer(ctx, metadata.Pairs("x-apb-update-user-data-names", value)); err != nil {
|
||||
log.Printf("[DiffInterceptor] failed to set trailer: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func Logging(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
log.Printf(">>> %s", info.FullMethod)
|
||||
resp, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
log.Printf("<<< %s ERROR: %v", info.FullMethod, err)
|
||||
} else {
|
||||
log.Printf("<<< %s OK", info.FullMethod)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func UnknownService(_ any, stream grpc.ServerStream) error {
|
||||
fullMethod, ok := grpc.MethodFromServerStream(stream)
|
||||
if !ok {
|
||||
fullMethod = "<unknown>"
|
||||
}
|
||||
log.Printf(">>> %s", fullMethod)
|
||||
err := status.Errorf(codes.Unimplemented, "unknown service or method %s", fullMethod)
|
||||
log.Printf("<<< %s ERROR: %v", fullMethod, err)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func Platform(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
p := model.ClientPlatformFromHeaders(ctx)
|
||||
ctx = model.NewContextWithPlatform(ctx, p)
|
||||
return handler(ctx, req)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TimeSync(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
resp, err := handler(ctx, req)
|
||||
switch info.FullMethod {
|
||||
case "/apb.api.user.UserService/Auth",
|
||||
"/apb.api.user.UserService/RegisterUser",
|
||||
"/apb.api.user.UserService/TransferUser":
|
||||
default:
|
||||
grpc.SetTrailer(ctx, metadata.Pairs(
|
||||
"x-apb-response-datetime", fmt.Sprintf("%d", gametime.NowMillis()),
|
||||
))
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
@@ -8,15 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type bigHuntBossQuestRow struct {
|
||||
BigHuntBossQuestId int32 `json:"BigHuntBossQuestId"`
|
||||
BigHuntBossId int32 `json:"BigHuntBossId"`
|
||||
BigHuntQuestGroupId int32 `json:"BigHuntQuestGroupId"`
|
||||
BigHuntBossQuestScoreCoefficientId int32 `json:"BigHuntBossQuestScoreCoefficientId"`
|
||||
BigHuntScoreRewardGroupScheduleId int32 `json:"BigHuntScoreRewardGroupScheduleId"`
|
||||
DailyChallengeCount int32 `json:"DailyChallengeCount"`
|
||||
}
|
||||
|
||||
type BigHuntBossQuestRow struct {
|
||||
BigHuntBossQuestId int32
|
||||
BigHuntBossId int32
|
||||
@@ -25,97 +16,39 @@ type BigHuntBossQuestRow struct {
|
||||
DailyChallengeCount int32
|
||||
}
|
||||
|
||||
type bigHuntQuestRow struct {
|
||||
BigHuntQuestId int32 `json:"BigHuntQuestId"`
|
||||
QuestId int32 `json:"QuestId"`
|
||||
BigHuntQuestScoreCoefficientId int32 `json:"BigHuntQuestScoreCoefficientId"`
|
||||
}
|
||||
|
||||
type BigHuntQuestRow struct {
|
||||
BigHuntQuestId int32
|
||||
QuestId int32
|
||||
BigHuntQuestScoreCoefficientId int32
|
||||
}
|
||||
|
||||
type bigHuntQuestScoreCoefficientRow struct {
|
||||
BigHuntQuestScoreCoefficientId int32 `json:"BigHuntQuestScoreCoefficientId"`
|
||||
ScoreDifficultBonusPermil int32 `json:"ScoreDifficultBonusPermil"`
|
||||
}
|
||||
|
||||
type bigHuntBossRow struct {
|
||||
BigHuntBossId int32 `json:"BigHuntBossId"`
|
||||
BigHuntBossGradeGroupId int32 `json:"BigHuntBossGradeGroupId"`
|
||||
AttributeType int32 `json:"AttributeType"`
|
||||
}
|
||||
|
||||
type BigHuntBossRow struct {
|
||||
BigHuntBossId int32
|
||||
BigHuntBossGradeGroupId int32
|
||||
AttributeType int32
|
||||
}
|
||||
|
||||
type bigHuntBossGradeGroupRow struct {
|
||||
BigHuntBossGradeGroupId int32 `json:"BigHuntBossGradeGroupId"`
|
||||
NecessaryScore int64 `json:"NecessaryScore"`
|
||||
AssetGradeIconId int32 `json:"AssetGradeIconId"`
|
||||
}
|
||||
|
||||
type GradeThreshold struct {
|
||||
NecessaryScore int64
|
||||
AssetGradeIconId int32
|
||||
}
|
||||
|
||||
type bigHuntScheduleRow struct {
|
||||
BigHuntScheduleId int32 `json:"BigHuntScheduleId"`
|
||||
ChallengeStartDatetime int64 `json:"ChallengeStartDatetime"`
|
||||
ChallengeEndDatetime int64 `json:"ChallengeEndDatetime"`
|
||||
}
|
||||
|
||||
type scoreRewardScheduleRow struct {
|
||||
BigHuntScoreRewardGroupScheduleId int32 `json:"BigHuntScoreRewardGroupScheduleId"`
|
||||
GroupIndex int32 `json:"GroupIndex"`
|
||||
BigHuntScoreRewardGroupId int32 `json:"BigHuntScoreRewardGroupId"`
|
||||
StartDatetime int64 `json:"StartDatetime"`
|
||||
}
|
||||
|
||||
type ScoreRewardScheduleEntry struct {
|
||||
BigHuntScoreRewardGroupId int32
|
||||
StartDatetime int64
|
||||
}
|
||||
|
||||
type scoreRewardGroupRow struct {
|
||||
BigHuntScoreRewardGroupId int32 `json:"BigHuntScoreRewardGroupId"`
|
||||
NecessaryScore int64 `json:"NecessaryScore"`
|
||||
BigHuntRewardGroupId int32 `json:"BigHuntRewardGroupId"`
|
||||
}
|
||||
|
||||
type ScoreRewardThreshold struct {
|
||||
NecessaryScore int64
|
||||
BigHuntRewardGroupId int32
|
||||
}
|
||||
|
||||
type rewardGroupRow struct {
|
||||
BigHuntRewardGroupId int32 `json:"BigHuntRewardGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type RewardItem struct {
|
||||
PossessionType int32
|
||||
PossessionId int32
|
||||
Count int32
|
||||
}
|
||||
|
||||
type weeklyRewardScheduleRow struct {
|
||||
BigHuntWeeklyAttributeScoreRewardGroupScheduleId int32 `json:"BigHuntWeeklyAttributeScoreRewardGroupScheduleId"`
|
||||
AttributeType int32 `json:"AttributeType"`
|
||||
GroupIndex int32 `json:"GroupIndex"`
|
||||
BigHuntScoreRewardGroupId int32 `json:"BigHuntScoreRewardGroupId"`
|
||||
StartDatetime int64 `json:"StartDatetime"`
|
||||
}
|
||||
|
||||
type BigHuntWeeklyRewardKey struct {
|
||||
ScheduleId int32
|
||||
AttributeType int32
|
||||
@@ -189,7 +122,7 @@ func (c *BigHuntCatalog) CollectNewRewards(scoreRewardGroupId int32, oldMax, new
|
||||
}
|
||||
|
||||
func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
bossQuestRows, err := utils.ReadJSON[bigHuntBossQuestRow]("EntityMBigHuntBossQuestTable.json")
|
||||
bossQuestRows, err := utils.ReadTable[EntityMBigHuntBossQuest]("m_big_hunt_boss_quest")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt boss quest table: %v", err)
|
||||
}
|
||||
@@ -204,7 +137,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
questRows, err := utils.ReadJSON[bigHuntQuestRow]("EntityMBigHuntQuestTable.json")
|
||||
questRows, err := utils.ReadTable[EntityMBigHuntQuest]("m_big_hunt_quest")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt quest table: %v", err)
|
||||
}
|
||||
@@ -217,7 +150,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
coeffRows, err := utils.ReadJSON[bigHuntQuestScoreCoefficientRow]("EntityMBigHuntQuestScoreCoefficientTable.json")
|
||||
coeffRows, err := utils.ReadTable[EntityMBigHuntQuestScoreCoefficient]("m_big_hunt_quest_score_coefficient")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt quest score coefficient table: %v", err)
|
||||
}
|
||||
@@ -226,7 +159,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
scoreCoefficients[r.BigHuntQuestScoreCoefficientId] = r.ScoreDifficultBonusPermil
|
||||
}
|
||||
|
||||
bossRows, err := utils.ReadJSON[bigHuntBossRow]("EntityMBigHuntBossTable.json")
|
||||
bossRows, err := utils.ReadTable[EntityMBigHuntBoss]("m_big_hunt_boss")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt boss table: %v", err)
|
||||
}
|
||||
@@ -239,7 +172,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
gradeRows, err := utils.ReadJSON[bigHuntBossGradeGroupRow]("EntityMBigHuntBossGradeGroupTable.json")
|
||||
gradeRows, err := utils.ReadTable[EntityMBigHuntBossGradeGroup]("m_big_hunt_boss_grade_group")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt boss grade group table: %v", err)
|
||||
}
|
||||
@@ -256,7 +189,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
})
|
||||
}
|
||||
|
||||
scheduleRows, err := utils.ReadJSON[bigHuntScheduleRow]("EntityMBigHuntScheduleTable.json")
|
||||
scheduleRows, err := utils.ReadTable[EntityMBigHuntSchedule]("m_big_hunt_schedule")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt schedule table: %v", err)
|
||||
}
|
||||
@@ -274,7 +207,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
}
|
||||
}
|
||||
|
||||
rewardSchedRows, err := utils.ReadJSON[scoreRewardScheduleRow]("EntityMBigHuntScoreRewardGroupScheduleTable.json")
|
||||
rewardSchedRows, err := utils.ReadTable[EntityMBigHuntScoreRewardGroupSchedule]("m_big_hunt_score_reward_group_schedule")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt score reward group schedule table: %v", err)
|
||||
}
|
||||
@@ -294,7 +227,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
})
|
||||
}
|
||||
|
||||
rewardGroupRows, err := utils.ReadJSON[scoreRewardGroupRow]("EntityMBigHuntScoreRewardGroupTable.json")
|
||||
rewardGroupRows, err := utils.ReadTable[EntityMBigHuntScoreRewardGroup]("m_big_hunt_score_reward_group")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt score reward group table: %v", err)
|
||||
}
|
||||
@@ -314,7 +247,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
})
|
||||
}
|
||||
|
||||
rewardItemRows, err := utils.ReadJSON[rewardGroupRow]("EntityMBigHuntRewardGroupTable.json")
|
||||
rewardItemRows, err := utils.ReadTable[EntityMBigHuntRewardGroup]("m_big_hunt_reward_group")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt reward group table: %v", err)
|
||||
}
|
||||
@@ -327,7 +260,7 @@ func LoadBigHuntCatalog() *BigHuntCatalog {
|
||||
})
|
||||
}
|
||||
|
||||
weeklySchedRows, err := utils.ReadJSON[weeklyRewardScheduleRow]("EntityMBigHuntWeeklyAttributeScoreRewardGroupScheduleTable.json")
|
||||
weeklySchedRows, err := utils.ReadTable[EntityMBigHuntWeeklyAttributeScoreRewardGroupSchedule]("m_big_hunt_weekly_attribute_score_reward_group_schedule")
|
||||
if err != nil {
|
||||
log.Fatalf("load big hunt weekly attribute score reward group schedule table: %v", err)
|
||||
}
|
||||
|
||||
@@ -5,18 +5,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type cageOrnament struct {
|
||||
CageOrnamentId int32 `json:"CageOrnamentId"`
|
||||
CageOrnamentRewardId int32 `json:"CageOrnamentRewardId"`
|
||||
}
|
||||
|
||||
type cageOrnamentRewardRow struct {
|
||||
CageOrnamentRewardId int32 `json:"CageOrnamentRewardId"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type CageOrnamentReward struct {
|
||||
PossessionType int32
|
||||
PossessionId int32
|
||||
@@ -38,11 +26,11 @@ func (c *CageOrnamentCatalog) LookupReward(cageOrnamentId int32) (CageOrnamentRe
|
||||
}
|
||||
|
||||
func LoadCageOrnamentCatalog() *CageOrnamentCatalog {
|
||||
ornaments, err := utils.ReadJSON[cageOrnament]("EntityMCageOrnamentTable.json")
|
||||
ornaments, err := utils.ReadTable[EntityMCageOrnament]("m_cage_ornament")
|
||||
if err != nil {
|
||||
log.Fatalf("load cage ornament table: %v", err)
|
||||
}
|
||||
rewards, err := utils.ReadJSON[cageOrnamentRewardRow]("EntityMCageOrnamentRewardTable.json")
|
||||
rewards, err := utils.ReadTable[EntityMCageOrnamentReward]("m_cage_ornament_reward")
|
||||
if err != nil {
|
||||
log.Fatalf("load cage ornament reward table: %v", err)
|
||||
}
|
||||
|
||||
@@ -6,24 +6,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type CharacterRebirthRow struct {
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
CharacterRebirthStepGroupId int32 `json:"CharacterRebirthStepGroupId"`
|
||||
}
|
||||
|
||||
type CharacterRebirthStepRow struct {
|
||||
CharacterRebirthStepGroupId int32 `json:"CharacterRebirthStepGroupId"`
|
||||
BeforeRebirthCount int32 `json:"BeforeRebirthCount"`
|
||||
CostumeLevelLimitUp int32 `json:"CostumeLevelLimitUp"`
|
||||
CharacterRebirthMaterialGroupId int32 `json:"CharacterRebirthMaterialGroupId"`
|
||||
}
|
||||
|
||||
type CharacterRebirthMaterialRow struct {
|
||||
CharacterRebirthMaterialGroupId int32 `json:"CharacterRebirthMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type StepKey struct {
|
||||
GroupId int32
|
||||
BeforeRebirthCount int32
|
||||
@@ -31,22 +13,22 @@ type StepKey struct {
|
||||
|
||||
type CharacterRebirthCatalog struct {
|
||||
StepGroupByCharacterId map[int32]int32
|
||||
StepByGroupAndCount map[StepKey]CharacterRebirthStepRow
|
||||
MaterialsByGroupId map[int32][]CharacterRebirthMaterialRow
|
||||
StepByGroupAndCount map[StepKey]EntityMCharacterRebirthStepGroup
|
||||
MaterialsByGroupId map[int32][]EntityMCharacterRebirthMaterialGroup
|
||||
}
|
||||
|
||||
func LoadCharacterRebirthCatalog() (*CharacterRebirthCatalog, error) {
|
||||
rebirthRows, err := utils.ReadJSON[CharacterRebirthRow]("EntityMCharacterRebirthTable.json")
|
||||
rebirthRows, err := utils.ReadTable[EntityMCharacterRebirth]("m_character_rebirth")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character rebirth table: %w", err)
|
||||
}
|
||||
|
||||
stepRows, err := utils.ReadJSON[CharacterRebirthStepRow]("EntityMCharacterRebirthStepGroupTable.json")
|
||||
stepRows, err := utils.ReadTable[EntityMCharacterRebirthStepGroup]("m_character_rebirth_step_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character rebirth step group table: %w", err)
|
||||
}
|
||||
|
||||
materialRows, err := utils.ReadJSON[CharacterRebirthMaterialRow]("EntityMCharacterRebirthMaterialGroupTable.json")
|
||||
materialRows, err := utils.ReadTable[EntityMCharacterRebirthMaterialGroup]("m_character_rebirth_material_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character rebirth material group table: %w", err)
|
||||
}
|
||||
@@ -56,12 +38,12 @@ func LoadCharacterRebirthCatalog() (*CharacterRebirthCatalog, error) {
|
||||
stepGroupByCharacterId[r.CharacterId] = r.CharacterRebirthStepGroupId
|
||||
}
|
||||
|
||||
stepByGroupAndCount := make(map[StepKey]CharacterRebirthStepRow, len(stepRows))
|
||||
stepByGroupAndCount := make(map[StepKey]EntityMCharacterRebirthStepGroup, len(stepRows))
|
||||
for _, s := range stepRows {
|
||||
stepByGroupAndCount[StepKey{GroupId: s.CharacterRebirthStepGroupId, BeforeRebirthCount: s.BeforeRebirthCount}] = s
|
||||
}
|
||||
|
||||
materialsByGroupId := make(map[int32][]CharacterRebirthMaterialRow)
|
||||
materialsByGroupId := make(map[int32][]EntityMCharacterRebirthMaterialGroup)
|
||||
for _, m := range materialRows {
|
||||
materialsByGroupId[m.CharacterRebirthMaterialGroupId] = append(materialsByGroupId[m.CharacterRebirthMaterialGroupId], m)
|
||||
}
|
||||
|
||||
@@ -7,66 +7,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type CharacterBoardPanelRow struct {
|
||||
CharacterBoardPanelId int32 `json:"CharacterBoardPanelId"`
|
||||
CharacterBoardId int32 `json:"CharacterBoardId"`
|
||||
CharacterBoardPanelUnlockConditionGroupId int32 `json:"CharacterBoardPanelUnlockConditionGroupId"`
|
||||
CharacterBoardPanelReleasePossessionGroupId int32 `json:"CharacterBoardPanelReleasePossessionGroupId"`
|
||||
CharacterBoardPanelReleaseRewardGroupId int32 `json:"CharacterBoardPanelReleaseRewardGroupId"`
|
||||
CharacterBoardPanelReleaseEffectGroupId int32 `json:"CharacterBoardPanelReleaseEffectGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
ParentCharacterBoardPanelId int32 `json:"ParentCharacterBoardPanelId"`
|
||||
PlaceIndex int32 `json:"PlaceIndex"`
|
||||
}
|
||||
|
||||
type CharacterBoardReleasePossessionRow struct {
|
||||
CharacterBoardPanelReleasePossessionGroupId int32 `json:"CharacterBoardPanelReleasePossessionGroupId"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type CharacterBoardReleaseEffectRow struct {
|
||||
CharacterBoardPanelReleaseEffectGroupId int32 `json:"CharacterBoardPanelReleaseEffectGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
CharacterBoardEffectType int32 `json:"CharacterBoardEffectType"`
|
||||
CharacterBoardEffectId int32 `json:"CharacterBoardEffectId"`
|
||||
EffectValue int32 `json:"EffectValue"`
|
||||
}
|
||||
|
||||
type CharacterBoardRow struct {
|
||||
CharacterBoardId int32 `json:"CharacterBoardId"`
|
||||
CharacterBoardGroupId int32 `json:"CharacterBoardGroupId"`
|
||||
CharacterBoardUnlockConditionGroupId int32 `json:"CharacterBoardUnlockConditionGroupId"`
|
||||
ReleaseRank int32 `json:"ReleaseRank"`
|
||||
}
|
||||
|
||||
type CharacterBoardStatusUpRow struct {
|
||||
CharacterBoardStatusUpId int32 `json:"CharacterBoardStatusUpId"`
|
||||
CharacterBoardStatusUpType int32 `json:"CharacterBoardStatusUpType"`
|
||||
CharacterBoardEffectTargetGroupId int32 `json:"CharacterBoardEffectTargetGroupId"`
|
||||
}
|
||||
|
||||
type CharacterBoardAbilityRow struct {
|
||||
CharacterBoardAbilityId int32 `json:"CharacterBoardAbilityId"`
|
||||
CharacterBoardEffectTargetGroupId int32 `json:"CharacterBoardEffectTargetGroupId"`
|
||||
AbilityId int32 `json:"AbilityId"`
|
||||
}
|
||||
|
||||
type CharacterBoardAbilityMaxLevelRow struct {
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
AbilityId int32 `json:"AbilityId"`
|
||||
MaxLevel int32 `json:"MaxLevel"`
|
||||
}
|
||||
|
||||
type CharacterBoardEffectTargetRow struct {
|
||||
CharacterBoardEffectTargetGroupId int32 `json:"CharacterBoardEffectTargetGroupId"`
|
||||
GroupIndex int32 `json:"GroupIndex"`
|
||||
CharacterBoardEffectTargetType int32 `json:"CharacterBoardEffectTargetType"`
|
||||
TargetValue int32 `json:"TargetValue"`
|
||||
}
|
||||
|
||||
type CharacterBoardAssignmentRow struct {
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
CharacterBoardCategoryId int32 `json:"CharacterBoardCategoryId"`
|
||||
@@ -83,68 +23,68 @@ type CharacterBoardGroupRow struct {
|
||||
}
|
||||
|
||||
type CharacterBoardCatalog struct {
|
||||
PanelById map[int32]CharacterBoardPanelRow
|
||||
PanelsByBoardId map[int32][]CharacterBoardPanelRow
|
||||
ReleaseCostsByGroupId map[int32][]CharacterBoardReleasePossessionRow
|
||||
ReleaseEffectsByGroupId map[int32][]CharacterBoardReleaseEffectRow
|
||||
StatusUpById map[int32]CharacterBoardStatusUpRow
|
||||
AbilityById map[int32]CharacterBoardAbilityRow
|
||||
PanelById map[int32]EntityMCharacterBoardPanel
|
||||
PanelsByBoardId map[int32][]EntityMCharacterBoardPanel
|
||||
ReleaseCostsByGroupId map[int32][]EntityMCharacterBoardPanelReleasePossessionGroup
|
||||
ReleaseEffectsByGroupId map[int32][]EntityMCharacterBoardPanelReleaseEffectGroup
|
||||
StatusUpById map[int32]EntityMCharacterBoardStatusUp
|
||||
AbilityById map[int32]EntityMCharacterBoardAbility
|
||||
AbilityMaxLevel map[store.CharacterBoardAbilityKey]int32
|
||||
EffectTargetsByGroupId map[int32][]CharacterBoardEffectTargetRow
|
||||
BoardById map[int32]CharacterBoardRow
|
||||
EffectTargetsByGroupId map[int32][]EntityMCharacterBoardEffectTargetGroup
|
||||
BoardById map[int32]EntityMCharacterBoard
|
||||
}
|
||||
|
||||
func LoadCharacterBoardCatalog() (*CharacterBoardCatalog, error) {
|
||||
panels, err := utils.ReadJSON[CharacterBoardPanelRow]("EntityMCharacterBoardPanelTable.json")
|
||||
panels, err := utils.ReadTable[EntityMCharacterBoardPanel]("m_character_board_panel")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board panel table: %w", err)
|
||||
}
|
||||
|
||||
costs, err := utils.ReadJSON[CharacterBoardReleasePossessionRow]("EntityMCharacterBoardPanelReleasePossessionGroupTable.json")
|
||||
costs, err := utils.ReadTable[EntityMCharacterBoardPanelReleasePossessionGroup]("m_character_board_panel_release_possession_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board release possession table: %w", err)
|
||||
}
|
||||
|
||||
effects, err := utils.ReadJSON[CharacterBoardReleaseEffectRow]("EntityMCharacterBoardPanelReleaseEffectGroupTable.json")
|
||||
effects, err := utils.ReadTable[EntityMCharacterBoardPanelReleaseEffectGroup]("m_character_board_panel_release_effect_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board release effect table: %w", err)
|
||||
}
|
||||
|
||||
boards, err := utils.ReadJSON[CharacterBoardRow]("EntityMCharacterBoardTable.json")
|
||||
boards, err := utils.ReadTable[EntityMCharacterBoard]("m_character_board")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board table: %w", err)
|
||||
}
|
||||
|
||||
statusUps, err := utils.ReadJSON[CharacterBoardStatusUpRow]("EntityMCharacterBoardStatusUpTable.json")
|
||||
statusUps, err := utils.ReadTable[EntityMCharacterBoardStatusUp]("m_character_board_status_up")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board status up table: %w", err)
|
||||
}
|
||||
|
||||
abilities, err := utils.ReadJSON[CharacterBoardAbilityRow]("EntityMCharacterBoardAbilityTable.json")
|
||||
abilities, err := utils.ReadTable[EntityMCharacterBoardAbility]("m_character_board_ability")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board ability table: %w", err)
|
||||
}
|
||||
|
||||
abilityMaxLevels, err := utils.ReadJSON[CharacterBoardAbilityMaxLevelRow]("EntityMCharacterBoardAbilityMaxLevelTable.json")
|
||||
abilityMaxLevels, err := utils.ReadTable[EntityMCharacterBoardAbilityMaxLevel]("m_character_board_ability_max_level")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board ability max level table: %w", err)
|
||||
}
|
||||
|
||||
targets, err := utils.ReadJSON[CharacterBoardEffectTargetRow]("EntityMCharacterBoardEffectTargetGroupTable.json")
|
||||
targets, err := utils.ReadTable[EntityMCharacterBoardEffectTargetGroup]("m_character_board_effect_target_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load character board effect target table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &CharacterBoardCatalog{
|
||||
PanelById: make(map[int32]CharacterBoardPanelRow, len(panels)),
|
||||
PanelsByBoardId: make(map[int32][]CharacterBoardPanelRow),
|
||||
ReleaseCostsByGroupId: make(map[int32][]CharacterBoardReleasePossessionRow),
|
||||
ReleaseEffectsByGroupId: make(map[int32][]CharacterBoardReleaseEffectRow),
|
||||
StatusUpById: make(map[int32]CharacterBoardStatusUpRow, len(statusUps)),
|
||||
AbilityById: make(map[int32]CharacterBoardAbilityRow, len(abilities)),
|
||||
PanelById: make(map[int32]EntityMCharacterBoardPanel, len(panels)),
|
||||
PanelsByBoardId: make(map[int32][]EntityMCharacterBoardPanel),
|
||||
ReleaseCostsByGroupId: make(map[int32][]EntityMCharacterBoardPanelReleasePossessionGroup),
|
||||
ReleaseEffectsByGroupId: make(map[int32][]EntityMCharacterBoardPanelReleaseEffectGroup),
|
||||
StatusUpById: make(map[int32]EntityMCharacterBoardStatusUp, len(statusUps)),
|
||||
AbilityById: make(map[int32]EntityMCharacterBoardAbility, len(abilities)),
|
||||
AbilityMaxLevel: make(map[store.CharacterBoardAbilityKey]int32, len(abilityMaxLevels)),
|
||||
EffectTargetsByGroupId: make(map[int32][]CharacterBoardEffectTargetRow),
|
||||
BoardById: make(map[int32]CharacterBoardRow, len(boards)),
|
||||
EffectTargetsByGroupId: make(map[int32][]EntityMCharacterBoardEffectTargetGroup),
|
||||
BoardById: make(map[int32]EntityMCharacterBoard, len(boards)),
|
||||
}
|
||||
|
||||
for _, p := range panels {
|
||||
|
||||
@@ -9,11 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type characterViewerField struct {
|
||||
CharacterViewerFieldId int32 `json:"CharacterViewerFieldId"`
|
||||
ReleaseEvaluateConditionId int32 `json:"ReleaseEvaluateConditionId"`
|
||||
}
|
||||
|
||||
type characterViewerFieldEntry struct {
|
||||
FieldId int32
|
||||
RequiredQuestId int32
|
||||
@@ -39,7 +34,7 @@ func (c *CharacterViewerCatalog) ReleasedFieldIds(user store.UserState) []int32
|
||||
}
|
||||
|
||||
func LoadCharacterViewerCatalog(resolver *ConditionResolver) *CharacterViewerCatalog {
|
||||
fields, err := utils.ReadJSON[characterViewerField]("EntityMCharacterViewerFieldTable.json")
|
||||
fields, err := utils.ReadTable[EntityMCharacterViewerField]("m_character_viewer_field")
|
||||
if err != nil {
|
||||
log.Fatalf("load character viewer field table: %v", err)
|
||||
}
|
||||
|
||||
@@ -6,23 +6,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type companionRow struct {
|
||||
CompanionId int32 `json:"CompanionId"`
|
||||
CompanionCategoryType int32 `json:"CompanionCategoryType"`
|
||||
}
|
||||
|
||||
type companionCategoryRow struct {
|
||||
CompanionCategoryType int32 `json:"CompanionCategoryType"`
|
||||
EnhancementCostNumericalFunctionId int32 `json:"EnhancementCostNumericalFunctionId"`
|
||||
}
|
||||
|
||||
type companionEnhancementMaterialRow struct {
|
||||
CompanionCategoryType int32 `json:"CompanionCategoryType"`
|
||||
Level int32 `json:"Level"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type CompanionLevelKey struct {
|
||||
CategoryType int32
|
||||
Level int32
|
||||
@@ -34,23 +17,23 @@ type CompanionMaterialCost struct {
|
||||
}
|
||||
|
||||
type CompanionCatalog struct {
|
||||
CompanionById map[int32]companionRow
|
||||
CompanionById map[int32]EntityMCompanion
|
||||
GoldCostByCategory map[int32]NumericalFunc
|
||||
MaterialsByKey map[CompanionLevelKey]CompanionMaterialCost
|
||||
}
|
||||
|
||||
func LoadCompanionCatalog() (*CompanionCatalog, error) {
|
||||
companions, err := utils.ReadJSON[companionRow]("EntityMCompanionTable.json")
|
||||
companions, err := utils.ReadTable[EntityMCompanion]("m_companion")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load companion table: %w", err)
|
||||
}
|
||||
|
||||
categories, err := utils.ReadJSON[companionCategoryRow]("EntityMCompanionCategoryTable.json")
|
||||
categories, err := utils.ReadTable[EntityMCompanionCategory]("m_companion_category")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load companion category table: %w", err)
|
||||
}
|
||||
|
||||
materials, err := utils.ReadJSON[companionEnhancementMaterialRow]("EntityMCompanionEnhancementMaterialTable.json")
|
||||
materials, err := utils.ReadTable[EntityMCompanionEnhancementMaterial]("m_companion_enhancement_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load companion enhancement material table: %w", err)
|
||||
}
|
||||
@@ -60,7 +43,7 @@ func LoadCompanionCatalog() (*CompanionCatalog, error) {
|
||||
return nil, fmt.Errorf("load function resolver: %w", err)
|
||||
}
|
||||
|
||||
companionById := make(map[int32]companionRow, len(companions))
|
||||
companionById := make(map[int32]EntityMCompanion, len(companions))
|
||||
for _, c := range companions {
|
||||
companionById[c.CompanionId] = c
|
||||
}
|
||||
|
||||
@@ -7,19 +7,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type evaluateCondition struct {
|
||||
EvaluateConditionId int32 `json:"EvaluateConditionId"`
|
||||
EvaluateConditionFunctionType model.EvaluateConditionFunctionType `json:"EvaluateConditionFunctionType"`
|
||||
EvaluateConditionEvaluateType model.EvaluateConditionEvaluateType `json:"EvaluateConditionEvaluateType"`
|
||||
EvaluateConditionValueGroupId int32 `json:"EvaluateConditionValueGroupId"`
|
||||
}
|
||||
|
||||
type evaluateConditionValueGroup struct {
|
||||
EvaluateConditionValueGroupId int32 `json:"EvaluateConditionValueGroupId"`
|
||||
GroupIndex int32 `json:"GroupIndex"`
|
||||
Value int64 `json:"Value"`
|
||||
}
|
||||
|
||||
const defaultGroupIndex = 1
|
||||
|
||||
type ConditionResolver struct {
|
||||
@@ -27,16 +14,16 @@ type ConditionResolver struct {
|
||||
}
|
||||
|
||||
func LoadConditionResolver() (*ConditionResolver, error) {
|
||||
conditions, err := utils.ReadJSON[evaluateCondition]("EntityMEvaluateConditionTable.json")
|
||||
conditions, err := utils.ReadTable[EntityMEvaluateCondition]("m_evaluate_condition")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load evaluate condition table: %w", err)
|
||||
}
|
||||
valueGroups, err := utils.ReadJSON[evaluateConditionValueGroup]("EntityMEvaluateConditionValueGroupTable.json")
|
||||
valueGroups, err := utils.ReadTable[EntityMEvaluateConditionValueGroup]("m_evaluate_condition_value_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load evaluate condition value group table: %w", err)
|
||||
}
|
||||
|
||||
condById := make(map[int32]evaluateCondition, len(conditions))
|
||||
condById := make(map[int32]EntityMEvaluateCondition, len(conditions))
|
||||
for _, c := range conditions {
|
||||
condById[c.EvaluateConditionId] = c
|
||||
}
|
||||
@@ -52,8 +39,8 @@ func LoadConditionResolver() (*ConditionResolver, error) {
|
||||
|
||||
resolved := make(map[int32]int32)
|
||||
for _, c := range conditions {
|
||||
if c.EvaluateConditionFunctionType == model.EvaluateConditionFunctionTypeQuestClear &&
|
||||
c.EvaluateConditionEvaluateType == model.EvaluateConditionEvaluateTypeIdContain {
|
||||
if model.EvaluateConditionFunctionType(c.EvaluateConditionFunctionType) == model.EvaluateConditionFunctionTypeQuestClear &&
|
||||
model.EvaluateConditionEvaluateType(c.EvaluateConditionEvaluateType) == model.EvaluateConditionEvaluateTypeIdContain {
|
||||
if questId, ok := vgByKey[vgKey{c.EvaluateConditionValueGroupId, defaultGroupIndex}]; ok {
|
||||
resolved[c.EvaluateConditionId] = int32(questId)
|
||||
}
|
||||
|
||||
@@ -7,11 +7,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type configRow struct {
|
||||
ConfigKey string `json:"ConfigKey"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
type GameConfig struct {
|
||||
ConsumableItemIdForGold int32
|
||||
ConsumableItemIdForMedal int32
|
||||
@@ -41,7 +36,7 @@ type GameConfig struct {
|
||||
}
|
||||
|
||||
func LoadGameConfig() (*GameConfig, error) {
|
||||
rows, err := utils.ReadJSON[configRow]("EntityMConfigTable.json")
|
||||
rows, err := utils.ReadTable[EntityMConfig]("m_config")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load config table: %w", err)
|
||||
}
|
||||
|
||||
@@ -6,23 +6,18 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type ConsumableItemRow struct {
|
||||
ConsumableItemId int32 `json:"ConsumableItemId"`
|
||||
SellPrice int32 `json:"SellPrice"`
|
||||
}
|
||||
|
||||
type ConsumableItemCatalog struct {
|
||||
All map[int32]ConsumableItemRow
|
||||
All map[int32]EntityMConsumableItem
|
||||
}
|
||||
|
||||
func LoadConsumableItemCatalog() (*ConsumableItemCatalog, error) {
|
||||
rows, err := utils.ReadJSON[ConsumableItemRow]("EntityMConsumableItemTable.json")
|
||||
rows, err := utils.ReadTable[EntityMConsumableItem]("m_consumable_item")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load consumable item table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &ConsumableItemCatalog{
|
||||
All: make(map[int32]ConsumableItemRow, len(rows)),
|
||||
All: make(map[int32]EntityMConsumableItem, len(rows)),
|
||||
}
|
||||
for _, row := range rows {
|
||||
catalog.All[row.ConsumableItemId] = row
|
||||
|
||||
@@ -8,132 +8,37 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type CostumeMasterRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
SkillfulWeaponType int32 `json:"SkillfulWeaponType"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
CostumeLimitBreakMaterialGroupId int32 `json:"CostumeLimitBreakMaterialGroupId"`
|
||||
CostumeActiveSkillGroupId int32 `json:"CostumeActiveSkillGroupId"`
|
||||
}
|
||||
|
||||
type costumeRarityRow struct {
|
||||
RarityType int32 `json:"RarityType"`
|
||||
CostumeLimitBreakMaterialRarityGroupId int32 `json:"CostumeLimitBreakMaterialRarityGroupId"`
|
||||
RequiredExpForLevelUpNumericalParameterMapId int32 `json:"RequiredExpForLevelUpNumericalParameterMapId"`
|
||||
EnhancementCostByMaterialNumericalFunctionId int32 `json:"EnhancementCostByMaterialNumericalFunctionId"`
|
||||
LimitBreakCostNumericalFunctionId int32 `json:"LimitBreakCostNumericalFunctionId"`
|
||||
MaxLevelNumericalFunctionId int32 `json:"MaxLevelNumericalFunctionId"`
|
||||
ActiveSkillMaxLevelNumericalFunctionId int32 `json:"ActiveSkillMaxLevelNumericalFunctionId"`
|
||||
ActiveSkillEnhancementCostNumericalFunctionId int32 `json:"ActiveSkillEnhancementCostNumericalFunctionId"`
|
||||
}
|
||||
|
||||
type CostumeAwakenRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
CostumeAwakenEffectGroupId int32 `json:"CostumeAwakenEffectGroupId"`
|
||||
CostumeAwakenStepMaterialGroupId int32 `json:"CostumeAwakenStepMaterialGroupId"`
|
||||
CostumeAwakenPriceGroupId int32 `json:"CostumeAwakenPriceGroupId"`
|
||||
}
|
||||
|
||||
type costumeAwakenPriceRow struct {
|
||||
CostumeAwakenPriceGroupId int32 `json:"CostumeAwakenPriceGroupId"`
|
||||
AwakenStepLowerLimit int32 `json:"AwakenStepLowerLimit"`
|
||||
Gold int32 `json:"Gold"`
|
||||
}
|
||||
|
||||
type CostumeAwakenEffectRow struct {
|
||||
CostumeAwakenEffectGroupId int32 `json:"CostumeAwakenEffectGroupId"`
|
||||
AwakenStep int32 `json:"AwakenStep"`
|
||||
CostumeAwakenEffectType int32 `json:"CostumeAwakenEffectType"`
|
||||
CostumeAwakenEffectId int32 `json:"CostumeAwakenEffectId"`
|
||||
}
|
||||
|
||||
type CostumeAwakenStatusUpRow struct {
|
||||
CostumeAwakenStatusUpGroupId int32 `json:"CostumeAwakenStatusUpGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
StatusKindType int32 `json:"StatusKindType"`
|
||||
StatusCalculationType int32 `json:"StatusCalculationType"`
|
||||
EffectValue int32 `json:"EffectValue"`
|
||||
}
|
||||
|
||||
type CostumeAwakenItemAcquireRow struct {
|
||||
CostumeAwakenItemAcquireId int32 `json:"CostumeAwakenItemAcquireId"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type CostumeActiveSkillGroupRow struct {
|
||||
CostumeActiveSkillGroupId int32 `json:"CostumeActiveSkillGroupId"`
|
||||
CostumeLimitBreakCountLowerLimit int32 `json:"CostumeLimitBreakCountLowerLimit"`
|
||||
CostumeActiveSkillId int32 `json:"CostumeActiveSkillId"`
|
||||
CostumeActiveSkillEnhancementMaterialId int32 `json:"CostumeActiveSkillEnhancementMaterialId"`
|
||||
}
|
||||
|
||||
type CostumeActiveSkillEnhanceMaterialRow struct {
|
||||
CostumeActiveSkillEnhancementMaterialId int32 `json:"CostumeActiveSkillEnhancementMaterialId"`
|
||||
SkillLevel int32 `json:"SkillLevel"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type CostumeLotteryEffectRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
SlotNumber int32 `json:"SlotNumber"`
|
||||
CostumeLotteryEffectOddsGroupId int32 `json:"CostumeLotteryEffectOddsGroupId"`
|
||||
CostumeLotteryEffectUnlockMaterialGroupId int32 `json:"CostumeLotteryEffectUnlockMaterialGroupId"`
|
||||
CostumeLotteryEffectDrawMaterialGroupId int32 `json:"CostumeLotteryEffectDrawMaterialGroupId"`
|
||||
CostumeLotteryEffectReleaseScheduleId int32 `json:"CostumeLotteryEffectReleaseScheduleId"`
|
||||
}
|
||||
|
||||
type CostumeLotteryEffectMaterialGroupRow struct {
|
||||
CostumeLotteryEffectMaterialGroupId int32 `json:"CostumeLotteryEffectMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type CostumeLotteryEffectOddsRow struct {
|
||||
CostumeLotteryEffectOddsGroupId int32 `json:"CostumeLotteryEffectOddsGroupId"`
|
||||
OddsNumber int32 `json:"OddsNumber"`
|
||||
Weight int32 `json:"Weight"`
|
||||
CostumeLotteryEffectType int32 `json:"CostumeLotteryEffectType"`
|
||||
CostumeLotteryEffectTargetId int32 `json:"CostumeLotteryEffectTargetId"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
}
|
||||
|
||||
type CostumeCatalog struct {
|
||||
Costumes map[int32]CostumeMasterRow
|
||||
Materials map[int32]MaterialRow
|
||||
Costumes map[int32]EntityMCostume
|
||||
Materials map[int32]EntityMMaterial
|
||||
ExpByRarity map[int32][]int32
|
||||
EnhanceCostByRarity map[int32]NumericalFunc
|
||||
MaxLevelByRarity map[int32]NumericalFunc
|
||||
LimitBreakCostByRarity map[int32]NumericalFunc
|
||||
|
||||
AwakenByCostumeId map[int32]CostumeAwakenRow
|
||||
AwakenByCostumeId map[int32]EntityMCostumeAwaken
|
||||
AwakenPriceByGroup map[int32]int32
|
||||
AwakenEffectsByGroupAndStep map[int32]map[int32]CostumeAwakenEffectRow
|
||||
AwakenStatusUpByGroup map[int32][]CostumeAwakenStatusUpRow
|
||||
AwakenItemAcquireById map[int32]CostumeAwakenItemAcquireRow
|
||||
AwakenEffectsByGroupAndStep map[int32]map[int32]EntityMCostumeAwakenEffectGroup
|
||||
AwakenStatusUpByGroup map[int32][]EntityMCostumeAwakenStatusUpGroup
|
||||
AwakenItemAcquireById map[int32]EntityMCostumeAwakenItemAcquire
|
||||
|
||||
ActiveSkillGroupsByGroupId map[int32][]CostumeActiveSkillGroupRow // sorted by CostumeLimitBreakCountLowerLimit desc
|
||||
ActiveSkillEnhanceMats map[[2]int32][]CostumeActiveSkillEnhanceMaterialRow // key: [enhancementMaterialId, skillLevel]
|
||||
ActiveSkillGroupsByGroupId map[int32][]EntityMCostumeActiveSkillGroup // sorted by CostumeLimitBreakCountLowerLimit desc
|
||||
ActiveSkillEnhanceMats map[[2]int32][]EntityMCostumeActiveSkillEnhancementMaterial // key: [enhancementMaterialId, skillLevel]
|
||||
ActiveSkillMaxLevelByRarity map[int32]NumericalFunc
|
||||
ActiveSkillCostByRarity map[int32]NumericalFunc
|
||||
|
||||
LotteryEffects map[[2]int32]CostumeLotteryEffectRow // key: [costumeId, slotNumber]
|
||||
LotteryEffectMats map[int32][]CostumeLotteryEffectMaterialGroupRow // key: materialGroupId (both unlock and draw)
|
||||
LotteryEffectOdds map[int32][]CostumeLotteryEffectOddsRow // key: oddsGroupId
|
||||
LotteryEffects map[[2]int32]EntityMCostumeLotteryEffect // key: [costumeId, slotNumber]
|
||||
LotteryEffectMats map[int32][]EntityMCostumeLotteryEffectMaterialGroup // key: materialGroupId (both unlock and draw)
|
||||
LotteryEffectOdds map[int32][]EntityMCostumeLotteryEffectOddsGroup // key: oddsGroupId
|
||||
}
|
||||
|
||||
func LoadCostumeCatalog(matCatalog *MaterialCatalog) (*CostumeCatalog, error) {
|
||||
costumes, err := utils.ReadJSON[CostumeMasterRow]("EntityMCostumeTable.json")
|
||||
costumes, err := utils.ReadTable[EntityMCostume]("m_costume")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume table: %w", err)
|
||||
}
|
||||
|
||||
rarities, err := utils.ReadJSON[costumeRarityRow]("EntityMCostumeRarityTable.json")
|
||||
rarities, err := utils.ReadTable[EntityMCostumeRarity]("m_costume_rarity")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume rarity table: %w", err)
|
||||
}
|
||||
@@ -148,71 +53,71 @@ func LoadCostumeCatalog(matCatalog *MaterialCatalog) (*CostumeCatalog, error) {
|
||||
return nil, fmt.Errorf("load function resolver: %w", err)
|
||||
}
|
||||
|
||||
awakenRows, err := utils.ReadJSON[CostumeAwakenRow]("EntityMCostumeAwakenTable.json")
|
||||
awakenRows, err := utils.ReadTable[EntityMCostumeAwaken]("m_costume_awaken")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume awaken table: %w", err)
|
||||
}
|
||||
awakenPriceRows, err := utils.ReadJSON[costumeAwakenPriceRow]("EntityMCostumeAwakenPriceGroupTable.json")
|
||||
awakenPriceRows, err := utils.ReadTable[EntityMCostumeAwakenPriceGroup]("m_costume_awaken_price_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume awaken price table: %w", err)
|
||||
}
|
||||
awakenEffectRows, err := utils.ReadJSON[CostumeAwakenEffectRow]("EntityMCostumeAwakenEffectGroupTable.json")
|
||||
awakenEffectRows, err := utils.ReadTable[EntityMCostumeAwakenEffectGroup]("m_costume_awaken_effect_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume awaken effect table: %w", err)
|
||||
}
|
||||
awakenStatusUpRows, err := utils.ReadJSON[CostumeAwakenStatusUpRow]("EntityMCostumeAwakenStatusUpGroupTable.json")
|
||||
awakenStatusUpRows, err := utils.ReadTable[EntityMCostumeAwakenStatusUpGroup]("m_costume_awaken_status_up_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume awaken status up table: %w", err)
|
||||
}
|
||||
awakenItemAcquireRows, err := utils.ReadJSON[CostumeAwakenItemAcquireRow]("EntityMCostumeAwakenItemAcquireTable.json")
|
||||
awakenItemAcquireRows, err := utils.ReadTable[EntityMCostumeAwakenItemAcquire]("m_costume_awaken_item_acquire")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume awaken item acquire table: %w", err)
|
||||
}
|
||||
|
||||
activeSkillGroupRows, err := utils.ReadJSON[CostumeActiveSkillGroupRow]("EntityMCostumeActiveSkillGroupTable.json")
|
||||
activeSkillGroupRows, err := utils.ReadTable[EntityMCostumeActiveSkillGroup]("m_costume_active_skill_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume active skill group table: %w", err)
|
||||
}
|
||||
activeSkillMatRows, err := utils.ReadJSON[CostumeActiveSkillEnhanceMaterialRow]("EntityMCostumeActiveSkillEnhancementMaterialTable.json")
|
||||
activeSkillMatRows, err := utils.ReadTable[EntityMCostumeActiveSkillEnhancementMaterial]("m_costume_active_skill_enhancement_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume active skill enhancement material table: %w", err)
|
||||
}
|
||||
|
||||
lotteryEffectRows, err := utils.ReadJSON[CostumeLotteryEffectRow]("EntityMCostumeLotteryEffectTable.json")
|
||||
lotteryEffectRows, err := utils.ReadTable[EntityMCostumeLotteryEffect]("m_costume_lottery_effect")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume lottery effect table: %w", err)
|
||||
}
|
||||
lotteryEffectMatRows, err := utils.ReadJSON[CostumeLotteryEffectMaterialGroupRow]("EntityMCostumeLotteryEffectMaterialGroupTable.json")
|
||||
lotteryEffectMatRows, err := utils.ReadTable[EntityMCostumeLotteryEffectMaterialGroup]("m_costume_lottery_effect_material_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume lottery effect material group table: %w", err)
|
||||
}
|
||||
lotteryEffectOddsRows, err := utils.ReadJSON[CostumeLotteryEffectOddsRow]("EntityMCostumeLotteryEffectOddsGroupTable.json")
|
||||
lotteryEffectOddsRows, err := utils.ReadTable[EntityMCostumeLotteryEffectOddsGroup]("m_costume_lottery_effect_odds_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume lottery effect odds group table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &CostumeCatalog{
|
||||
Costumes: make(map[int32]CostumeMasterRow, len(costumes)),
|
||||
Costumes: make(map[int32]EntityMCostume, len(costumes)),
|
||||
Materials: matCatalog.ByType[model.MaterialTypeCostumeEnhancement],
|
||||
ExpByRarity: make(map[int32][]int32, len(rarities)),
|
||||
EnhanceCostByRarity: make(map[int32]NumericalFunc, len(rarities)),
|
||||
MaxLevelByRarity: make(map[int32]NumericalFunc, len(rarities)),
|
||||
LimitBreakCostByRarity: make(map[int32]NumericalFunc, len(rarities)),
|
||||
|
||||
AwakenByCostumeId: make(map[int32]CostumeAwakenRow, len(awakenRows)),
|
||||
AwakenByCostumeId: make(map[int32]EntityMCostumeAwaken, len(awakenRows)),
|
||||
AwakenPriceByGroup: make(map[int32]int32, len(awakenPriceRows)),
|
||||
AwakenEffectsByGroupAndStep: make(map[int32]map[int32]CostumeAwakenEffectRow),
|
||||
AwakenStatusUpByGroup: make(map[int32][]CostumeAwakenStatusUpRow),
|
||||
AwakenItemAcquireById: make(map[int32]CostumeAwakenItemAcquireRow, len(awakenItemAcquireRows)),
|
||||
AwakenEffectsByGroupAndStep: make(map[int32]map[int32]EntityMCostumeAwakenEffectGroup),
|
||||
AwakenStatusUpByGroup: make(map[int32][]EntityMCostumeAwakenStatusUpGroup),
|
||||
AwakenItemAcquireById: make(map[int32]EntityMCostumeAwakenItemAcquire, len(awakenItemAcquireRows)),
|
||||
|
||||
ActiveSkillGroupsByGroupId: make(map[int32][]CostumeActiveSkillGroupRow),
|
||||
ActiveSkillEnhanceMats: make(map[[2]int32][]CostumeActiveSkillEnhanceMaterialRow),
|
||||
ActiveSkillGroupsByGroupId: make(map[int32][]EntityMCostumeActiveSkillGroup),
|
||||
ActiveSkillEnhanceMats: make(map[[2]int32][]EntityMCostumeActiveSkillEnhancementMaterial),
|
||||
ActiveSkillMaxLevelByRarity: make(map[int32]NumericalFunc, len(rarities)),
|
||||
ActiveSkillCostByRarity: make(map[int32]NumericalFunc, len(rarities)),
|
||||
|
||||
LotteryEffects: make(map[[2]int32]CostumeLotteryEffectRow, len(lotteryEffectRows)),
|
||||
LotteryEffectMats: make(map[int32][]CostumeLotteryEffectMaterialGroupRow),
|
||||
LotteryEffectOdds: make(map[int32][]CostumeLotteryEffectOddsRow),
|
||||
LotteryEffects: make(map[[2]int32]EntityMCostumeLotteryEffect, len(lotteryEffectRows)),
|
||||
LotteryEffectMats: make(map[int32][]EntityMCostumeLotteryEffectMaterialGroup),
|
||||
LotteryEffectOdds: make(map[int32][]EntityMCostumeLotteryEffectOddsGroup),
|
||||
}
|
||||
|
||||
for _, row := range costumes {
|
||||
@@ -259,7 +164,7 @@ func LoadCostumeCatalog(matCatalog *MaterialCatalog) (*CostumeCatalog, error) {
|
||||
for _, row := range awakenEffectRows {
|
||||
m, ok := catalog.AwakenEffectsByGroupAndStep[row.CostumeAwakenEffectGroupId]
|
||||
if !ok {
|
||||
m = make(map[int32]CostumeAwakenEffectRow)
|
||||
m = make(map[int32]EntityMCostumeAwakenEffectGroup)
|
||||
catalog.AwakenEffectsByGroupAndStep[row.CostumeAwakenEffectGroupId] = m
|
||||
}
|
||||
m[row.AwakenStep] = row
|
||||
|
||||
@@ -5,17 +5,10 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type costumeDupRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
func LoadDupExchange() (map[int32][]model.DupExchangeEntry, error) {
|
||||
result := make(map[int32][]model.DupExchangeEntry)
|
||||
|
||||
costumeRows, err := utils.ReadJSON[costumeDupRow]("EntityMCostumeDuplicationExchangePossessionGroupTable.json")
|
||||
costumeRows, err := utils.ReadTable[EntityMCostumeDuplicationExchangePossessionGroup]("m_costume_duplication_exchange_possession_group")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -30,20 +23,10 @@ func LoadDupExchange() (map[int32][]model.DupExchangeEntry, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type lbMaterialRow struct {
|
||||
CostumeLimitBreakMaterialGroupId int32 `json:"CostumeLimitBreakMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
}
|
||||
|
||||
type costumeLBRef struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
CostumeLimitBreakMaterialGroupId int32 `json:"CostumeLimitBreakMaterialGroupId"`
|
||||
}
|
||||
|
||||
const dupExchangeFallbackCount int32 = 10
|
||||
|
||||
func EnrichDupExchange(dupMap map[int32][]model.DupExchangeEntry, pool *GachaCatalog) (int, error) {
|
||||
lbRows, err := utils.ReadJSON[lbMaterialRow]("EntityMCostumeLimitBreakMaterialGroupTable.json")
|
||||
lbRows, err := utils.ReadTable[EntityMCostumeLimitBreakMaterialGroup]("m_costume_limit_break_material_group")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -52,7 +35,7 @@ func EnrichDupExchange(dupMap map[int32][]model.DupExchangeEntry, pool *GachaCat
|
||||
groupToMaterial[r.CostumeLimitBreakMaterialGroupId] = r.MaterialId
|
||||
}
|
||||
|
||||
costumeRows, err := utils.ReadJSON[costumeLBRef]("EntityMCostumeTable.json")
|
||||
costumeRows, err := utils.ReadTable[EntityMCostume]("m_costume")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,48 +7,31 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type ExploreRow struct {
|
||||
ExploreId int32 `json:"ExploreId"`
|
||||
ConsumeItemCount int32 `json:"ConsumeItemCount"`
|
||||
RewardLotteryCount int32 `json:"RewardLotteryCount"`
|
||||
}
|
||||
|
||||
type ExploreGradeScoreRow struct {
|
||||
ExploreId int32 `json:"ExploreId"`
|
||||
NecessaryScore int32 `json:"NecessaryScore"`
|
||||
ExploreGradeId int32 `json:"ExploreGradeId"`
|
||||
}
|
||||
|
||||
type ExploreGradeAssetRow struct {
|
||||
ExploreGradeId int32 `json:"ExploreGradeId"`
|
||||
AssetGradeIconId int32 `json:"AssetGradeIconId"`
|
||||
}
|
||||
|
||||
type ExploreCatalog struct {
|
||||
Explores map[int32]ExploreRow
|
||||
GradeScores map[int32][]ExploreGradeScoreRow // keyed by ExploreId, sorted desc by NecessaryScore
|
||||
GradeAssets map[int32]int32 // gradeId -> assetGradeIconId
|
||||
Explores map[int32]EntityMExplore
|
||||
GradeScores map[int32][]EntityMExploreGradeScore // keyed by ExploreId, sorted desc by NecessaryScore
|
||||
GradeAssets map[int32]int32 // gradeId -> assetGradeIconId
|
||||
}
|
||||
|
||||
func LoadExploreCatalog() (*ExploreCatalog, error) {
|
||||
explores, err := utils.ReadJSON[ExploreRow]("EntityMExploreTable.json")
|
||||
explores, err := utils.ReadTable[EntityMExplore]("m_explore")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load explore table: %w", err)
|
||||
}
|
||||
|
||||
gradeScores, err := utils.ReadJSON[ExploreGradeScoreRow]("EntityMExploreGradeScoreTable.json")
|
||||
gradeScores, err := utils.ReadTable[EntityMExploreGradeScore]("m_explore_grade_score")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load explore grade score table: %w", err)
|
||||
}
|
||||
|
||||
gradeAssets, err := utils.ReadJSON[ExploreGradeAssetRow]("EntityMExploreGradeAssetTable.json")
|
||||
gradeAssets, err := utils.ReadTable[EntityMExploreGradeAsset]("m_explore_grade_asset")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load explore grade asset table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &ExploreCatalog{
|
||||
Explores: make(map[int32]ExploreRow, len(explores)),
|
||||
GradeScores: make(map[int32][]ExploreGradeScoreRow),
|
||||
Explores: make(map[int32]EntityMExplore, len(explores)),
|
||||
GradeScores: make(map[int32][]EntityMExploreGradeScore),
|
||||
GradeAssets: make(map[int32]int32, len(gradeAssets)),
|
||||
}
|
||||
|
||||
|
||||
@@ -10,24 +10,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type gachaMedalRow struct {
|
||||
GachaMedalId int32 `json:"GachaMedalId"`
|
||||
ShopTransitionGachaId int32 `json:"ShopTransitionGachaId"`
|
||||
ConsumableItemId int32 `json:"ConsumableItemId"`
|
||||
AutoConvertDatetime int64 `json:"AutoConvertDatetime"`
|
||||
ConversionRate int32 `json:"ConversionRate"`
|
||||
}
|
||||
|
||||
type momBannerRow struct {
|
||||
MomBannerId int32 `json:"MomBannerId"`
|
||||
SortOrderDesc int32 `json:"SortOrderDesc"`
|
||||
DestinationDomainType int32 `json:"DestinationDomainType"`
|
||||
DestinationDomainId int32 `json:"DestinationDomainId"`
|
||||
BannerAssetName string `json:"BannerAssetName"`
|
||||
StartDatetime int64 `json:"StartDatetime"`
|
||||
EndDatetime int64 `json:"EndDatetime"`
|
||||
}
|
||||
|
||||
type GachaMedalInfo struct {
|
||||
GachaMedalId int32
|
||||
ConsumableItemId int32
|
||||
@@ -38,16 +20,16 @@ type GachaMedalInfo struct {
|
||||
const chapterGachaIdBase int32 = 200000
|
||||
|
||||
func LoadGachaCatalog() ([]store.GachaCatalogEntry, map[int32]GachaMedalInfo, error) {
|
||||
medals, err := utils.ReadJSON[gachaMedalRow]("EntityMGachaMedalTable.json")
|
||||
medals, err := utils.ReadTable[EntityMGachaMedal]("m_gacha_medal")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("load gacha medal table: %w", err)
|
||||
}
|
||||
banners, err := utils.ReadJSON[momBannerRow]("EntityMMomBannerTable.json")
|
||||
banners, err := utils.ReadTable[EntityMMomBanner]("m_mom_banner")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("load mom banner table: %w", err)
|
||||
}
|
||||
|
||||
gachaToMedal := make(map[int32]gachaMedalRow)
|
||||
gachaToMedal := make(map[int32]EntityMGachaMedal)
|
||||
medalInfoByGacha := make(map[int32]GachaMedalInfo)
|
||||
for _, m := range medals {
|
||||
gachaToMedal[m.ShopTransitionGachaId] = m
|
||||
@@ -59,7 +41,7 @@ func LoadGachaCatalog() ([]store.GachaCatalogEntry, map[int32]GachaMedalInfo, er
|
||||
}
|
||||
}
|
||||
|
||||
stepupSteps := make(map[int32][]momBannerRow)
|
||||
stepupSteps := make(map[int32][]EntityMMomBanner)
|
||||
var entries []store.GachaCatalogEntry
|
||||
|
||||
for _, b := range banners {
|
||||
|
||||
@@ -46,58 +46,28 @@ type GachaCatalog struct {
|
||||
ShopFeaturedByMedal map[int32][]ShopFeaturedEntry // consumableId -> paired entries
|
||||
}
|
||||
|
||||
type costumePoolRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
SkillfulWeaponType int32 `json:"SkillfulWeaponType"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
}
|
||||
|
||||
type weaponPoolRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
WeaponType int32 `json:"WeaponType"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
IsRestrictDiscard bool `json:"IsRestrictDiscard"`
|
||||
}
|
||||
|
||||
type catalogCostumeRow struct {
|
||||
CostumeId int32 `json:"CostumeId"`
|
||||
CatalogTermId int32 `json:"CatalogTermId"`
|
||||
}
|
||||
|
||||
type catalogWeaponRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
CatalogTermId int32 `json:"CatalogTermId"`
|
||||
}
|
||||
|
||||
type materialPoolRow struct {
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
MaterialType int32 `json:"MaterialType"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
}
|
||||
|
||||
func LoadGachaPool() (*GachaCatalog, error) {
|
||||
costumes, err := utils.ReadJSON[costumePoolRow]("EntityMCostumeTable.json")
|
||||
costumes, err := utils.ReadTable[EntityMCostume]("m_costume")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume table: %w", err)
|
||||
}
|
||||
weapons, err := utils.ReadJSON[weaponPoolRow]("EntityMWeaponTable.json")
|
||||
weapons, err := utils.ReadTable[EntityMWeapon]("m_weapon")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon table: %w", err)
|
||||
}
|
||||
catalogCostumes, err := utils.ReadJSON[catalogCostumeRow]("EntityMCatalogCostumeTable.json")
|
||||
catalogCostumes, err := utils.ReadTable[EntityMCatalogCostume]("m_catalog_costume")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load catalog costume table: %w", err)
|
||||
}
|
||||
catalogWeapons, err := utils.ReadJSON[catalogWeaponRow]("EntityMCatalogWeaponTable.json")
|
||||
catalogWeapons, err := utils.ReadTable[EntityMCatalogWeapon]("m_catalog_weapon")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load catalog weapon table: %w", err)
|
||||
}
|
||||
materials, err := utils.ReadJSON[materialPoolRow]("EntityMMaterialTable.json")
|
||||
materials, err := utils.ReadTable[EntityMMaterial]("m_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load material table: %w", err)
|
||||
}
|
||||
evoGroupRows, err := utils.ReadJSON[WeaponEvolutionGroupRow]("EntityMWeaponEvolutionGroupTable.json")
|
||||
evoGroupRows, err := utils.ReadTable[EntityMWeaponEvolutionGroup]("m_weapon_evolution_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon evolution group table: %w", err)
|
||||
}
|
||||
@@ -414,8 +384,8 @@ func (pool *GachaCatalog) BuildBannerPools(entries []store.GachaCatalogEntry) {
|
||||
len(pool.BannerPools), len(allFeaturedCostumes), len(allFeaturedWeapons))
|
||||
}
|
||||
|
||||
func buildEvolvedWeaponSet(rows []WeaponEvolutionGroupRow) map[int32]bool {
|
||||
grouped := make(map[int32][]WeaponEvolutionGroupRow)
|
||||
func buildEvolvedWeaponSet(rows []EntityMWeaponEvolutionGroup) map[int32]bool {
|
||||
grouped := make(map[int32][]EntityMWeaponEvolutionGroup)
|
||||
for _, r := range rows {
|
||||
grouped[r.WeaponEvolutionGroupId] = append(grouped[r.WeaponEvolutionGroupId], r)
|
||||
}
|
||||
|
||||
@@ -9,14 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type gimmickScheduleRow struct {
|
||||
GimmickSequenceScheduleId int32 `json:"GimmickSequenceScheduleId"`
|
||||
StartDatetime int64 `json:"StartDatetime"`
|
||||
EndDatetime int64 `json:"EndDatetime"`
|
||||
FirstGimmickSequenceId int32 `json:"FirstGimmickSequenceId"`
|
||||
ReleaseEvaluateConditionId int32 `json:"ReleaseEvaluateConditionId"`
|
||||
}
|
||||
|
||||
type gimmickScheduleEntry struct {
|
||||
ScheduleId int32
|
||||
StartDatetime int64
|
||||
@@ -30,7 +22,7 @@ type GimmickCatalog struct {
|
||||
}
|
||||
|
||||
func LoadGimmickCatalog(resolver *ConditionResolver) (*GimmickCatalog, error) {
|
||||
rows, err := utils.ReadJSON[gimmickScheduleRow]("EntityMGimmickSequenceScheduleTable.json")
|
||||
rows, err := utils.ReadTable[EntityMGimmickSequenceSchedule]("m_gimmick_sequence_schedule")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load gimmick sequence schedule table: %w", err)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type loginBonusStamp struct {
|
||||
LoginBonusId int32 `json:"LoginBonusId"`
|
||||
LowerPageNumber int32 `json:"LowerPageNumber"`
|
||||
StampNumber int32 `json:"StampNumber"`
|
||||
RewardPossessionType int32 `json:"RewardPossessionType"`
|
||||
RewardPossessionId int32 `json:"RewardPossessionId"`
|
||||
RewardCount int32 `json:"RewardCount"`
|
||||
}
|
||||
|
||||
type loginBonusStampKey struct {
|
||||
LoginBonusId int32
|
||||
LowerPageNumber int32
|
||||
@@ -36,7 +27,7 @@ func (c *LoginBonusCatalog) LookupStampReward(loginBonusId, pageNumber, stampNum
|
||||
}
|
||||
|
||||
func LoadLoginBonusCatalog() *LoginBonusCatalog {
|
||||
stamps, err := utils.ReadJSON[loginBonusStamp]("EntityMLoginBonusStampTable.json")
|
||||
stamps, err := utils.ReadTable[EntityMLoginBonusStamp]("m_login_bonus_stamp")
|
||||
if err != nil {
|
||||
log.Fatalf("load login bonus stamp table: %v", err)
|
||||
}
|
||||
|
||||
@@ -7,29 +7,15 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type MaterialRow struct {
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
MaterialType model.MaterialType `json:"MaterialType"`
|
||||
WeaponType int32 `json:"WeaponType"`
|
||||
EffectValue int32 `json:"EffectValue"`
|
||||
SellPrice int32 `json:"SellPrice"`
|
||||
}
|
||||
|
||||
type numericalParameterMapRow struct {
|
||||
NumericalParameterMapId int32 `json:"NumericalParameterMapId"`
|
||||
ParameterKey int32 `json:"ParameterKey"`
|
||||
ParameterValue int32 `json:"ParameterValue"`
|
||||
}
|
||||
|
||||
func LoadParameterMap() ([]numericalParameterMapRow, error) {
|
||||
rows, err := utils.ReadJSON[numericalParameterMapRow]("EntityMNumericalParameterMapTable.json")
|
||||
func LoadParameterMap() ([]EntityMNumericalParameterMap, error) {
|
||||
rows, err := utils.ReadTable[EntityMNumericalParameterMap]("m_numerical_parameter_map")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load numerical parameter map table: %w", err)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func BuildExpThresholds(paramMapRows []numericalParameterMapRow, mapId int32) []int32 {
|
||||
func BuildExpThresholds(paramMapRows []EntityMNumericalParameterMap, mapId int32) []int32 {
|
||||
maxKey := int32(0)
|
||||
for _, r := range paramMapRows {
|
||||
if r.NumericalParameterMapId == mapId && r.ParameterKey > maxKey {
|
||||
@@ -46,26 +32,27 @@ func BuildExpThresholds(paramMapRows []numericalParameterMapRow, mapId int32) []
|
||||
}
|
||||
|
||||
type MaterialCatalog struct {
|
||||
All map[int32]MaterialRow
|
||||
ByType map[model.MaterialType]map[int32]MaterialRow
|
||||
All map[int32]EntityMMaterial
|
||||
ByType map[model.MaterialType]map[int32]EntityMMaterial
|
||||
}
|
||||
|
||||
func LoadMaterialCatalog() (*MaterialCatalog, error) {
|
||||
rows, err := utils.ReadJSON[MaterialRow]("EntityMMaterialTable.json")
|
||||
rows, err := utils.ReadTable[EntityMMaterial]("m_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load material table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &MaterialCatalog{
|
||||
All: make(map[int32]MaterialRow, len(rows)),
|
||||
ByType: make(map[model.MaterialType]map[int32]MaterialRow),
|
||||
All: make(map[int32]EntityMMaterial, len(rows)),
|
||||
ByType: make(map[model.MaterialType]map[int32]EntityMMaterial),
|
||||
}
|
||||
for _, row := range rows {
|
||||
catalog.All[row.MaterialId] = row
|
||||
if catalog.ByType[row.MaterialType] == nil {
|
||||
catalog.ByType[row.MaterialType] = make(map[int32]MaterialRow)
|
||||
mt := model.MaterialType(row.MaterialType)
|
||||
if catalog.ByType[mt] == nil {
|
||||
catalog.ByType[mt] = make(map[int32]EntityMMaterial)
|
||||
}
|
||||
catalog.ByType[row.MaterialType][row.MaterialId] = row
|
||||
catalog.ByType[mt][row.MaterialId] = row
|
||||
}
|
||||
return catalog, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
package memorydb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pierrec/lz4/v4"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
var tables map[string][]byte
|
||||
|
||||
const (
|
||||
aesKeyHex = "36436230313332314545356536624265"
|
||||
aesIVHex = "45666341656634434165356536446141"
|
||||
lz4ExtCode = int8(99)
|
||||
)
|
||||
|
||||
func Init(binPath string) error {
|
||||
encrypted, err := os.ReadFile(binPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read bin.e: %w", err)
|
||||
}
|
||||
|
||||
decrypted, err := decrypt(encrypted)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decrypt: %w", err)
|
||||
}
|
||||
|
||||
toc, dataBlob, err := parseHeader(decrypted)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse header: %w", err)
|
||||
}
|
||||
|
||||
tables = make(map[string][]byte, len(toc))
|
||||
for name, offLen := range toc {
|
||||
off := offLen[0]
|
||||
length := offLen[1]
|
||||
if off+length > len(dataBlob) {
|
||||
return fmt.Errorf("table %q: offset %d + length %d exceeds data blob size %d", name, off, length, len(dataBlob))
|
||||
}
|
||||
tables[name] = dataBlob[off : off+length]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TableCount() int {
|
||||
return len(tables)
|
||||
}
|
||||
|
||||
func TableBytes(key string) ([]byte, bool) {
|
||||
b, ok := tables[key]
|
||||
return b, ok
|
||||
}
|
||||
|
||||
func ReadTable[T any](key string) ([]T, error) {
|
||||
raw, ok := TableBytes(key)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("table %q not found in master data", key)
|
||||
}
|
||||
return decompressAndUnmarshal[T](raw)
|
||||
}
|
||||
|
||||
func decrypt(data []byte) ([]byte, error) {
|
||||
key, err := hex.DecodeString(aesKeyHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode key: %w", err)
|
||||
}
|
||||
iv, err := hex.DecodeString(aesIVHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode iv: %w", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new cipher: %w", err)
|
||||
}
|
||||
|
||||
if len(data)%aes.BlockSize != 0 {
|
||||
return nil, fmt.Errorf("ciphertext length %d is not a multiple of block size %d", len(data), aes.BlockSize)
|
||||
}
|
||||
|
||||
decrypted := make([]byte, len(data))
|
||||
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||
cbc.CryptBlocks(decrypted, data)
|
||||
|
||||
decrypted, err = pkcs7Unpad(decrypted, aes.BlockSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unpad: %w", err)
|
||||
}
|
||||
return decrypted, nil
|
||||
}
|
||||
|
||||
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("empty data")
|
||||
}
|
||||
padLen := int(data[len(data)-1])
|
||||
if padLen == 0 || padLen > blockSize || padLen > len(data) {
|
||||
return nil, fmt.Errorf("invalid padding length %d", padLen)
|
||||
}
|
||||
for _, b := range data[len(data)-padLen:] {
|
||||
if int(b) != padLen {
|
||||
return nil, fmt.Errorf("invalid padding byte")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-padLen], nil
|
||||
}
|
||||
|
||||
func parseHeader(data []byte) (map[string][2]int, []byte, error) {
|
||||
// Decode the header (first msgpack object) using Decode into interface{},
|
||||
// then compute how many bytes it consumed to find the data blob start.
|
||||
r := bytes.NewReader(data)
|
||||
dec := msgpack.NewDecoder(r)
|
||||
dec.UseLooseInterfaceDecoding(true)
|
||||
|
||||
var headerRaw interface{}
|
||||
if err := dec.Decode(&headerRaw); err != nil {
|
||||
return nil, nil, fmt.Errorf("decode header: %w", err)
|
||||
}
|
||||
|
||||
headerMap, ok := headerRaw.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("header is not a map, got %T", headerRaw)
|
||||
}
|
||||
|
||||
toc := make(map[string][2]int, len(headerMap))
|
||||
for name, val := range headerMap {
|
||||
arr, ok := val.([]interface{})
|
||||
if !ok || len(arr) != 2 {
|
||||
return nil, nil, fmt.Errorf("table %q: expected [offset, length] array, got %T", name, val)
|
||||
}
|
||||
offset, err := toInt(arr[0])
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("table %q offset: %w", name, err)
|
||||
}
|
||||
length, err := toInt(arr[1])
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("table %q length: %w", name, err)
|
||||
}
|
||||
toc[name] = [2]int{offset, length}
|
||||
}
|
||||
|
||||
consumed := int(int64(len(data)) - int64(r.Len()))
|
||||
return toc, data[consumed:], nil
|
||||
}
|
||||
|
||||
func toInt(v interface{}) (int, error) {
|
||||
switch n := v.(type) {
|
||||
case int8:
|
||||
return int(n), nil
|
||||
case int16:
|
||||
return int(n), nil
|
||||
case int32:
|
||||
return int(n), nil
|
||||
case int64:
|
||||
return int(n), nil
|
||||
case uint8:
|
||||
return int(n), nil
|
||||
case uint16:
|
||||
return int(n), nil
|
||||
case uint32:
|
||||
return int(n), nil
|
||||
case uint64:
|
||||
return int(n), nil
|
||||
default:
|
||||
return 0, fmt.Errorf("cannot convert %T to int", v)
|
||||
}
|
||||
}
|
||||
|
||||
func decompressAndUnmarshal[T any](raw []byte) ([]T, error) {
|
||||
// Peek at the raw msgpack to check if it's an ext type (LZ4 compressed)
|
||||
// or a plain array.
|
||||
if len(raw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Try to decode as ext type first
|
||||
dec := msgpack.NewDecoder(bytes.NewReader(raw))
|
||||
code, extData, err := decodeExt(dec)
|
||||
if err == nil && code == lz4ExtCode {
|
||||
uncompressedSize, lz4Data, err := readLZ4ExtHeader(extData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read lz4 ext header: %w", err)
|
||||
}
|
||||
|
||||
decompressed := make([]byte, uncompressedSize)
|
||||
n, err := lz4.UncompressBlock(lz4Data, decompressed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("lz4 decompress: %w", err)
|
||||
}
|
||||
decompressed = decompressed[:n]
|
||||
|
||||
var result []T
|
||||
if err := msgpack.Unmarshal(decompressed, &result); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal decompressed table: %w", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Not LZ4 compressed, try as plain array
|
||||
var result []T
|
||||
if err := msgpack.Unmarshal(raw, &result); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal plain table: %w", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func decodeExt(dec *msgpack.Decoder) (int8, []byte, error) {
|
||||
var ext msgpack.RawMessage
|
||||
if err := dec.Decode(&ext); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
// ext is the full msgpack ext bytes including the header.
|
||||
// Re-decode just the header to get the type code, then the body is the rest.
|
||||
innerDec := msgpack.NewDecoder(bytes.NewReader(ext))
|
||||
extID, extLen, err := innerDec.DecodeExtHeader()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
extData := make([]byte, extLen)
|
||||
if _, err := innerDec.Buffered().Read(extData); err != nil {
|
||||
return 0, nil, fmt.Errorf("read ext data: %w", err)
|
||||
}
|
||||
return extID, extData, nil
|
||||
}
|
||||
|
||||
func readLZ4ExtHeader(data []byte) (int, []byte, error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil, fmt.Errorf("empty ext data")
|
||||
}
|
||||
tag := data[0]
|
||||
switch {
|
||||
case tag == 0xd2: // big-endian int32
|
||||
if len(data) < 5 {
|
||||
return 0, nil, fmt.Errorf("not enough data for int32 size")
|
||||
}
|
||||
size := int(int32(binary.BigEndian.Uint32(data[1:5])))
|
||||
return size, data[5:], nil
|
||||
case tag == 0xce: // big-endian uint32
|
||||
if len(data) < 5 {
|
||||
return 0, nil, fmt.Errorf("not enough data for uint32 size")
|
||||
}
|
||||
size := int(binary.BigEndian.Uint32(data[1:5]))
|
||||
return size, data[5:], nil
|
||||
case tag <= 0x7f: // positive fixint
|
||||
return int(tag), data[1:], nil
|
||||
default:
|
||||
return 0, nil, fmt.Errorf("unexpected tag 0x%02x in LZ4 ext header", tag)
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type numericalFunctionRow struct {
|
||||
NumericalFunctionId int32 `json:"NumericalFunctionId"`
|
||||
NumericalFunctionType int32 `json:"NumericalFunctionType"`
|
||||
NumericalFunctionParameterGroupId int32 `json:"NumericalFunctionParameterGroupId"`
|
||||
}
|
||||
|
||||
type numericalFunctionParameterRow struct {
|
||||
NumericalFunctionParameterGroupId int32 `json:"NumericalFunctionParameterGroupId"`
|
||||
ParameterIndex int32 `json:"ParameterIndex"`
|
||||
ParameterValue int32 `json:"ParameterValue"`
|
||||
}
|
||||
|
||||
type NumericalFunc struct {
|
||||
Type model.NumericalFunctionType
|
||||
Params []int32
|
||||
@@ -61,17 +49,17 @@ type FunctionResolver struct {
|
||||
}
|
||||
|
||||
func LoadFunctionResolver() (*FunctionResolver, error) {
|
||||
funcRows, err := utils.ReadJSON[numericalFunctionRow]("EntityMNumericalFunctionTable.json")
|
||||
funcRows, err := utils.ReadTable[EntityMNumericalFunction]("m_numerical_function")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load numerical function table: %w", err)
|
||||
}
|
||||
|
||||
paramRows, err := utils.ReadJSON[numericalFunctionParameterRow]("EntityMNumericalFunctionParameterGroupTable.json")
|
||||
paramRows, err := utils.ReadTable[EntityMNumericalFunctionParameterGroup]("m_numerical_function_parameter_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load numerical function parameter group table: %w", err)
|
||||
}
|
||||
|
||||
paramsByGroup := make(map[int32][]numericalFunctionParameterRow, len(paramRows))
|
||||
paramsByGroup := make(map[int32][]EntityMNumericalFunctionParameterGroup, len(paramRows))
|
||||
for _, r := range paramRows {
|
||||
paramsByGroup[r.NumericalFunctionParameterGroupId] = append(
|
||||
paramsByGroup[r.NumericalFunctionParameterGroupId], r)
|
||||
|
||||
@@ -5,11 +5,6 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type omikujiEntry struct {
|
||||
OmikujiId int32 `json:"OmikujiId"`
|
||||
OmikujiAssetId int32 `json:"OmikujiAssetId"`
|
||||
}
|
||||
|
||||
type OmikujiCatalog struct {
|
||||
assetIds map[int32]int32
|
||||
}
|
||||
@@ -22,7 +17,7 @@ func (c *OmikujiCatalog) LookupAssetId(omikujiId int32) int32 {
|
||||
}
|
||||
|
||||
func LoadOmikujiCatalog() *OmikujiCatalog {
|
||||
entries, err := utils.ReadJSON[omikujiEntry]("EntityMOmikujiTable.json")
|
||||
entries, err := utils.ReadTable[EntityMOmikuji]("m_omikuji")
|
||||
if err != nil {
|
||||
log.Fatalf("load omikuji table: %v", err)
|
||||
}
|
||||
|
||||
@@ -7,63 +7,37 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type PartsRow struct {
|
||||
PartsId int32 `json:"PartsId"`
|
||||
RarityType model.RarityType `json:"RarityType"`
|
||||
PartsGroupId int32 `json:"PartsGroupId"`
|
||||
PartsStatusMainLotteryGroupId int32 `json:"PartsStatusMainLotteryGroupId"`
|
||||
}
|
||||
|
||||
type PartsRarityRow struct {
|
||||
RarityType model.RarityType `json:"RarityType"`
|
||||
PartsLevelUpRateGroupId int32 `json:"PartsLevelUpRateGroupId"`
|
||||
PartsLevelUpPriceGroupId int32 `json:"PartsLevelUpPriceGroupId"`
|
||||
SellPriceNumericalFunctionId int32 `json:"SellPriceNumericalFunctionId"`
|
||||
}
|
||||
|
||||
type partsLevelUpRateRow struct {
|
||||
PartsLevelUpRateGroupId int32 `json:"PartsLevelUpRateGroupId"`
|
||||
LevelLowerLimit int32 `json:"LevelLowerLimit"`
|
||||
SuccessRatePermil int32 `json:"SuccessRatePermil"`
|
||||
}
|
||||
|
||||
type partsLevelUpPriceRow struct {
|
||||
PartsLevelUpPriceGroupId int32 `json:"PartsLevelUpPriceGroupId"`
|
||||
LevelLowerLimit int32 `json:"LevelLowerLimit"`
|
||||
Gold int32 `json:"Gold"`
|
||||
}
|
||||
|
||||
type PartsCatalog struct {
|
||||
PartsById map[int32]PartsRow
|
||||
PartsById map[int32]EntityMParts
|
||||
DefaultPartsStatusMainByLotteryGroup map[int32]int32
|
||||
RarityByRarityType map[model.RarityType]PartsRarityRow
|
||||
RarityByRarityType map[model.RarityType]EntityMPartsRarity
|
||||
RateByGroupAndLevel map[int32]map[int32]int32
|
||||
PriceByGroupAndLevel map[int32]map[int32]int32
|
||||
SellPriceByRarity map[model.RarityType]NumericalFunc
|
||||
}
|
||||
|
||||
func LoadPartsCatalog() (*PartsCatalog, error) {
|
||||
partsRows, err := utils.ReadJSON[PartsRow]("EntityMPartsTable.json")
|
||||
partsRows, err := utils.ReadTable[EntityMParts]("m_parts")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load parts table: %w", err)
|
||||
}
|
||||
|
||||
rarityRows, err := utils.ReadJSON[PartsRarityRow]("EntityMPartsRarityTable.json")
|
||||
rarityRows, err := utils.ReadTable[EntityMPartsRarity]("m_parts_rarity")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load parts rarity table: %w", err)
|
||||
}
|
||||
|
||||
rateRows, err := utils.ReadJSON[partsLevelUpRateRow]("EntityMPartsLevelUpRateGroupTable.json")
|
||||
rateRows, err := utils.ReadTable[EntityMPartsLevelUpRateGroup]("m_parts_level_up_rate_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load parts level up rate table: %w", err)
|
||||
}
|
||||
|
||||
priceRows, err := utils.ReadJSON[partsLevelUpPriceRow]("EntityMPartsLevelUpPriceGroupTable.json")
|
||||
priceRows, err := utils.ReadTable[EntityMPartsLevelUpPriceGroup]("m_parts_level_up_price_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load parts level up price table: %w", err)
|
||||
}
|
||||
|
||||
partsById := make(map[int32]PartsRow, len(partsRows))
|
||||
partsById := make(map[int32]EntityMParts, len(partsRows))
|
||||
for _, p := range partsRows {
|
||||
partsById[p.PartsId] = p
|
||||
}
|
||||
@@ -84,7 +58,7 @@ func LoadPartsCatalog() (*PartsCatalog, error) {
|
||||
return nil, fmt.Errorf("load function resolver: %w", err)
|
||||
}
|
||||
|
||||
rarityByRarityType := make(map[model.RarityType]PartsRarityRow, len(rarityRows))
|
||||
rarityByRarityType := make(map[model.RarityType]EntityMPartsRarity, len(rarityRows))
|
||||
sellPriceByRarity := make(map[model.RarityType]NumericalFunc, len(rarityRows))
|
||||
for _, r := range rarityRows {
|
||||
rarityByRarityType[r.RarityType] = r
|
||||
|
||||
@@ -4,214 +4,34 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type QuestSceneRow struct {
|
||||
QuestSceneId int32 `json:"QuestSceneId"`
|
||||
QuestId int32 `json:"QuestId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
QuestSceneType model.QuestSceneType `json:"QuestSceneType"`
|
||||
AssetBackgroundId int32 `json:"AssetBackgroundId"`
|
||||
EventMapNumberUpper int32 `json:"EventMapNumberUpper"`
|
||||
EventMapNumberLower int32 `json:"EventMapNumberLower"`
|
||||
IsMainFlowQuestTarget bool `json:"IsMainFlowQuestTarget"`
|
||||
IsBattleOnlyTarget bool `json:"IsBattleOnlyTarget"`
|
||||
QuestResultType model.QuestResultType `json:"QuestResultType"`
|
||||
IsStorySkipTarget bool `json:"IsStorySkipTarget"`
|
||||
}
|
||||
|
||||
type QuestRow struct {
|
||||
QuestId int32 `json:"QuestId"`
|
||||
NameQuestTextId int32 `json:"NameQuestTextId"`
|
||||
PictureBookNameQuestTextId int32 `json:"PictureBookNameQuestTextId"`
|
||||
QuestReleaseConditionListId int32 `json:"QuestReleaseConditionListId"`
|
||||
StoryQuestTextId int32 `json:"StoryQuestTextId"`
|
||||
QuestDisplayAttributeGroupId int32 `json:"QuestDisplayAttributeGroupId"`
|
||||
RecommendedDeckPower int32 `json:"RecommendedDeckPower"`
|
||||
QuestFirstClearRewardGroupId int32 `json:"QuestFirstClearRewardGroupId"`
|
||||
QuestPickupRewardGroupId int32 `json:"QuestPickupRewardGroupId"`
|
||||
QuestDeckRestrictionGroupId int32 `json:"QuestDeckRestrictionGroupId"`
|
||||
QuestMissionGroupId int32 `json:"QuestMissionGroupId"`
|
||||
Stamina int32 `json:"Stamina"`
|
||||
UserExp int32 `json:"UserExp"`
|
||||
CharacterExp int32 `json:"CharacterExp"`
|
||||
CostumeExp int32 `json:"CostumeExp"`
|
||||
Gold int32 `json:"Gold"`
|
||||
DailyClearableCount int32 `json:"DailyClearableCount"`
|
||||
IsRunInTheBackground bool `json:"IsRunInTheBackground"`
|
||||
IsCountedAsQuest bool `json:"IsCountedAsQuest"`
|
||||
QuestBonusId int32 `json:"QuestBonusId"`
|
||||
IsNotShowAfterClear bool `json:"IsNotShowAfterClear"`
|
||||
IsBigWinTarget bool `json:"IsBigWinTarget"`
|
||||
IsUsableSkipTicket bool `json:"IsUsableSkipTicket"`
|
||||
QuestReplayFlowRewardGroupId int32 `json:"QuestReplayFlowRewardGroupId"`
|
||||
InvisibleQuestMissionGroupId int32 `json:"InvisibleQuestMissionGroupId"`
|
||||
FieldEffectGroupId int32 `json:"FieldEffectGroupId"`
|
||||
}
|
||||
|
||||
type QuestMissionRow struct {
|
||||
QuestMissionId int32 `json:"QuestMissionId"`
|
||||
QuestMissionConditionType model.QuestMissionConditionType `json:"QuestMissionConditionType"`
|
||||
QuestMissionRewardId int32 `json:"QuestMissionRewardId"`
|
||||
QuestMissionConditionValueGroupId int32 `json:"QuestMissionConditionValueGroupId"`
|
||||
}
|
||||
|
||||
type QuestMissionGroupRow struct {
|
||||
QuestMissionGroupId int32 `json:"QuestMissionGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
QuestMissionId int32 `json:"QuestMissionId"`
|
||||
}
|
||||
|
||||
type QuestMissionRewardRow struct {
|
||||
QuestMissionRewardId int32 `json:"QuestMissionRewardId"`
|
||||
PossessionType model.PossessionType `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type MainQuestSequenceRow struct {
|
||||
MainQuestSequenceId int32 `json:"MainQuestSequenceId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
QuestId int32 `json:"QuestId"`
|
||||
}
|
||||
|
||||
type MainQuestRouteRow struct {
|
||||
MainQuestRouteId int32 `json:"MainQuestRouteId"`
|
||||
MainQuestSeasonId int32 `json:"MainQuestSeasonId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
CharacterId int32 `json:"CharacterId"`
|
||||
}
|
||||
|
||||
type MainQuestChapterRow struct {
|
||||
MainQuestChapterId int32 `json:"MainQuestChapterId"`
|
||||
MainQuestRouteId int32 `json:"MainQuestRouteId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
MainQuestSequenceGroupId int32 `json:"MainQuestSequenceGroupId"`
|
||||
PortalCageCharacterGroupId int32 `json:"PortalCageCharacterGroupId"`
|
||||
StartDatetime int64 `json:"StartDatetime"`
|
||||
IsInvisibleInLibrary bool `json:"IsInvisibleInLibrary"`
|
||||
JoinLibraryChapterId int32 `json:"JoinLibraryChapterId"`
|
||||
}
|
||||
|
||||
type QuestFirstClearRewardSwitchRow struct {
|
||||
QuestId int32 `json:"QuestId"`
|
||||
QuestFirstClearRewardGroupId int32 `json:"QuestFirstClearRewardGroupId"`
|
||||
SwitchConditionClearQuestId int32 `json:"SwitchConditionClearQuestId"`
|
||||
}
|
||||
|
||||
type QuestFirstClearRewardGroupRow struct {
|
||||
QuestFirstClearRewardGroupId int32 `json:"QuestFirstClearRewardGroupId"`
|
||||
QuestFirstClearRewardType int32 `json:"QuestFirstClearRewardType"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
PossessionType model.PossessionType `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
IsPickup bool `json:"IsPickup"`
|
||||
}
|
||||
|
||||
type QuestReplayFlowRewardGroupRow struct {
|
||||
QuestReplayFlowRewardGroupId int32 `json:"QuestReplayFlowRewardGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
PossessionType model.PossessionType `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type QuestSceneGrantRow struct {
|
||||
QuestSceneId int32 `json:"QuestSceneId"`
|
||||
PossessionType model.PossessionType `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type QuestPickupRewardGroupRow struct {
|
||||
QuestPickupRewardGroupId int32 `json:"QuestPickupRewardGroupId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
BattleDropRewardId int32 `json:"BattleDropRewardId"`
|
||||
}
|
||||
|
||||
type BattleDropRewardRow struct {
|
||||
BattleDropRewardId int32 `json:"BattleDropRewardId"`
|
||||
PossessionType model.PossessionType `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type QuestSceneBattleRow struct {
|
||||
QuestSceneId int32 `json:"QuestSceneId"`
|
||||
BattleGroupId int32 `json:"BattleGroupId"`
|
||||
}
|
||||
|
||||
type BattleGroupRow struct {
|
||||
BattleGroupId int32 `json:"BattleGroupId"`
|
||||
WaveNumber int32 `json:"WaveNumber"`
|
||||
BattleId int32 `json:"BattleId"`
|
||||
}
|
||||
|
||||
type BattleRow struct {
|
||||
BattleId int32 `json:"BattleId"`
|
||||
BattleNpcId int32 `json:"BattleNpcId"`
|
||||
DeckType model.DeckType `json:"DeckType"`
|
||||
BattleNpcDeckNumber int32 `json:"BattleNpcDeckNumber"`
|
||||
}
|
||||
|
||||
type BattleNpcDeckRow struct {
|
||||
BattleNpcId int32 `json:"BattleNpcId"`
|
||||
DeckType model.DeckType `json:"DeckType"`
|
||||
BattleNpcDeckNumber int32 `json:"BattleNpcDeckNumber"`
|
||||
BattleNpcDeckCharacterUuid01 string `json:"BattleNpcDeckCharacterUuid01"`
|
||||
BattleNpcDeckCharacterUuid02 string `json:"BattleNpcDeckCharacterUuid02"`
|
||||
BattleNpcDeckCharacterUuid03 string `json:"BattleNpcDeckCharacterUuid03"`
|
||||
}
|
||||
|
||||
type BattleNpcDropCategoryRow struct {
|
||||
BattleNpcId int32 `json:"BattleNpcId"`
|
||||
BattleNpcDeckCharacterUuid string `json:"BattleNpcDeckCharacterUuid"`
|
||||
BattleDropCategoryId int32 `json:"BattleDropCategoryId"`
|
||||
}
|
||||
|
||||
type BattleDropInfo struct {
|
||||
QuestSceneId int32
|
||||
BattleDropCategoryId int32
|
||||
}
|
||||
|
||||
type TutorialUnlockConditionRow struct {
|
||||
TutorialType int32 `json:"TutorialType"`
|
||||
TutorialUnlockConditionType int32 `json:"TutorialUnlockConditionType"`
|
||||
ConditionValue int32 `json:"ConditionValue"`
|
||||
}
|
||||
|
||||
type RentalDeckRow struct {
|
||||
BattleGroupId int32 `json:"BattleGroupId"`
|
||||
}
|
||||
|
||||
type UserLevelRow struct {
|
||||
UserLevel int32 `json:"UserLevel"`
|
||||
MaxStamina int32 `json:"MaxStamina"`
|
||||
}
|
||||
|
||||
type QuestCatalog struct {
|
||||
SceneById map[int32]QuestSceneRow
|
||||
MissionById map[int32]QuestMissionRow
|
||||
QuestById map[int32]QuestRow
|
||||
SceneById map[int32]EntityMQuestScene
|
||||
MissionById map[int32]EntityMQuestMission
|
||||
QuestById map[int32]EntityMQuest
|
||||
MissionIdsByQuestId map[int32][]int32
|
||||
RouteIdByQuestId map[int32]int32
|
||||
SceneIdsByQuestId map[int32][]int32
|
||||
OrderedQuestIds []int32
|
||||
FirstClearRewardsByGroupId map[int32][]QuestFirstClearRewardGroupRow
|
||||
FirstClearRewardSwitchesByQuestId map[int32][]QuestFirstClearRewardSwitchRow
|
||||
MissionRewardsByMissionId map[int32][]QuestMissionRewardRow
|
||||
FirstClearRewardsByGroupId map[int32][]EntityMQuestFirstClearRewardGroup
|
||||
FirstClearRewardSwitchesByQuestId map[int32][]EntityMQuestFirstClearRewardSwitch
|
||||
MissionRewardsByMissionId map[int32][]EntityMQuestMissionReward
|
||||
WeaponIdsByReleaseConditionGroupId map[int32][]int32
|
||||
ReleaseConditionsByGroupId map[int32][]WeaponStoryReleaseConditionRow
|
||||
SceneGrantsBySceneId map[int32][]QuestSceneGrantRow
|
||||
BattleDropRewardById map[int32]BattleDropRewardRow
|
||||
ReleaseConditionsByGroupId map[int32][]EntityMWeaponStoryReleaseConditionGroup
|
||||
SceneGrantsBySceneId map[int32][]EntityMUserQuestSceneGrantPossession
|
||||
BattleDropRewardById map[int32]EntityMBattleDropReward
|
||||
PickupRewardIdsByGroupId map[int32][]int32
|
||||
BattleDropsByQuestId map[int32][]BattleDropInfo
|
||||
ReplayFlowRewardsByGroupId map[int32][]QuestReplayFlowRewardGroupRow
|
||||
ReplayFlowRewardsByGroupId map[int32][]EntityMQuestReplayFlowRewardGroup
|
||||
RentalQuestIds map[int32]bool
|
||||
TutorialUnlockConditions []TutorialUnlockConditionRow
|
||||
TutorialUnlockConditions []EntityMTutorialUnlockCondition
|
||||
ChapterLastSceneByQuestId map[int32]int32
|
||||
SeasonIdByRouteId map[int32]int32
|
||||
|
||||
@@ -221,8 +41,8 @@ type QuestCatalog struct {
|
||||
CostumeMaxLevelByRarity map[int32]NumericalFunc
|
||||
MaxStaminaByLevel map[int32]int32
|
||||
|
||||
CostumeById map[int32]CostumeMasterRow
|
||||
WeaponById map[int32]WeaponMasterRow
|
||||
CostumeById map[int32]EntityMCostume
|
||||
WeaponById map[int32]EntityMWeapon
|
||||
|
||||
WeaponSkillSlots map[int32][]int32
|
||||
WeaponAbilitySlots map[int32][]int32
|
||||
@@ -231,7 +51,7 @@ type QuestCatalog struct {
|
||||
}
|
||||
|
||||
func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
scenes, err := utils.ReadJSON[QuestSceneRow]("EntityMQuestSceneTable.json")
|
||||
scenes, err := utils.ReadTable[EntityMQuestScene]("m_quest_scene")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest scene table: %w", err)
|
||||
}
|
||||
@@ -245,17 +65,17 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return scenes[i].QuestSceneId < scenes[j].QuestSceneId
|
||||
})
|
||||
|
||||
missions, err := utils.ReadJSON[QuestMissionRow]("EntityMQuestMissionTable.json")
|
||||
missions, err := utils.ReadTable[EntityMQuestMission]("m_quest_mission")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest mission table: %w", err)
|
||||
}
|
||||
|
||||
quests, err := utils.ReadJSON[QuestRow]("EntityMQuestTable.json")
|
||||
quests, err := utils.ReadTable[EntityMQuest]("m_quest")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest table: %w", err)
|
||||
}
|
||||
|
||||
missionGroups, err := utils.ReadJSON[QuestMissionGroupRow]("EntityMQuestMissionGroupTable.json")
|
||||
missionGroups, err := utils.ReadTable[EntityMQuestMissionGroup]("m_quest_mission_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest mission group table: %w", err)
|
||||
}
|
||||
@@ -269,7 +89,7 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return missionGroups[i].QuestMissionId < missionGroups[j].QuestMissionId
|
||||
})
|
||||
|
||||
sequences, err := utils.ReadJSON[MainQuestSequenceRow]("EntityMMainQuestSequenceTable.json")
|
||||
sequences, err := utils.ReadTable[EntityMMainQuestSequence]("m_main_quest_sequence")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load main quest sequence table: %w", err)
|
||||
}
|
||||
@@ -283,12 +103,12 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return sequences[i].QuestId < sequences[j].QuestId
|
||||
})
|
||||
|
||||
chapters, err := utils.ReadJSON[MainQuestChapterRow]("EntityMMainQuestChapterTable.json")
|
||||
chapters, err := utils.ReadTable[EntityMMainQuestChapter]("m_main_quest_chapter")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load main quest chapter table: %w", err)
|
||||
}
|
||||
|
||||
routes, err := utils.ReadJSON[MainQuestRouteRow]("EntityMMainQuestRouteTable.json")
|
||||
routes, err := utils.ReadTable[EntityMMainQuestRoute]("m_main_quest_route")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load main quest route table: %w", err)
|
||||
}
|
||||
@@ -297,12 +117,12 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
seasonIdByRouteId[r.MainQuestRouteId] = r.MainQuestSeasonId
|
||||
}
|
||||
|
||||
firstClearSwitches, err := utils.ReadJSON[QuestFirstClearRewardSwitchRow]("EntityMQuestFirstClearRewardSwitchTable.json")
|
||||
firstClearSwitches, err := utils.ReadTable[EntityMQuestFirstClearRewardSwitch]("m_quest_first_clear_reward_switch")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest first clear reward switch table: %w", err)
|
||||
}
|
||||
|
||||
firstClearRewards, err := utils.ReadJSON[QuestFirstClearRewardGroupRow]("EntityMQuestFirstClearRewardGroupTable.json")
|
||||
firstClearRewards, err := utils.ReadTable[EntityMQuestFirstClearRewardGroup]("m_quest_first_clear_reward_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest first clear reward group table: %w", err)
|
||||
}
|
||||
@@ -316,7 +136,7 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return firstClearRewards[i].QuestFirstClearRewardType < firstClearRewards[j].QuestFirstClearRewardType
|
||||
})
|
||||
|
||||
replayFlowRewards, err := utils.ReadJSON[QuestReplayFlowRewardGroupRow]("EntityMQuestReplayFlowRewardGroupTable.json")
|
||||
replayFlowRewards, err := utils.ReadTable[EntityMQuestReplayFlowRewardGroup]("m_quest_replay_flow_reward_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest replay flow reward group table: %w", err)
|
||||
}
|
||||
@@ -327,52 +147,52 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return replayFlowRewards[i].SortOrder < replayFlowRewards[j].SortOrder
|
||||
})
|
||||
|
||||
missionRewards, err := utils.ReadJSON[QuestMissionRewardRow]("EntityMQuestMissionRewardTable.json")
|
||||
missionRewards, err := utils.ReadTable[EntityMQuestMissionReward]("m_quest_mission_reward")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest mission reward table: %w", err)
|
||||
}
|
||||
|
||||
weapons, err := utils.ReadJSON[WeaponMasterRow]("EntityMWeaponTable.json")
|
||||
weapons, err := utils.ReadTable[EntityMWeapon]("m_weapon")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon table: %w", err)
|
||||
}
|
||||
|
||||
weaponSkillGroups, err := utils.ReadJSON[WeaponSkillGroupRow]("EntityMWeaponSkillGroupTable.json")
|
||||
weaponSkillGroups, err := utils.ReadTable[EntityMWeaponSkillGroup]("m_weapon_skill_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon skill group table: %w", err)
|
||||
}
|
||||
|
||||
weaponAbilityGroups, err := utils.ReadJSON[WeaponAbilityGroupRow]("EntityMWeaponAbilityGroupTable.json")
|
||||
weaponAbilityGroups, err := utils.ReadTable[EntityMWeaponAbilityGroup]("m_weapon_ability_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon ability group table: %w", err)
|
||||
}
|
||||
|
||||
releaseConditions, err := utils.ReadJSON[WeaponStoryReleaseConditionRow]("EntityMWeaponStoryReleaseConditionGroupTable.json")
|
||||
releaseConditions, err := utils.ReadTable[EntityMWeaponStoryReleaseConditionGroup]("m_weapon_story_release_condition_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon story release condition table: %w", err)
|
||||
}
|
||||
|
||||
costumeMasters, err := utils.ReadJSON[CostumeMasterRow]("EntityMCostumeTable.json")
|
||||
costumeMasters, err := utils.ReadTable[EntityMCostume]("m_costume")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume table: %w", err)
|
||||
}
|
||||
|
||||
costumeRarities, err := utils.ReadJSON[costumeRarityRow]("EntityMCostumeRarityTable.json")
|
||||
costumeRarities, err := utils.ReadTable[EntityMCostumeRarity]("m_costume_rarity")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load costume rarity table: %w", err)
|
||||
}
|
||||
|
||||
sceneGrants, err := utils.ReadJSON[QuestSceneGrantRow]("EntityMUserQuestSceneGrantPossessionTable.json")
|
||||
sceneGrants, err := utils.ReadTable[EntityMUserQuestSceneGrantPossession]("m_user_quest_scene_grant_possession")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest scene grant table: %w", err)
|
||||
}
|
||||
|
||||
battleDropRewards, err := utils.ReadJSON[BattleDropRewardRow]("EntityMBattleDropRewardTable.json")
|
||||
battleDropRewards, err := utils.ReadTable[EntityMBattleDropReward]("m_battle_drop_reward")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle drop reward table: %w", err)
|
||||
}
|
||||
|
||||
pickupRewardGroups, err := utils.ReadJSON[QuestPickupRewardGroupRow]("EntityMQuestPickupRewardGroupTable.json")
|
||||
pickupRewardGroups, err := utils.ReadTable[EntityMQuestPickupRewardGroup]("m_quest_pickup_reward_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest pickup reward group table: %w", err)
|
||||
}
|
||||
@@ -383,37 +203,37 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return pickupRewardGroups[i].SortOrder < pickupRewardGroups[j].SortOrder
|
||||
})
|
||||
|
||||
sceneBattles, err := utils.ReadJSON[QuestSceneBattleRow]("EntityMQuestSceneBattleTable.json")
|
||||
sceneBattles, err := utils.ReadTable[EntityMQuestSceneBattle]("m_quest_scene_battle")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load quest scene battle table: %w", err)
|
||||
}
|
||||
|
||||
battleGroups, err := utils.ReadJSON[BattleGroupRow]("EntityMBattleGroupTable.json")
|
||||
battleGroups, err := utils.ReadTable[EntityMBattleGroup]("m_battle_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle group table: %w", err)
|
||||
}
|
||||
|
||||
battles, err := utils.ReadJSON[BattleRow]("EntityMBattleTable.json")
|
||||
battles, err := utils.ReadTable[EntityMBattle]("m_battle")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle table: %w", err)
|
||||
}
|
||||
|
||||
npcDecks, err := utils.ReadJSON[BattleNpcDeckRow]("EntityMBattleNpcDeckTable.json")
|
||||
npcDecks, err := utils.ReadTable[EntityMBattleNpcDeck]("m_battle_npc_deck")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle npc deck table: %w", err)
|
||||
}
|
||||
|
||||
npcDropCategories, err := utils.ReadJSON[BattleNpcDropCategoryRow]("EntityMBattleNpcDeckCharacterDropCategoryTable.json")
|
||||
npcDropCategories, err := utils.ReadTable[EntityMBattleNpcDeckCharacterDropCategory]("m_battle_npc_deck_character_drop_category")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle npc drop category table: %w", err)
|
||||
}
|
||||
|
||||
rentalDecks, err := utils.ReadJSON[RentalDeckRow]("EntityMBattleRentalDeckTable.json")
|
||||
rentalDecks, err := utils.ReadTable[EntityMBattleRentalDeck]("m_battle_rental_deck")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load battle rental deck table: %w", err)
|
||||
}
|
||||
|
||||
tutorialUnlockConds, err := utils.ReadJSON[TutorialUnlockConditionRow]("EntityMTutorialUnlockConditionTable.json")
|
||||
tutorialUnlockConds, err := utils.ReadTable[EntityMTutorialUnlockCondition]("m_tutorial_unlock_condition")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load tutorial unlock condition table: %w", err)
|
||||
}
|
||||
@@ -423,7 +243,7 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userLevels, err := utils.ReadJSON[UserLevelRow]("EntityMUserLevelTable.json")
|
||||
userLevels, err := utils.ReadTable[EntityMUserLevel]("m_user_level")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load user level table: %w", err)
|
||||
}
|
||||
@@ -450,12 +270,12 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
}
|
||||
}
|
||||
|
||||
costumeById := make(map[int32]CostumeMasterRow, len(costumeMasters))
|
||||
costumeById := make(map[int32]EntityMCostume, len(costumeMasters))
|
||||
for _, cm := range costumeMasters {
|
||||
costumeById[cm.CostumeId] = cm
|
||||
}
|
||||
|
||||
weaponById := make(map[int32]WeaponMasterRow, len(weapons))
|
||||
weaponById := make(map[int32]EntityMWeapon, len(weapons))
|
||||
for _, w := range weapons {
|
||||
weaponById[w.WeaponId] = w
|
||||
}
|
||||
@@ -469,19 +289,19 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
abilitySlots[row.WeaponAbilityGroupId] = append(abilitySlots[row.WeaponAbilityGroupId], row.SlotNumber)
|
||||
}
|
||||
|
||||
sceneById := make(map[int32]QuestSceneRow, len(scenes))
|
||||
sceneById := make(map[int32]EntityMQuestScene, len(scenes))
|
||||
sceneIdsByQuestId := make(map[int32][]int32)
|
||||
for _, scene := range scenes {
|
||||
sceneById[scene.QuestSceneId] = scene
|
||||
sceneIdsByQuestId[scene.QuestId] = append(sceneIdsByQuestId[scene.QuestId], scene.QuestSceneId)
|
||||
}
|
||||
|
||||
missionById := make(map[int32]QuestMissionRow, len(missions))
|
||||
missionById := make(map[int32]EntityMQuestMission, len(missions))
|
||||
for _, mission := range missions {
|
||||
missionById[mission.QuestMissionId] = mission
|
||||
}
|
||||
|
||||
questById := make(map[int32]QuestRow, len(quests))
|
||||
questById := make(map[int32]EntityMQuest, len(quests))
|
||||
for _, quest := range quests {
|
||||
questById[quest.QuestId] = quest
|
||||
}
|
||||
@@ -500,7 +320,7 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
missionIdsByQuestId[questId] = append([]int32(nil), missionIds...)
|
||||
}
|
||||
|
||||
chapterBySequenceId := make(map[int32]MainQuestChapterRow, len(chapters))
|
||||
chapterBySequenceId := make(map[int32]EntityMMainQuestChapter, len(chapters))
|
||||
for _, chapter := range chapters {
|
||||
chapterBySequenceId[chapter.MainQuestSequenceGroupId] = chapter
|
||||
}
|
||||
@@ -511,12 +331,12 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
}
|
||||
}
|
||||
|
||||
sortedChapters := make([]MainQuestChapterRow, len(chapters))
|
||||
sortedChapters := make([]EntityMMainQuestChapter, len(chapters))
|
||||
copy(sortedChapters, chapters)
|
||||
sort.Slice(sortedChapters, func(i, j int) bool {
|
||||
return sortedChapters[i].SortOrder < sortedChapters[j].SortOrder
|
||||
})
|
||||
sequencesByGroupId := make(map[int32][]MainQuestSequenceRow)
|
||||
sequencesByGroupId := make(map[int32][]EntityMMainQuestSequence)
|
||||
for _, seq := range sequences {
|
||||
sequencesByGroupId[seq.MainQuestSequenceId] = append(sequencesByGroupId[seq.MainQuestSequenceId], seq)
|
||||
}
|
||||
@@ -544,25 +364,25 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
}
|
||||
}
|
||||
|
||||
firstClearRewardsByGroupId := make(map[int32][]QuestFirstClearRewardGroupRow, len(firstClearRewards))
|
||||
firstClearRewardsByGroupId := make(map[int32][]EntityMQuestFirstClearRewardGroup, len(firstClearRewards))
|
||||
for _, reward := range firstClearRewards {
|
||||
firstClearRewardsByGroupId[reward.QuestFirstClearRewardGroupId] = append(
|
||||
firstClearRewardsByGroupId[reward.QuestFirstClearRewardGroupId], reward)
|
||||
}
|
||||
|
||||
replayFlowRewardsByGroupId := make(map[int32][]QuestReplayFlowRewardGroupRow, len(replayFlowRewards))
|
||||
replayFlowRewardsByGroupId := make(map[int32][]EntityMQuestReplayFlowRewardGroup, len(replayFlowRewards))
|
||||
for _, reward := range replayFlowRewards {
|
||||
replayFlowRewardsByGroupId[reward.QuestReplayFlowRewardGroupId] = append(
|
||||
replayFlowRewardsByGroupId[reward.QuestReplayFlowRewardGroupId], reward)
|
||||
}
|
||||
|
||||
firstClearRewardSwitchesByQuestId := make(map[int32][]QuestFirstClearRewardSwitchRow, len(firstClearSwitches))
|
||||
firstClearRewardSwitchesByQuestId := make(map[int32][]EntityMQuestFirstClearRewardSwitch, len(firstClearSwitches))
|
||||
for _, switchRow := range firstClearSwitches {
|
||||
firstClearRewardSwitchesByQuestId[switchRow.QuestId] = append(
|
||||
firstClearRewardSwitchesByQuestId[switchRow.QuestId], switchRow)
|
||||
}
|
||||
|
||||
missionRewardsByMissionId := make(map[int32][]QuestMissionRewardRow, len(missionRewards))
|
||||
missionRewardsByMissionId := make(map[int32][]EntityMQuestMissionReward, len(missionRewards))
|
||||
for _, reward := range missionRewards {
|
||||
missionRewardsByMissionId[reward.QuestMissionRewardId] = append(
|
||||
missionRewardsByMissionId[reward.QuestMissionRewardId], reward)
|
||||
@@ -576,18 +396,18 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
}
|
||||
}
|
||||
|
||||
releaseConditionsByGroupId := make(map[int32][]WeaponStoryReleaseConditionRow)
|
||||
releaseConditionsByGroupId := make(map[int32][]EntityMWeaponStoryReleaseConditionGroup)
|
||||
for _, c := range releaseConditions {
|
||||
releaseConditionsByGroupId[c.WeaponStoryReleaseConditionGroupId] = append(
|
||||
releaseConditionsByGroupId[c.WeaponStoryReleaseConditionGroupId], c)
|
||||
}
|
||||
|
||||
sceneGrantsBySceneId := make(map[int32][]QuestSceneGrantRow)
|
||||
sceneGrantsBySceneId := make(map[int32][]EntityMUserQuestSceneGrantPossession)
|
||||
for _, sg := range sceneGrants {
|
||||
sceneGrantsBySceneId[sg.QuestSceneId] = append(sceneGrantsBySceneId[sg.QuestSceneId], sg)
|
||||
}
|
||||
|
||||
battleDropRewardById := make(map[int32]BattleDropRewardRow, len(battleDropRewards))
|
||||
battleDropRewardById := make(map[int32]EntityMBattleDropReward, len(battleDropRewards))
|
||||
for _, bdr := range battleDropRewards {
|
||||
battleDropRewardById[bdr.BattleDropRewardId] = bdr
|
||||
}
|
||||
@@ -609,22 +429,22 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
||||
}
|
||||
|
||||
type npcDeckKey struct {
|
||||
BattleNpcId int32
|
||||
DeckType model.DeckType
|
||||
BattleNpcId int64
|
||||
DeckType int32
|
||||
BattleNpcDeckNumber int32
|
||||
}
|
||||
npcDeckByKey := make(map[npcDeckKey]BattleNpcDeckRow, len(npcDecks))
|
||||
npcDeckByKey := make(map[npcDeckKey]EntityMBattleNpcDeck, len(npcDecks))
|
||||
for _, d := range npcDecks {
|
||||
npcDeckByKey[npcDeckKey{d.BattleNpcId, d.DeckType, d.BattleNpcDeckNumber}] = d
|
||||
}
|
||||
|
||||
battleByIdMap := make(map[int32]BattleRow, len(battles))
|
||||
battleByIdMap := make(map[int32]EntityMBattle, len(battles))
|
||||
for _, b := range battles {
|
||||
battleByIdMap[b.BattleId] = b
|
||||
}
|
||||
|
||||
type dropCatKey struct {
|
||||
BattleNpcId int32
|
||||
BattleNpcId int64
|
||||
Uuid string
|
||||
}
|
||||
dropCategoryByKey := make(map[dropCatKey]int32, len(npcDropCategories))
|
||||
|
||||
@@ -8,96 +8,47 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type ShopItemRow struct {
|
||||
ShopItemId int32 `json:"ShopItemId"`
|
||||
PriceType int32 `json:"PriceType"`
|
||||
PriceId int32 `json:"PriceId"`
|
||||
Price int32 `json:"Price"`
|
||||
ShopItemLimitedStockId int32 `json:"ShopItemLimitedStockId"`
|
||||
}
|
||||
|
||||
type ShopContentRow struct {
|
||||
ShopItemId int32 `json:"ShopItemId"`
|
||||
PossessionType int32 `json:"PossessionType"`
|
||||
PossessionId int32 `json:"PossessionId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type ShopContentEffectRow struct {
|
||||
ShopItemId int32 `json:"ShopItemId"`
|
||||
EffectTargetType int32 `json:"EffectTargetType"`
|
||||
EffectValueType int32 `json:"EffectValueType"`
|
||||
EffectValue int32 `json:"EffectValue"`
|
||||
}
|
||||
|
||||
type shopItemLimitedStockRow struct {
|
||||
ShopItemLimitedStockId int32 `json:"ShopItemLimitedStockId"`
|
||||
MaxCount int32 `json:"MaxCount"`
|
||||
}
|
||||
|
||||
type shopRow struct {
|
||||
ShopId int32 `json:"ShopId"`
|
||||
ShopGroupType int32 `json:"ShopGroupType"`
|
||||
ShopItemCellGroupId int32 `json:"ShopItemCellGroupId"`
|
||||
}
|
||||
|
||||
type shopItemCellGroupRow struct {
|
||||
ShopItemCellGroupId int32 `json:"ShopItemCellGroupId"`
|
||||
ShopItemCellId int32 `json:"ShopItemCellId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type shopItemCellRow struct {
|
||||
ShopItemCellId int32 `json:"ShopItemCellId"`
|
||||
ShopItemId int32 `json:"ShopItemId"`
|
||||
}
|
||||
|
||||
type ExchangeShopCell struct {
|
||||
SortOrder int32
|
||||
ShopItemId int32
|
||||
}
|
||||
|
||||
type ShopCatalog struct {
|
||||
Items map[int32]ShopItemRow
|
||||
Contents map[int32][]ShopContentRow
|
||||
Effects map[int32][]ShopContentEffectRow
|
||||
Items map[int32]EntityMShopItem
|
||||
Contents map[int32][]EntityMShopItemContentPossession
|
||||
Effects map[int32][]EntityMShopItemContentEffect
|
||||
MaxStaminaMillis map[int32]int32 // level -> max stamina in millis
|
||||
LimitedStock map[int32]int32 // stock id -> max count
|
||||
ItemShopPool []int32 // shop item IDs for the replaceable item shop, sorted by cell sort order
|
||||
ExchangeShopCells map[int32][]ExchangeShopCell // shopId -> sorted cells for exchange shops
|
||||
}
|
||||
|
||||
type userLevelEntry struct {
|
||||
UserLevel int32 `json:"UserLevel"`
|
||||
MaxStamina int32 `json:"MaxStamina"`
|
||||
}
|
||||
|
||||
func LoadShopCatalog() (*ShopCatalog, error) {
|
||||
items, err := utils.ReadJSON[ShopItemRow]("EntityMShopItemTable.json")
|
||||
items, err := utils.ReadTable[EntityMShopItem]("m_shop_item")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop item table: %w", err)
|
||||
}
|
||||
contents, err := utils.ReadJSON[ShopContentRow]("EntityMShopItemContentPossessionTable.json")
|
||||
contents, err := utils.ReadTable[EntityMShopItemContentPossession]("m_shop_item_content_possession")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop content possession table: %w", err)
|
||||
}
|
||||
effects, err := utils.ReadJSON[ShopContentEffectRow]("EntityMShopItemContentEffectTable.json")
|
||||
effects, err := utils.ReadTable[EntityMShopItemContentEffect]("m_shop_item_content_effect")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop content effect table: %w", err)
|
||||
}
|
||||
userLevels, err := utils.ReadJSON[userLevelEntry]("EntityMUserLevelTable.json")
|
||||
userLevels, err := utils.ReadTable[EntityMUserLevel]("m_user_level")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load user level table: %w", err)
|
||||
}
|
||||
stockRows, err := utils.ReadJSON[shopItemLimitedStockRow]("EntityMShopItemLimitedStockTable.json")
|
||||
stockRows, err := utils.ReadTable[EntityMShopItemLimitedStock]("m_shop_item_limited_stock")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop item limited stock table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &ShopCatalog{
|
||||
Items: make(map[int32]ShopItemRow, len(items)),
|
||||
Contents: make(map[int32][]ShopContentRow, len(contents)),
|
||||
Effects: make(map[int32][]ShopContentEffectRow, len(effects)),
|
||||
Items: make(map[int32]EntityMShopItem, len(items)),
|
||||
Contents: make(map[int32][]EntityMShopItemContentPossession, len(contents)),
|
||||
Effects: make(map[int32][]EntityMShopItemContentEffect, len(effects)),
|
||||
MaxStaminaMillis: make(map[int32]int32, len(userLevels)),
|
||||
LimitedStock: make(map[int32]int32, len(stockRows)),
|
||||
}
|
||||
@@ -117,15 +68,15 @@ func LoadShopCatalog() (*ShopCatalog, error) {
|
||||
catalog.LimitedStock[row.ShopItemLimitedStockId] = row.MaxCount
|
||||
}
|
||||
|
||||
shops, err := utils.ReadJSON[shopRow]("EntityMShopTable.json")
|
||||
shops, err := utils.ReadTable[EntityMShop]("m_shop")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop table: %w", err)
|
||||
}
|
||||
cellGroups, err := utils.ReadJSON[shopItemCellGroupRow]("EntityMShopItemCellGroupTable.json")
|
||||
cellGroups, err := utils.ReadTable[EntityMShopItemCellGroup]("m_shop_item_cell_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop item cell group table: %w", err)
|
||||
}
|
||||
cells, err := utils.ReadJSON[shopItemCellRow]("EntityMShopItemCellTable.json")
|
||||
cells, err := utils.ReadTable[EntityMShopItemCell]("m_shop_item_cell")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load shop item cell table: %w", err)
|
||||
}
|
||||
@@ -135,7 +86,7 @@ func LoadShopCatalog() (*ShopCatalog, error) {
|
||||
cellIdToItemId[c.ShopItemCellId] = c.ShopItemId
|
||||
}
|
||||
|
||||
cellGroupByCGId := make(map[int32][]shopItemCellGroupRow, len(cellGroups))
|
||||
cellGroupByCGId := make(map[int32][]EntityMShopItemCellGroup, len(cellGroups))
|
||||
for _, cg := range cellGroups {
|
||||
cellGroupByCGId[cg.ShopItemCellGroupId] = append(cellGroupByCGId[cg.ShopItemCellGroupId], cg)
|
||||
}
|
||||
|
||||
@@ -5,18 +5,12 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type sideStorySceneRow struct {
|
||||
SideStoryQuestId int32 `json:"SideStoryQuestId"`
|
||||
SideStoryQuestSceneId int32 `json:"SideStoryQuestSceneId"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type SideStoryCatalog struct {
|
||||
FirstSceneByQuestId map[int32]int32
|
||||
}
|
||||
|
||||
func LoadSideStoryCatalog() *SideStoryCatalog {
|
||||
scenes, err := utils.ReadJSON[sideStorySceneRow]("EntityMSideStoryQuestSceneTable.json")
|
||||
scenes, err := utils.ReadTable[EntityMSideStoryQuestScene]("m_side_story_quest_scene")
|
||||
if err != nil {
|
||||
log.Fatalf("load side story quest scene table: %v", err)
|
||||
}
|
||||
|
||||
@@ -9,172 +9,55 @@ import (
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
type WeaponMasterRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
RarityType int32 `json:"RarityType"`
|
||||
WeaponType int32 `json:"WeaponType"`
|
||||
WeaponSpecificEnhanceId int32 `json:"WeaponSpecificEnhanceId"`
|
||||
WeaponSkillGroupId int32 `json:"WeaponSkillGroupId"`
|
||||
WeaponAbilityGroupId int32 `json:"WeaponAbilityGroupId"`
|
||||
WeaponStoryReleaseConditionGroupId int32 `json:"WeaponStoryReleaseConditionGroupId"`
|
||||
WeaponEvolutionMaterialGroupId int32 `json:"WeaponEvolutionMaterialGroupId"`
|
||||
}
|
||||
|
||||
type WeaponStoryReleaseConditionRow struct {
|
||||
WeaponStoryReleaseConditionGroupId int32 `json:"WeaponStoryReleaseConditionGroupId"`
|
||||
StoryIndex int32 `json:"StoryIndex"`
|
||||
WeaponStoryReleaseConditionType model.WeaponStoryReleaseConditionType `json:"WeaponStoryReleaseConditionType"`
|
||||
ConditionValue int32 `json:"ConditionValue"`
|
||||
WeaponStoryReleaseConditionOperationGroupId int32 `json:"WeaponStoryReleaseConditionOperationGroupId"`
|
||||
}
|
||||
|
||||
type WeaponSkillGroupRow struct {
|
||||
WeaponSkillGroupId int32 `json:"WeaponSkillGroupId"`
|
||||
SlotNumber int32 `json:"SlotNumber"`
|
||||
SkillId int32 `json:"SkillId"`
|
||||
WeaponSkillEnhancementMaterialId int32 `json:"WeaponSkillEnhancementMaterialId"`
|
||||
}
|
||||
|
||||
type WeaponAbilityGroupRow struct {
|
||||
WeaponAbilityGroupId int32 `json:"WeaponAbilityGroupId"`
|
||||
SlotNumber int32 `json:"SlotNumber"`
|
||||
AbilityId int32 `json:"AbilityId"`
|
||||
WeaponAbilityEnhancementMaterialId int32 `json:"WeaponAbilityEnhancementMaterialId"`
|
||||
}
|
||||
|
||||
type weaponSpecificEnhanceRow struct {
|
||||
WeaponSpecificEnhanceId int32 `json:"WeaponSpecificEnhanceId"`
|
||||
BaseEnhancementObtainedExp int32 `json:"BaseEnhancementObtainedExp"`
|
||||
SellPriceNumericalFunctionId int32 `json:"SellPriceNumericalFunctionId"`
|
||||
RequiredExpForLevelUpNumericalParameterMapId int32 `json:"RequiredExpForLevelUpNumericalParameterMapId"`
|
||||
EnhancementCostByWeaponNumericalFunctionId int32 `json:"EnhancementCostByWeaponNumericalFunctionId"`
|
||||
EnhancementCostByMaterialNumericalFunctionId int32 `json:"EnhancementCostByMaterialNumericalFunctionId"`
|
||||
MaxLevelNumericalFunctionId int32 `json:"MaxLevelNumericalFunctionId"`
|
||||
EvolutionCostNumericalFunctionId int32 `json:"EvolutionCostNumericalFunctionId"`
|
||||
LimitBreakCostByWeaponNumericalFunctionId int32 `json:"LimitBreakCostByWeaponNumericalFunctionId"`
|
||||
LimitBreakCostByMaterialNumericalFunctionId int32 `json:"LimitBreakCostByMaterialNumericalFunctionId"`
|
||||
MaxSkillLevelNumericalFunctionId int32 `json:"MaxSkillLevelNumericalFunctionId"`
|
||||
SkillEnhancementCostNumericalFunctionId int32 `json:"SkillEnhancementCostNumericalFunctionId"`
|
||||
MaxAbilityLevelNumericalFunctionId int32 `json:"MaxAbilityLevelNumericalFunctionId"`
|
||||
AbilityEnhancementCostNumericalFunctionId int32 `json:"AbilityEnhancementCostNumericalFunctionId"`
|
||||
}
|
||||
|
||||
type weaponConsumeExchangeRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
ConsumableItemId int32 `json:"ConsumableItemId"`
|
||||
Count int32 `json:"Count"`
|
||||
}
|
||||
|
||||
type WeaponEvolutionGroupRow struct {
|
||||
WeaponEvolutionGroupId int32 `json:"WeaponEvolutionGroupId"`
|
||||
EvolutionOrder int32 `json:"EvolutionOrder"`
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
}
|
||||
|
||||
type WeaponEvolutionMaterialRow struct {
|
||||
WeaponEvolutionMaterialGroupId int32 `json:"WeaponEvolutionMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type WeaponSkillEnhanceMaterialRow struct {
|
||||
WeaponSkillEnhancementMaterialId int32 `json:"WeaponSkillEnhancementMaterialId"`
|
||||
SkillLevel int32 `json:"SkillLevel"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type WeaponAbilityEnhanceMaterialRow struct {
|
||||
WeaponAbilityEnhancementMaterialId int32 `json:"WeaponAbilityEnhancementMaterialId"`
|
||||
AbilityLevel int32 `json:"AbilityLevel"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type WeaponAwakenRow struct {
|
||||
WeaponId int32 `json:"WeaponId"`
|
||||
WeaponAwakenEffectGroupId int32 `json:"WeaponAwakenEffectGroupId"`
|
||||
WeaponAwakenMaterialGroupId int32 `json:"WeaponAwakenMaterialGroupId"`
|
||||
ConsumeGold int32 `json:"ConsumeGold"`
|
||||
LevelLimitUp int32 `json:"LevelLimitUp"`
|
||||
}
|
||||
|
||||
type WeaponAwakenEffectGroupRow struct {
|
||||
WeaponAwakenEffectGroupId int32 `json:"WeaponAwakenEffectGroupId"`
|
||||
WeaponAwakenEffectType int32 `json:"WeaponAwakenEffectType"`
|
||||
WeaponAwakenEffectId int32 `json:"WeaponAwakenEffectId"`
|
||||
}
|
||||
|
||||
type WeaponAwakenMaterialGroupRow struct {
|
||||
WeaponAwakenMaterialGroupId int32 `json:"WeaponAwakenMaterialGroupId"`
|
||||
MaterialId int32 `json:"MaterialId"`
|
||||
Count int32 `json:"Count"`
|
||||
SortOrder int32 `json:"SortOrder"`
|
||||
}
|
||||
|
||||
type weaponRarityEnhanceRow struct {
|
||||
RarityType int32 `json:"RarityType"`
|
||||
BaseEnhancementObtainedExp int32 `json:"BaseEnhancementObtainedExp"`
|
||||
SellPriceNumericalFunctionId int32 `json:"SellPriceNumericalFunctionId"`
|
||||
RequiredExpForLevelUpNumericalParameterMapId int32 `json:"RequiredExpForLevelUpNumericalParameterMapId"`
|
||||
EnhancementCostByWeaponNumericalFunctionId int32 `json:"EnhancementCostByWeaponNumericalFunctionId"`
|
||||
EnhancementCostByMaterialNumericalFunctionId int32 `json:"EnhancementCostByMaterialNumericalFunctionId"`
|
||||
MaxLevelNumericalFunctionId int32 `json:"MaxLevelNumericalFunctionId"`
|
||||
EvolutionCostNumericalFunctionId int32 `json:"EvolutionCostNumericalFunctionId"`
|
||||
LimitBreakCostByWeaponNumericalFunctionId int32 `json:"LimitBreakCostByWeaponNumericalFunctionId"`
|
||||
LimitBreakCostByMaterialNumericalFunctionId int32 `json:"LimitBreakCostByMaterialNumericalFunctionId"`
|
||||
MaxSkillLevelNumericalFunctionId int32 `json:"MaxSkillLevelNumericalFunctionId"`
|
||||
SkillEnhancementCostNumericalFunctionId int32 `json:"SkillEnhancementCostNumericalFunctionId"`
|
||||
MaxAbilityLevelNumericalFunctionId int32 `json:"MaxAbilityLevelNumericalFunctionId"`
|
||||
AbilityEnhancementCostNumericalFunctionId int32 `json:"AbilityEnhancementCostNumericalFunctionId"`
|
||||
}
|
||||
|
||||
type WeaponCatalog struct {
|
||||
Weapons map[int32]WeaponMasterRow
|
||||
Materials map[int32]MaterialRow
|
||||
Weapons map[int32]EntityMWeapon
|
||||
Materials map[int32]EntityMMaterial
|
||||
ExpByEnhanceId map[int32][]int32
|
||||
GoldCostByEnhanceId map[int32]NumericalFunc
|
||||
MaxLevelByEnhanceId map[int32]NumericalFunc
|
||||
SellPriceByEnhanceId map[int32]NumericalFunc
|
||||
MedalsByWeaponId map[int32]map[int32]int32 // WeaponId -> ConsumableItemId -> Count
|
||||
EvolutionNextWeaponId map[int32]int32
|
||||
EvolutionOrder map[int32]int32 // WeaponId -> 0-based position in evolution chain
|
||||
EvolutionMaterials map[int32][]WeaponEvolutionMaterialRow // WeaponEvolutionMaterialGroupId -> materials
|
||||
EvolutionOrder map[int32]int32 // WeaponId -> 0-based position in evolution chain
|
||||
EvolutionMaterials map[int32][]EntityMWeaponEvolutionMaterialGroup // WeaponEvolutionMaterialGroupId -> materials
|
||||
EvolutionCostByEnhanceId map[int32]NumericalFunc
|
||||
AbilitySlots map[int32][]int32 // WeaponAbilityGroupId -> slot numbers
|
||||
SkillGroupsByGroupId map[int32][]WeaponSkillGroupRow
|
||||
SkillEnhanceMats map[[2]int32][]WeaponSkillEnhanceMaterialRow // key: [enhancementMaterialId, skillLevel]
|
||||
SkillGroupsByGroupId map[int32][]EntityMWeaponSkillGroup
|
||||
SkillEnhanceMats map[[2]int32][]EntityMWeaponSkillEnhancementMaterial // key: [enhancementMaterialId, skillLevel]
|
||||
SkillMaxLevelByEnhanceId map[int32]NumericalFunc
|
||||
SkillCostByEnhanceId map[int32]NumericalFunc
|
||||
AbilityGroupsByGroupId map[int32][]WeaponAbilityGroupRow
|
||||
AbilityEnhanceMats map[[2]int32][]WeaponAbilityEnhanceMaterialRow // key: [enhancementMaterialId, abilityLevel]
|
||||
AbilityGroupsByGroupId map[int32][]EntityMWeaponAbilityGroup
|
||||
AbilityEnhanceMats map[[2]int32][]EntityMWeaponAbilityEnhancementMaterial // key: [enhancementMaterialId, abilityLevel]
|
||||
AbilityMaxLevelByEnhanceId map[int32]NumericalFunc
|
||||
AbilityCostByEnhanceId map[int32]NumericalFunc
|
||||
EnhanceCostByWeaponByEnhanceId map[int32]NumericalFunc
|
||||
LimitBreakCostByWeaponByEnhanceId map[int32]NumericalFunc
|
||||
LimitBreakCostByMaterialByEnhanceId map[int32]NumericalFunc
|
||||
BaseExpByEnhanceId map[int32]int32
|
||||
ReleaseConditionsByGroupId map[int32][]WeaponStoryReleaseConditionRow
|
||||
ReleaseConditionsByGroupId map[int32][]EntityMWeaponStoryReleaseConditionGroup
|
||||
|
||||
AwakenByWeaponId map[int32]WeaponAwakenRow
|
||||
AwakenMaterialsByGroupId map[int32][]WeaponAwakenMaterialGroupRow
|
||||
AwakenByWeaponId map[int32]EntityMWeaponAwaken
|
||||
AwakenMaterialsByGroupId map[int32][]EntityMWeaponAwakenMaterialGroup
|
||||
}
|
||||
|
||||
func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
weapons, err := utils.ReadJSON[WeaponMasterRow]("EntityMWeaponTable.json")
|
||||
weapons, err := utils.ReadTable[EntityMWeapon]("m_weapon")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon table: %w", err)
|
||||
}
|
||||
|
||||
enhanceRows, err := utils.ReadJSON[weaponSpecificEnhanceRow]("EntityMWeaponSpecificEnhanceTable.json")
|
||||
enhanceRows, err := utils.ReadTable[EntityMWeaponSpecificEnhance]("m_weapon_specific_enhance")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon specific enhance table: %w", err)
|
||||
}
|
||||
|
||||
rarityEnhanceRows, err := utils.ReadJSON[weaponRarityEnhanceRow]("EntityMWeaponRarityTable.json")
|
||||
rarityEnhanceRows, err := utils.ReadTable[EntityMWeaponRarity]("m_weapon_rarity")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon rarity table: %w", err)
|
||||
}
|
||||
@@ -189,51 +72,51 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
return nil, fmt.Errorf("load function resolver: %w", err)
|
||||
}
|
||||
|
||||
exchangeRows, err := utils.ReadJSON[weaponConsumeExchangeRow]("EntityMWeaponConsumeExchangeConsumableItemGroupTable.json")
|
||||
exchangeRows, err := utils.ReadTable[EntityMWeaponConsumeExchangeConsumableItemGroup]("m_weapon_consume_exchange_consumable_item_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon consume exchange table: %w", err)
|
||||
}
|
||||
|
||||
evoGroupRows, err := utils.ReadJSON[WeaponEvolutionGroupRow]("EntityMWeaponEvolutionGroupTable.json")
|
||||
evoGroupRows, err := utils.ReadTable[EntityMWeaponEvolutionGroup]("m_weapon_evolution_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon evolution group table: %w", err)
|
||||
}
|
||||
evoMatRows, err := utils.ReadJSON[WeaponEvolutionMaterialRow]("EntityMWeaponEvolutionMaterialGroupTable.json")
|
||||
evoMatRows, err := utils.ReadTable[EntityMWeaponEvolutionMaterialGroup]("m_weapon_evolution_material_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon evolution material group table: %w", err)
|
||||
}
|
||||
abilityGroupRows, err := utils.ReadJSON[WeaponAbilityGroupRow]("EntityMWeaponAbilityGroupTable.json")
|
||||
abilityGroupRows, err := utils.ReadTable[EntityMWeaponAbilityGroup]("m_weapon_ability_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon ability group table: %w", err)
|
||||
}
|
||||
skillGroupRows, err := utils.ReadJSON[WeaponSkillGroupRow]("EntityMWeaponSkillGroupTable.json")
|
||||
skillGroupRows, err := utils.ReadTable[EntityMWeaponSkillGroup]("m_weapon_skill_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon skill group table: %w", err)
|
||||
}
|
||||
skillMatRows, err := utils.ReadJSON[WeaponSkillEnhanceMaterialRow]("EntityMWeaponSkillEnhancementMaterialTable.json")
|
||||
skillMatRows, err := utils.ReadTable[EntityMWeaponSkillEnhancementMaterial]("m_weapon_skill_enhancement_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon skill enhancement material table: %w", err)
|
||||
}
|
||||
abilityMatRows, err := utils.ReadJSON[WeaponAbilityEnhanceMaterialRow]("EntityMWeaponAbilityEnhancementMaterialTable.json")
|
||||
abilityMatRows, err := utils.ReadTable[EntityMWeaponAbilityEnhancementMaterial]("m_weapon_ability_enhancement_material")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon ability enhancement material table: %w", err)
|
||||
}
|
||||
releaseConditions, err := utils.ReadJSON[WeaponStoryReleaseConditionRow]("EntityMWeaponStoryReleaseConditionGroupTable.json")
|
||||
releaseConditions, err := utils.ReadTable[EntityMWeaponStoryReleaseConditionGroup]("m_weapon_story_release_condition_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon story release condition table: %w", err)
|
||||
}
|
||||
|
||||
awakenRows, err := utils.ReadJSON[WeaponAwakenRow]("EntityMWeaponAwakenTable.json")
|
||||
awakenRows, err := utils.ReadTable[EntityMWeaponAwaken]("m_weapon_awaken")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon awaken table: %w", err)
|
||||
}
|
||||
awakenMatRows, err := utils.ReadJSON[WeaponAwakenMaterialGroupRow]("EntityMWeaponAwakenMaterialGroupTable.json")
|
||||
awakenMatRows, err := utils.ReadTable[EntityMWeaponAwakenMaterialGroup]("m_weapon_awaken_material_group")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load weapon awaken material group table: %w", err)
|
||||
}
|
||||
|
||||
catalog := &WeaponCatalog{
|
||||
Weapons: make(map[int32]WeaponMasterRow, len(weapons)),
|
||||
Weapons: make(map[int32]EntityMWeapon, len(weapons)),
|
||||
Materials: matCatalog.ByType[model.MaterialTypeWeaponEnhancement],
|
||||
ExpByEnhanceId: make(map[int32][]int32, len(enhanceRows)),
|
||||
GoldCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
@@ -242,25 +125,25 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
MedalsByWeaponId: make(map[int32]map[int32]int32),
|
||||
EvolutionNextWeaponId: make(map[int32]int32),
|
||||
EvolutionOrder: make(map[int32]int32),
|
||||
EvolutionMaterials: make(map[int32][]WeaponEvolutionMaterialRow),
|
||||
EvolutionMaterials: make(map[int32][]EntityMWeaponEvolutionMaterialGroup),
|
||||
EvolutionCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
AbilitySlots: make(map[int32][]int32),
|
||||
SkillGroupsByGroupId: make(map[int32][]WeaponSkillGroupRow),
|
||||
SkillEnhanceMats: make(map[[2]int32][]WeaponSkillEnhanceMaterialRow),
|
||||
SkillGroupsByGroupId: make(map[int32][]EntityMWeaponSkillGroup),
|
||||
SkillEnhanceMats: make(map[[2]int32][]EntityMWeaponSkillEnhancementMaterial),
|
||||
SkillMaxLevelByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
SkillCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
AbilityGroupsByGroupId: make(map[int32][]WeaponAbilityGroupRow),
|
||||
AbilityEnhanceMats: make(map[[2]int32][]WeaponAbilityEnhanceMaterialRow),
|
||||
AbilityGroupsByGroupId: make(map[int32][]EntityMWeaponAbilityGroup),
|
||||
AbilityEnhanceMats: make(map[[2]int32][]EntityMWeaponAbilityEnhancementMaterial),
|
||||
AbilityMaxLevelByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
AbilityCostByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
EnhanceCostByWeaponByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
LimitBreakCostByWeaponByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
LimitBreakCostByMaterialByEnhanceId: make(map[int32]NumericalFunc, len(enhanceRows)),
|
||||
BaseExpByEnhanceId: make(map[int32]int32, len(enhanceRows)),
|
||||
ReleaseConditionsByGroupId: make(map[int32][]WeaponStoryReleaseConditionRow),
|
||||
ReleaseConditionsByGroupId: make(map[int32][]EntityMWeaponStoryReleaseConditionGroup),
|
||||
|
||||
AwakenByWeaponId: make(map[int32]WeaponAwakenRow, len(awakenRows)),
|
||||
AwakenMaterialsByGroupId: make(map[int32][]WeaponAwakenMaterialGroupRow),
|
||||
AwakenByWeaponId: make(map[int32]EntityMWeaponAwaken, len(awakenRows)),
|
||||
AwakenMaterialsByGroupId: make(map[int32][]EntityMWeaponAwakenMaterialGroup),
|
||||
}
|
||||
|
||||
for _, w := range weapons {
|
||||
@@ -338,7 +221,7 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
catalog.MedalsByWeaponId[ex.WeaponId][ex.ConsumableItemId] = ex.Count
|
||||
}
|
||||
|
||||
grouped := make(map[int32][]WeaponEvolutionGroupRow)
|
||||
grouped := make(map[int32][]EntityMWeaponEvolutionGroup)
|
||||
for _, row := range evoGroupRows {
|
||||
grouped[row.WeaponEvolutionGroupId] = append(grouped[row.WeaponEvolutionGroupId], row)
|
||||
}
|
||||
@@ -399,7 +282,7 @@ func LoadWeaponCatalog(matCatalog *MaterialCatalog) (*WeaponCatalog, error) {
|
||||
|
||||
// Rarity-based enhancement fallback: for weapons with WeaponSpecificEnhanceId == 0,
|
||||
// use EntityMWeaponRarityTable curves via synthetic enhance IDs (-RarityType).
|
||||
rarityByType := make(map[int32]weaponRarityEnhanceRow, len(rarityEnhanceRows))
|
||||
rarityByType := make(map[int32]EntityMWeaponRarity, len(rarityEnhanceRows))
|
||||
for _, r := range rarityEnhanceRows {
|
||||
rarityByType[r.RarityType] = r
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type ClientPlatform struct {
|
||||
OsType int32 // 1=iOS, 2=Android
|
||||
PlatformType int32 // 1=AppStore, 2=GooglePlay, 8=Amazon
|
||||
}
|
||||
|
||||
const (
|
||||
OsTypeIOS int32 = 1
|
||||
OsTypeAndroid int32 = 2
|
||||
|
||||
PlatformTypeAppStore int32 = 1
|
||||
PlatformTypeGooglePlayStore int32 = 2
|
||||
PlatformTypeAmazonAppStore int32 = 8
|
||||
)
|
||||
|
||||
var DefaultPlatform = ClientPlatform{OsType: OsTypeAndroid, PlatformType: PlatformTypeGooglePlayStore}
|
||||
|
||||
type platformKey struct{}
|
||||
|
||||
func (p ClientPlatform) String() string {
|
||||
os := "unknown"
|
||||
switch p.OsType {
|
||||
case OsTypeIOS:
|
||||
os = "iOS"
|
||||
case OsTypeAndroid:
|
||||
os = "Android"
|
||||
}
|
||||
plat := "unknown"
|
||||
switch p.PlatformType {
|
||||
case PlatformTypeAppStore:
|
||||
plat = "AppStore"
|
||||
case PlatformTypeGooglePlayStore:
|
||||
plat = "GooglePlay"
|
||||
case PlatformTypeAmazonAppStore:
|
||||
plat = "Amazon"
|
||||
}
|
||||
return os + "/" + plat
|
||||
}
|
||||
|
||||
func ClientPlatformFromHeaders(ctx context.Context) ClientPlatform {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return DefaultPlatform
|
||||
}
|
||||
|
||||
p := DefaultPlatform
|
||||
if vals := md.Get("x-apb-os-type"); len(vals) > 0 {
|
||||
if v, err := strconv.ParseInt(vals[0], 10, 32); err == nil {
|
||||
p.OsType = int32(v)
|
||||
}
|
||||
}
|
||||
if vals := md.Get("x-apb-platform-type"); len(vals) > 0 {
|
||||
if v, err := strconv.ParseInt(vals[0], 10, 32); err == nil {
|
||||
p.PlatformType = int32(v)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func NewContextWithPlatform(ctx context.Context, p ClientPlatform) context.Context {
|
||||
return context.WithValue(ctx, platformKey{}, p)
|
||||
}
|
||||
|
||||
func ClientPlatformFromContext(ctx context.Context) ClientPlatform {
|
||||
if p, ok := ctx.Value(platformKey{}).(ClientPlatform); ok {
|
||||
return p
|
||||
}
|
||||
return DefaultPlatform
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (h *QuestHandler) HandleEventQuestSceneProgress(user *store.UserState, ques
|
||||
|
||||
h.applySceneGrants(user, questSceneId, nowMillis)
|
||||
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
if model.QuestResultType(scene.QuestResultType) == model.QuestResultTypeHalfResult {
|
||||
h.clearQuestMissions(user, scene.QuestId, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (h *QuestHandler) HandleExtraQuestSceneProgress(user *store.UserState, ques
|
||||
|
||||
h.applySceneGrants(user, questSceneId, nowMillis)
|
||||
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
if model.QuestResultType(scene.QuestResultType) == model.QuestResultTypeHalfResult {
|
||||
h.clearQuestMissions(user, scene.QuestId, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func BuildGranter(catalog *masterdata.QuestCatalog) *store.PossessionGranter {
|
||||
for i, r := range rows {
|
||||
conds[i] = store.WeaponStoryReleaseCond{
|
||||
StoryIndex: r.StoryIndex,
|
||||
WeaponStoryReleaseConditionType: r.WeaponStoryReleaseConditionType,
|
||||
WeaponStoryReleaseConditionType: model.WeaponStoryReleaseConditionType(r.WeaponStoryReleaseConditionType),
|
||||
ConditionValue: r.ConditionValue,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func (h *QuestHandler) initQuestState(user *store.UserState, questId int32) {
|
||||
}
|
||||
}
|
||||
|
||||
func isMainQuestPlayable(quest masterdata.QuestRow) bool {
|
||||
func isMainQuestPlayable(quest masterdata.EntityMQuest) bool {
|
||||
return !quest.IsRunInTheBackground && quest.IsCountedAsQuest
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ func (h *QuestHandler) isQuestCleared(user *store.UserState, questId int32) bool
|
||||
return quest.QuestStateType == model.UserQuestStateTypeCleared
|
||||
}
|
||||
|
||||
func appendMissionRewards(dst []RewardGrant, src []masterdata.QuestMissionRewardRow) []RewardGrant {
|
||||
func appendMissionRewards(dst []RewardGrant, src []masterdata.EntityMQuestMissionReward) []RewardGrant {
|
||||
for _, r := range src {
|
||||
dst = append(dst, RewardGrant{
|
||||
PossessionType: r.PossessionType,
|
||||
PossessionType: model.PossessionType(r.PossessionType),
|
||||
PossessionId: r.PossessionId,
|
||||
Count: r.Count,
|
||||
})
|
||||
@@ -31,7 +31,7 @@ func appendMissionRewards(dst []RewardGrant, src []masterdata.QuestMissionReward
|
||||
return dst
|
||||
}
|
||||
|
||||
func (h *QuestHandler) firstClearRewardGroupId(user *store.UserState, questDef masterdata.QuestRow) int32 {
|
||||
func (h *QuestHandler) firstClearRewardGroupId(user *store.UserState, questDef masterdata.EntityMQuest) int32 {
|
||||
rewardGroupId := questDef.QuestFirstClearRewardGroupId
|
||||
for _, switchRow := range h.FirstClearRewardSwitchesByQuestId[questDef.QuestId] {
|
||||
if h.isQuestCleared(user, switchRow.SwitchConditionClearQuestId) {
|
||||
@@ -57,7 +57,7 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
outcome.FirstClearRewards = append(outcome.FirstClearRewards, RewardGrant{
|
||||
PossessionType: reward.PossessionType,
|
||||
PossessionType: model.PossessionType(reward.PossessionType),
|
||||
PossessionId: reward.PossessionId,
|
||||
Count: reward.Count,
|
||||
})
|
||||
@@ -67,7 +67,7 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
if user.MainQuest.CurrentQuestFlowType == int32(model.QuestFlowTypeReplayFlow) && questDef.QuestReplayFlowRewardGroupId > 0 {
|
||||
for _, reward := range h.ReplayFlowRewardsByGroupId[questDef.QuestReplayFlowRewardGroupId] {
|
||||
outcome.ReplayFlowFirstClearRewards = append(outcome.ReplayFlowFirstClearRewards, RewardGrant{
|
||||
PossessionType: reward.PossessionType,
|
||||
PossessionType: model.PossessionType(reward.PossessionType),
|
||||
PossessionId: reward.PossessionId,
|
||||
Count: reward.Count,
|
||||
})
|
||||
@@ -78,7 +78,7 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
regularMissionCount := 0
|
||||
for _, questMissionId := range h.MissionIdsByQuestId[questId] {
|
||||
missionDef, ok := h.MissionById[questMissionId]
|
||||
if !ok || missionDef.QuestMissionConditionType == model.QuestMissionConditionTypeComplete {
|
||||
if !ok || model.QuestMissionConditionType(missionDef.QuestMissionConditionType) == model.QuestMissionConditionTypeComplete {
|
||||
continue
|
||||
}
|
||||
regularMissionCount++
|
||||
@@ -103,7 +103,7 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
if allRegularWillClear {
|
||||
for _, questMissionId := range h.MissionIdsByQuestId[questId] {
|
||||
missionDef, ok := h.MissionById[questMissionId]
|
||||
if !ok || missionDef.QuestMissionConditionType != model.QuestMissionConditionTypeComplete {
|
||||
if !ok || model.QuestMissionConditionType(missionDef.QuestMissionConditionType) != model.QuestMissionConditionTypeComplete {
|
||||
continue
|
||||
}
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: questMissionId}
|
||||
@@ -122,7 +122,7 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (h *QuestHandler) computeDropRewards(questDef masterdata.QuestRow) []RewardGrant {
|
||||
func (h *QuestHandler) computeDropRewards(questDef masterdata.EntityMQuest) []RewardGrant {
|
||||
if questDef.QuestPickupRewardGroupId == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (h *QuestHandler) computeDropRewards(questDef masterdata.QuestRow) []Reward
|
||||
for _, dropId := range h.PickupRewardIdsByGroupId[questDef.QuestPickupRewardGroupId] {
|
||||
if bdr, ok := h.BattleDropRewardById[dropId]; ok {
|
||||
drops = append(drops, RewardGrant{
|
||||
PossessionType: bdr.PossessionType,
|
||||
PossessionType: model.PossessionType(bdr.PossessionType),
|
||||
PossessionId: bdr.PossessionId,
|
||||
Count: bdr.Count,
|
||||
})
|
||||
@@ -257,7 +257,7 @@ func (h *QuestHandler) applyQuestRewards(user *store.UserState, questId int32, n
|
||||
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
h.applyRewardPossession(user, reward.PossessionType, reward.PossessionId, reward.Count, nowMillis)
|
||||
h.applyRewardPossession(user, model.PossessionType(reward.PossessionType), reward.PossessionId, reward.Count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ func (h *QuestHandler) grantWeaponStoryUnlocksForQuestScene(user *store.UserStat
|
||||
}
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
if reward.PossessionType != model.PossessionTypeWeapon {
|
||||
if model.PossessionType(reward.PossessionType) != model.PossessionTypeWeapon {
|
||||
continue
|
||||
}
|
||||
weaponId := reward.PossessionId
|
||||
@@ -375,7 +375,7 @@ func (h *QuestHandler) grantWeaponStoryUnlocksForQuestScene(user *store.UserStat
|
||||
}
|
||||
groupId := weapon.WeaponStoryReleaseConditionGroupId
|
||||
for _, cond := range h.ReleaseConditionsByGroupId[groupId] {
|
||||
if cond.WeaponStoryReleaseConditionType == model.WeaponStoryReleaseConditionTypeAcquisition && cond.ConditionValue == 0 {
|
||||
if model.WeaponStoryReleaseConditionType(cond.WeaponStoryReleaseConditionType) == model.WeaponStoryReleaseConditionTypeAcquisition && cond.ConditionValue == 0 {
|
||||
if h.grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis) {
|
||||
changedIds = append(changedIds, weaponId)
|
||||
}
|
||||
@@ -387,7 +387,7 @@ func (h *QuestHandler) grantWeaponStoryUnlocksForQuestScene(user *store.UserStat
|
||||
if resultType == model.QuestResultTypeFullResult {
|
||||
for groupId, conditions := range h.ReleaseConditionsByGroupId {
|
||||
for _, cond := range conditions {
|
||||
if cond.WeaponStoryReleaseConditionType == model.WeaponStoryReleaseConditionTypeQuestClear && cond.ConditionValue == questId {
|
||||
if model.WeaponStoryReleaseConditionType(cond.WeaponStoryReleaseConditionType) == model.WeaponStoryReleaseConditionTypeQuestClear && cond.ConditionValue == questId {
|
||||
for _, weaponId := range h.WeaponIdsByReleaseConditionGroupId[groupId] {
|
||||
if h.grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis) {
|
||||
changedIds = append(changedIds, weaponId)
|
||||
|
||||
@@ -15,7 +15,7 @@ func (h *QuestHandler) applySceneGrants(user *store.UserState, questSceneId int3
|
||||
return
|
||||
}
|
||||
for _, g := range grants {
|
||||
h.applyRewardPossession(user, g.PossessionType, g.PossessionId, g.Count, nowMillis)
|
||||
h.applyRewardPossession(user, model.PossessionType(g.PossessionType), g.PossessionId, g.Count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func (h *QuestHandler) HandleMainQuestSceneProgress(user *store.UserState, quest
|
||||
}
|
||||
|
||||
if isMainQuestPlayable(quest) {
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
if model.QuestResultType(scene.QuestResultType) == model.QuestResultTypeHalfResult {
|
||||
nowMillis := gametime.NowMillis()
|
||||
h.clearQuestMissions(user, quest.QuestId, nowMillis)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ type assetResolution struct {
|
||||
Candidates []assetCandidate
|
||||
}
|
||||
|
||||
type assetResolver struct{}
|
||||
type assetResolver struct {
|
||||
baseDir string
|
||||
}
|
||||
|
||||
func newRevisionTracker() *revisionTracker {
|
||||
return &revisionTracker{
|
||||
@@ -28,8 +30,8 @@ func newRevisionTracker() *revisionTracker {
|
||||
}
|
||||
}
|
||||
|
||||
func newAssetResolver() *assetResolver {
|
||||
return &assetResolver{}
|
||||
func newAssetResolver(baseDir string) *assetResolver {
|
||||
return &assetResolver{baseDir: baseDir}
|
||||
}
|
||||
|
||||
func normalizeClientAddr(remoteAddr string) string {
|
||||
@@ -73,7 +75,7 @@ func (r *assetResolver) Resolve(objectId, assetType, activeRevision string) (ass
|
||||
resolution := assetResolution{ActiveRevision: activeRevision}
|
||||
revision := activeRevision
|
||||
|
||||
candidates, listSize, ok := objectIdToFilePathCandidates(revision, assetType, objectId)
|
||||
candidates, listSize, ok := objectIdToFilePathCandidates(r.baseDir, revision, assetType, objectId)
|
||||
if ok && len(candidates) > 0 {
|
||||
resolution.ListRevision = revision
|
||||
resolution.ListSize = listSize
|
||||
@@ -94,6 +96,6 @@ func (r *assetResolver) Prewarm(activeRevision string) {
|
||||
if activeRevision == "" {
|
||||
return
|
||||
}
|
||||
_, _ = loadListBinIndex(activeRevision)
|
||||
_ = loadInfoIndex(activeRevision)
|
||||
_, _ = loadListBinIndex(r.baseDir, activeRevision)
|
||||
_ = loadInfoIndex(r.baseDir, activeRevision)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type BannerServiceServer struct {
|
||||
@@ -44,6 +43,5 @@ func (s *BannerServiceServer) GetMamaBanner(ctx context.Context, req *pb.GetMama
|
||||
TermLimitedGacha: termLimited,
|
||||
LatestChapterGacha: latestChapter,
|
||||
IsExistUnreadPop: false,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type BattleServiceServer struct {
|
||||
@@ -22,7 +21,7 @@ func NewBattleServiceServer(users store.UserRepository, sessions store.SessionRe
|
||||
|
||||
func (s *BattleServiceServer) StartWave(ctx context.Context, req *pb.StartWaveRequest) (*pb.StartWaveResponse, error) {
|
||||
log.Printf("[BattleService] StartWave: userParty=%d npcParty=%d", len(req.UserPartyInitialInfoList), len(req.NpcPartyInitialInfoList))
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.Battle.IsActive = true
|
||||
user.Battle.StartCount++
|
||||
@@ -30,15 +29,13 @@ func (s *BattleServiceServer) StartWave(ctx context.Context, req *pb.StartWaveRe
|
||||
user.Battle.LastUserPartyCount = int32(len(req.UserPartyInitialInfoList))
|
||||
user.Battle.LastNpcPartyCount = int32(len(req.NpcPartyInitialInfoList))
|
||||
})
|
||||
return &pb.StartWaveResponse{
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
return &pb.StartWaveResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *BattleServiceServer) FinishWave(ctx context.Context, req *pb.FinishWaveRequest) (*pb.FinishWaveResponse, error) {
|
||||
log.Printf("[BattleService] FinishWave: battleBinary=%d userParty=%d npcParty=%d elapsedFrames=%d",
|
||||
len(req.BattleBinary), len(req.UserPartyResultInfoList), len(req.NpcPartyResultInfoList), req.ElapsedFrameCount)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.Battle.IsActive = false
|
||||
user.Battle.FinishCount++
|
||||
@@ -48,7 +45,5 @@ func (s *BattleServiceServer) FinishWave(ctx context.Context, req *pb.FinishWave
|
||||
user.Battle.LastBattleBinarySize = int32(len(req.BattleBinary))
|
||||
user.Battle.LastElapsedFrameCount = req.ElapsedFrameCount
|
||||
})
|
||||
return &pb.FinishWaveResponse{
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
return &pb.FinishWaveResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type CageOrnamentServiceServer struct {
|
||||
@@ -32,9 +31,9 @@ func (s *CageOrnamentServiceServer) ReceiveReward(ctx context.Context, req *pb.R
|
||||
log.Fatalf("[CageOrnamentService] ReceiveReward: no reward for cageOrnamentId=%d", req.CageOrnamentId)
|
||||
}
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.CageOrnamentRewards[req.CageOrnamentId] = store.CageOrnamentRewardState{
|
||||
CageOrnamentId: req.CageOrnamentId,
|
||||
AcquisitionDatetime: nowMillis,
|
||||
@@ -43,17 +42,6 @@ func (s *CageOrnamentServiceServer) ReceiveReward(ctx context.Context, req *pb.R
|
||||
s.granter.GrantFull(user, model.PossessionType(reward.PossessionType), reward.PossessionId, reward.Count, nowMillis)
|
||||
})
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(user,
|
||||
[]string{
|
||||
"IUserMaterial", "IUserConsumableItem", "IUserGem",
|
||||
"IUserCostume", "IUserCostumeActiveSkill", "IUserCharacter",
|
||||
"IUserWeapon", "IUserWeaponSkill", "IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
"IUserCageOrnamentReward",
|
||||
},
|
||||
))
|
||||
userdata.AddWeaponStoryDiff(diff, user, s.granter.DrainChangedStoryWeaponIds())
|
||||
|
||||
return &pb.ReceiveRewardResponse{
|
||||
CageOrnamentReward: []*pb.CageOrnamentReward{
|
||||
{
|
||||
@@ -62,16 +50,15 @@ func (s *CageOrnamentServiceServer) ReceiveReward(ctx context.Context, req *pb.R
|
||||
Count: reward.Count,
|
||||
},
|
||||
},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *CageOrnamentServiceServer) RecordAccess(ctx context.Context, req *pb.RecordAccessRequest) (*pb.RecordAccessResponse, error) {
|
||||
log.Printf("[CageOrnamentService] RecordAccess: cageOrnamentId=%d", req.CageOrnamentId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if _, exists := user.CageOrnamentRewards[req.CageOrnamentId]; !exists {
|
||||
user.CageOrnamentRewards[req.CageOrnamentId] = store.CageOrnamentRewardState{
|
||||
CageOrnamentId: req.CageOrnamentId,
|
||||
@@ -81,11 +68,5 @@ func (s *CageOrnamentServiceServer) RecordAccess(ctx context.Context, req *pb.Re
|
||||
}
|
||||
})
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(user,
|
||||
[]string{"IUserCageOrnamentReward"},
|
||||
))
|
||||
|
||||
return &pb.RecordAccessResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.RecordAccessResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type CharacterServiceServer struct {
|
||||
@@ -26,7 +25,7 @@ func NewCharacterServiceServer(users store.UserRepository, sessions store.Sessio
|
||||
func (s *CharacterServiceServer) Rebirth(ctx context.Context, req *pb.RebirthRequest) (*pb.RebirthResponse, error) {
|
||||
log.Printf("[CharacterService] Rebirth: characterId=%d rebirthCount=%d", req.CharacterId, req.RebirthCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
stepGroupId, ok := s.catalog.StepGroupByCharacterId[req.CharacterId]
|
||||
@@ -35,11 +34,7 @@ func (s *CharacterServiceServer) Rebirth(ctx context.Context, req *pb.RebirthReq
|
||||
return &pb.RebirthResponse{}, nil
|
||||
}
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserMaterial", oldUser, userdata.SortedMaterialRecords, []string{"userId", "materialId"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
current := user.CharacterRebirths[req.CharacterId]
|
||||
currentCount := current.RebirthCount
|
||||
targetCount := currentCount + req.RebirthCount
|
||||
@@ -77,9 +72,5 @@ func (s *CharacterServiceServer) Rebirth(ctx context.Context, req *pb.RebirthReq
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rebirthTables := []string{"IUserCharacterRebirth", "IUserMaterial", "IUserConsumableItem"}
|
||||
tables := userdata.ProjectTables(snapshot, rebirthTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.RebirthResponse{DiffUserData: diff}, nil
|
||||
return &pb.RebirthResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type CharacterBoardServiceServer struct {
|
||||
@@ -25,14 +24,9 @@ func NewCharacterBoardServiceServer(users store.UserRepository, sessions store.S
|
||||
func (s *CharacterBoardServiceServer) ReleasePanel(ctx context.Context, req *pb.ReleasePanelRequest) (*pb.ReleasePanelResponse, error) {
|
||||
log.Printf("[CharacterBoardService] ReleasePanel: panelIds=%v", req.CharacterBoardPanelId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserMaterial", oldUser, userdata.SortedMaterialRecords, []string{"userId", "materialId"}).
|
||||
Track("IUserConsumableItem", oldUser, userdata.SortedConsumableItemRecords, []string{"userId", "consumableItemId"})
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, panelId := range req.CharacterBoardPanelId {
|
||||
panel, ok := s.catalog.PanelById[panelId]
|
||||
if !ok {
|
||||
@@ -46,28 +40,17 @@ func (s *CharacterBoardServiceServer) ReleasePanel(ctx context.Context, req *pb.
|
||||
}
|
||||
})
|
||||
|
||||
boardTables := []string{
|
||||
"IUserCharacterBoard",
|
||||
"IUserCharacterBoardAbility",
|
||||
"IUserCharacterBoardStatusUp",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
"IUserGem",
|
||||
}
|
||||
tables := userdata.ProjectTables(user, boardTables)
|
||||
diff := tracker.Apply(user, tables)
|
||||
|
||||
return &pb.ReleasePanelResponse{DiffUserData: diff}, nil
|
||||
return &pb.ReleasePanelResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CharacterBoardServiceServer) consumeCosts(user *store.UserState, panel masterdata.CharacterBoardPanelRow) {
|
||||
func (s *CharacterBoardServiceServer) consumeCosts(user *store.UserState, panel masterdata.EntityMCharacterBoardPanel) {
|
||||
costs := s.catalog.ReleaseCostsByGroupId[panel.CharacterBoardPanelReleasePossessionGroupId]
|
||||
for _, cost := range costs {
|
||||
store.DeductPossession(user, model.PossessionType(cost.PossessionType), cost.PossessionId, cost.Count)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CharacterBoardServiceServer) setReleaseBit(user *store.UserState, panel masterdata.CharacterBoardPanelRow) {
|
||||
func (s *CharacterBoardServiceServer) setReleaseBit(user *store.UserState, panel masterdata.EntityMCharacterBoardPanel) {
|
||||
boardId := panel.CharacterBoardId
|
||||
board := user.CharacterBoards[boardId]
|
||||
board.CharacterBoardId = boardId
|
||||
@@ -90,7 +73,7 @@ func (s *CharacterBoardServiceServer) setReleaseBit(user *store.UserState, panel
|
||||
user.CharacterBoards[boardId] = board
|
||||
}
|
||||
|
||||
func (s *CharacterBoardServiceServer) applyEffects(user *store.UserState, panel masterdata.CharacterBoardPanelRow) {
|
||||
func (s *CharacterBoardServiceServer) applyEffects(user *store.UserState, panel masterdata.EntityMCharacterBoardPanel) {
|
||||
effects := s.catalog.ReleaseEffectsByGroupId[panel.CharacterBoardPanelReleaseEffectGroupId]
|
||||
for _, eff := range effects {
|
||||
switch model.CharacterBoardEffectType(eff.CharacterBoardEffectType) {
|
||||
@@ -102,7 +85,7 @@ func (s *CharacterBoardServiceServer) applyEffects(user *store.UserState, panel
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CharacterBoardServiceServer) applyAbilityEffect(user *store.UserState, eff masterdata.CharacterBoardReleaseEffectRow) {
|
||||
func (s *CharacterBoardServiceServer) applyAbilityEffect(user *store.UserState, eff masterdata.EntityMCharacterBoardPanelReleaseEffectGroup) {
|
||||
ability, ok := s.catalog.AbilityById[eff.CharacterBoardEffectId]
|
||||
if !ok {
|
||||
log.Printf("[CharacterBoardService] unknown abilityId=%d", eff.CharacterBoardEffectId)
|
||||
@@ -127,7 +110,7 @@ func (s *CharacterBoardServiceServer) applyAbilityEffect(user *store.UserState,
|
||||
user.CharacterBoardAbilities[key] = state
|
||||
}
|
||||
|
||||
func (s *CharacterBoardServiceServer) applyStatusUpEffect(user *store.UserState, eff masterdata.CharacterBoardReleaseEffectRow) {
|
||||
func (s *CharacterBoardServiceServer) applyStatusUpEffect(user *store.UserState, eff masterdata.EntityMCharacterBoardPanelReleaseEffectGroup) {
|
||||
statusUp, ok := s.catalog.StatusUpById[eff.CharacterBoardEffectId]
|
||||
if !ok {
|
||||
log.Printf("[CharacterBoardService] unknown statusUpId=%d", eff.CharacterBoardEffectId)
|
||||
|
||||
@@ -2,12 +2,10 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
|
||||
@@ -28,7 +26,7 @@ func NewCharacterViewerServiceServer(users store.UserRepository, sessions store.
|
||||
func (s *CharacterViewerServiceServer) CharacterViewerTop(ctx context.Context, _ *emptypb.Empty) (*pb.CharacterViewerTopResponse, error) {
|
||||
log.Printf("[CharacterViewerService] CharacterViewerTop")
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("CharacterViewerTop: no user for userId=%d: %v", userId, err))
|
||||
@@ -37,32 +35,7 @@ func (s *CharacterViewerServiceServer) CharacterViewerTop(ctx context.Context, _
|
||||
released := s.catalog.ReleasedFieldIds(user)
|
||||
log.Printf("[CharacterViewerService] released %d fields for user %d", len(released), userId)
|
||||
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(released))
|
||||
for _, fieldId := range released {
|
||||
records = append(records, map[string]any{
|
||||
"userId": userId,
|
||||
"characterViewerFieldId": fieldId,
|
||||
"releaseDatetime": now,
|
||||
"latestVersion": 0,
|
||||
})
|
||||
}
|
||||
|
||||
payload := "[]"
|
||||
if len(records) > 0 {
|
||||
data, _ := json.Marshal(records)
|
||||
payload = string(data)
|
||||
}
|
||||
|
||||
diff := map[string]*pb.DiffData{
|
||||
"IUserCharacterViewerField": {
|
||||
UpdateRecordsJson: payload,
|
||||
DeleteKeysJson: "[]",
|
||||
},
|
||||
}
|
||||
|
||||
return &pb.CharacterViewerTopResponse{
|
||||
ReleaseCharacterViewerFieldId: released,
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -9,17 +9,10 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
const companionMaxLevel = int32(50)
|
||||
|
||||
var companionDiffTables = []string{
|
||||
"IUserCompanion",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type CompanionServiceServer struct {
|
||||
pb.UnimplementedCompanionServiceServer
|
||||
users store.UserRepository
|
||||
@@ -35,10 +28,10 @@ func NewCompanionServiceServer(users store.UserRepository, sessions store.Sessio
|
||||
func (s *CompanionServiceServer) Enhance(ctx context.Context, req *pb.CompanionEnhanceRequest) (*pb.CompanionEnhanceResponse, error) {
|
||||
log.Printf("[CompanionService] Enhance: uuid=%s addLevel=%d", req.UserCompanionUuid, req.AddLevelCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
companion, ok := user.Companions[req.UserCompanionUuid]
|
||||
if !ok {
|
||||
log.Printf("[CompanionService] Enhance: companion uuid=%s not found", req.UserCompanionUuid)
|
||||
@@ -77,9 +70,5 @@ func (s *CompanionServiceServer) Enhance(ctx context.Context, req *pb.CompanionE
|
||||
return nil, fmt.Errorf("companion enhance: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, companionDiffTables))
|
||||
|
||||
return &pb.CompanionEnhanceResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.CompanionEnhanceResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"log"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -14,7 +13,7 @@ type ConfigServiceServer struct {
|
||||
pb.UnimplementedConfigServiceServer
|
||||
GrpcHost string
|
||||
GrpcPort int32
|
||||
OctoURL string // HTTP base URL for Octo (list + assets); client uses this instead of default resources.app.nierreincarnation.com
|
||||
OctoURL string
|
||||
}
|
||||
|
||||
func NewConfigServiceServer(host string, port int32, octoURL string) *ConfigServiceServer {
|
||||
@@ -42,6 +41,5 @@ func (s *ConfigServiceServer) GetReviewServerConfig(ctx context.Context, _ *empt
|
||||
MasterData: &pb.MasterDataConfig{
|
||||
UrlFormat: s.OctoURL + "/master-data/%s",
|
||||
},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type ConsumableItemServiceServer struct {
|
||||
@@ -26,13 +25,9 @@ func NewConsumableItemServiceServer(users store.UserRepository, sessions store.S
|
||||
func (s *ConsumableItemServiceServer) Sell(ctx context.Context, req *pb.ConsumableItemSellRequest) (*pb.ConsumableItemSellResponse, error) {
|
||||
log.Printf("[ConsumableItemService] Sell: %d item(s)", len(req.ConsumableItemPossession))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserConsumableItem", oldUser, userdata.SortedConsumableItemRecords, []string{"userId", "consumableItemId"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
for _, item := range req.ConsumableItemPossession {
|
||||
row, ok := s.catalog.All[item.ConsumableItemId]
|
||||
@@ -66,10 +61,5 @@ func (s *ConsumableItemServiceServer) Sell(ctx context.Context, req *pb.Consumab
|
||||
return nil, fmt.Errorf("consumable item sell: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(snapshot, []string{"IUserConsumableItem"})
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.ConsumableItemSellResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.ConsumableItemSellResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type ContentsStoryServiceServer struct {
|
||||
@@ -24,19 +23,15 @@ func NewContentsStoryServiceServer(users store.UserRepository, sessions store.Se
|
||||
func (s *ContentsStoryServiceServer) RegisterPlayed(ctx context.Context, req *pb.ContentsStoryRegisterPlayedRequest) (*pb.ContentsStoryRegisterPlayedResponse, error) {
|
||||
log.Printf("[ContentsStoryService] RegisterPlayed: contentsStoryId=%d", req.ContentsStoryId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.ContentsStories[req.ContentsStoryId] = nowMillis
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserContentsStory"}))
|
||||
|
||||
return &pb.ContentsStoryRegisterPlayedResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.ContentsStoryRegisterPlayedResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -14,15 +14,8 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
var costumeDiffTables = []string{
|
||||
"IUserCostume",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type CostumeServiceServer struct {
|
||||
pb.UnimplementedCostumeServiceServer
|
||||
users store.UserRepository
|
||||
@@ -38,10 +31,10 @@ func NewCostumeServiceServer(users store.UserRepository, sessions store.SessionR
|
||||
func (s *CostumeServiceServer) Enhance(ctx context.Context, req *pb.EnhanceRequest) (*pb.EnhanceResponse, error) {
|
||||
log.Printf("[CostumeService] Enhance: uuid=%s materials=%v", req.UserCostumeUuid, req.Materials)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] Enhance: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -98,30 +91,19 @@ func (s *CostumeServiceServer) Enhance(ctx context.Context, req *pb.EnhanceReque
|
||||
return nil, fmt.Errorf("costume enhance: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, costumeDiffTables))
|
||||
|
||||
return &pb.EnhanceResponse{
|
||||
IsGreatSuccess: false,
|
||||
SurplusEnhanceMaterial: map[int32]int32{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var awakenDiffTables = []string{
|
||||
"IUserCostume",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
"IUserCostumeAwakenStatusUp",
|
||||
"IUserThought",
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) Awaken(ctx context.Context, req *pb.AwakenRequest) (*pb.AwakenResponse, error) {
|
||||
log.Printf("[CostumeService] Awaken: uuid=%s materials=%v", req.UserCostumeUuid, req.Materials)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] Awaken: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -179,11 +161,7 @@ func (s *CostumeServiceServer) Awaken(ctx context.Context, req *pb.AwakenRequest
|
||||
return nil, fmt.Errorf("costume awaken: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, awakenDiffTables))
|
||||
|
||||
return &pb.AwakenResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.AwakenResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) applyAwakenStatusUp(user *store.UserState, costumeUuid string, statusUpGroupId int32, nowMillis int64) {
|
||||
@@ -245,19 +223,13 @@ func (s *CostumeServiceServer) applyAwakenItemAcquire(user *store.UserState, ite
|
||||
log.Printf("[CostumeService] Awaken: granted thought id=%d", acq.PossessionId)
|
||||
}
|
||||
|
||||
var activeSkillDiffTables = []string{
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) EnhanceActiveSkill(ctx context.Context, req *pb.EnhanceActiveSkillRequest) (*pb.EnhanceActiveSkillResponse, error) {
|
||||
log.Printf("[CostumeService] EnhanceActiveSkill: uuid=%s addLevel=%d", req.UserCostumeUuid, req.AddLevelCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] EnhanceActiveSkill: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -332,20 +304,16 @@ func (s *CostumeServiceServer) EnhanceActiveSkill(ctx context.Context, req *pb.E
|
||||
return nil, fmt.Errorf("costume enhance active skill: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, activeSkillDiffTables))
|
||||
|
||||
return &pb.EnhanceActiveSkillResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.EnhanceActiveSkillResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) LimitBreak(ctx context.Context, req *pb.LimitBreakRequest) (*pb.LimitBreakResponse, error) {
|
||||
log.Printf("[CostumeService] LimitBreak: uuid=%s materials=%v", req.UserCostumeUuid, req.Materials)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] LimitBreak: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -389,30 +357,16 @@ func (s *CostumeServiceServer) LimitBreak(ctx context.Context, req *pb.LimitBrea
|
||||
return nil, fmt.Errorf("costume limit break: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, costumeDiffTables))
|
||||
|
||||
return &pb.LimitBreakResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var lotteryEffectDiffTables = []string{
|
||||
"IUserCostume",
|
||||
"IUserCostumeLotteryEffect",
|
||||
"IUserCostumeLotteryEffectAbility",
|
||||
"IUserCostumeLotteryEffectStatusUp",
|
||||
"IUserCostumeLotteryEffectPending",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
return &pb.LimitBreakResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) UnlockLotteryEffectSlot(ctx context.Context, req *pb.UnlockLotteryEffectSlotRequest) (*pb.UnlockLotteryEffectSlotResponse, error) {
|
||||
log.Printf("[CostumeService] UnlockLotteryEffectSlot: uuid=%s slot=%d", req.UserCostumeUuid, req.SlotNumber)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] UnlockLotteryEffectSlot: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -458,26 +412,16 @@ func (s *CostumeServiceServer) UnlockLotteryEffectSlot(ctx context.Context, req
|
||||
return nil, fmt.Errorf("costume unlock lottery effect slot: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, lotteryEffectDiffTables))
|
||||
|
||||
return &pb.UnlockLotteryEffectSlotResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.UnlockLotteryEffectSlotResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) DrawLotteryEffect(ctx context.Context, req *pb.DrawLotteryEffectRequest) (*pb.DrawLotteryEffectResponse, error) {
|
||||
log.Printf("[CostumeService] DrawLotteryEffect: uuid=%s slot=%d", req.UserCostumeUuid, req.SlotNumber)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserMaterial", oldUser, userdata.SortedMaterialRecords, []string{"userId", "materialId"}).
|
||||
Track("IUserConsumableItem", oldUser, userdata.SortedConsumableItemRecords, []string{"userId", "consumableItemId"}).
|
||||
Track("IUserCostumeLotteryEffectPending", oldUser, userdata.SortedCostumeLotteryEffectPendingRecords, []string{"userId", "userCostumeUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
costume, ok := user.Costumes[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] DrawLotteryEffect: costume uuid=%s not found", req.UserCostumeUuid)
|
||||
@@ -514,7 +458,7 @@ func (s *CostumeServiceServer) DrawLotteryEffect(ctx context.Context, req *pb.Dr
|
||||
totalWeight += row.Weight
|
||||
}
|
||||
roll := rand.Int31n(totalWeight)
|
||||
var picked masterdata.CostumeLotteryEffectOddsRow
|
||||
var picked masterdata.EntityMCostumeLotteryEffectOddsGroup
|
||||
for _, row := range oddsPool {
|
||||
roll -= row.Weight
|
||||
if roll < 0 {
|
||||
@@ -550,24 +494,16 @@ func (s *CostumeServiceServer) DrawLotteryEffect(ctx context.Context, req *pb.Dr
|
||||
return nil, fmt.Errorf("costume draw lottery effect: %w", err)
|
||||
}
|
||||
|
||||
diff := tracker.Apply(snapshot, userdata.ProjectTables(snapshot, lotteryEffectDiffTables))
|
||||
|
||||
return &pb.DrawLotteryEffectResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.DrawLotteryEffectResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *CostumeServiceServer) ConfirmLotteryEffect(ctx context.Context, req *pb.ConfirmLotteryEffectRequest) (*pb.ConfirmLotteryEffectResponse, error) {
|
||||
log.Printf("[CostumeService] ConfirmLotteryEffect: uuid=%s accept=%v", req.UserCostumeUuid, req.IsAccept)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserCostumeLotteryEffectPending", oldUser, userdata.SortedCostumeLotteryEffectPendingRecords, []string{"userId", "userCostumeUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
pending, ok := user.CostumeLotteryEffectPending[req.UserCostumeUuid]
|
||||
if !ok {
|
||||
log.Printf("[CostumeService] ConfirmLotteryEffect: no pending for uuid=%s", req.UserCostumeUuid)
|
||||
@@ -596,9 +532,5 @@ func (s *CostumeServiceServer) ConfirmLotteryEffect(ctx context.Context, req *pb
|
||||
return nil, fmt.Errorf("costume confirm lottery effect: %w", err)
|
||||
}
|
||||
|
||||
diff := tracker.Apply(snapshot, userdata.ProjectTables(snapshot, lotteryEffectDiffTables))
|
||||
|
||||
return &pb.ConfirmLotteryEffectResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.ConfirmLotteryEffectResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func (s *DataServiceServer) GetUserDataNameV2(ctx context.Context, _ *emptypb.Em
|
||||
func (s *DataServiceServer) GetUserData(ctx context.Context, req *pb.UserDataGetRequest) (*pb.UserDataGetResponse, error) {
|
||||
log.Printf("[DataService] GetUserData: tables=%v", req.TableName)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type DeckServiceServer struct {
|
||||
@@ -23,26 +22,23 @@ func NewDeckServiceServer(users store.UserRepository, sessions store.SessionRepo
|
||||
|
||||
func (s *DeckServiceServer) UpdateName(ctx context.Context, req *pb.UpdateNameRequest) (*pb.UpdateNameResponse, error) {
|
||||
log.Printf("[DeckService] UpdateName: deckType=%d deckNumber=%d name=%q", req.DeckType, req.UserDeckNumber, req.Name)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
deckKey := store.DeckKey{DeckType: model.DeckType(req.DeckType), UserDeckNumber: req.UserDeckNumber}
|
||||
deck := user.Decks[deckKey]
|
||||
deck.Name = req.Name
|
||||
user.Decks[deckKey] = deck
|
||||
})
|
||||
|
||||
result := userdata.ProjectTables(user, []string{"IUserDeck"})
|
||||
return &pb.UpdateNameResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(result),
|
||||
}, nil
|
||||
return &pb.UpdateNameResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *DeckServiceServer) RefreshDeckPower(ctx context.Context, req *pb.RefreshDeckPowerRequest) (*pb.RefreshDeckPowerResponse, error) {
|
||||
log.Printf("[DeckService] RefreshDeckPower: deckType=%d deckNumber=%d", req.DeckType, req.UserDeckNumber)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if req.DeckPower == nil {
|
||||
log.Printf("[DeckService] RefreshDeckPower: deckPower is nil")
|
||||
return
|
||||
@@ -81,19 +77,14 @@ func (s *DeckServiceServer) RefreshDeckPower(ctx context.Context, req *pb.Refres
|
||||
}
|
||||
})
|
||||
|
||||
result := userdata.ProjectTables(user, []string{
|
||||
"IUserDeck", "IUserDeckCharacter", "IUserDeckTypeNote",
|
||||
})
|
||||
return &pb.RefreshDeckPowerResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(result),
|
||||
}, nil
|
||||
return &pb.RefreshDeckPowerResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *DeckServiceServer) RefreshMultiDeckPower(ctx context.Context, req *pb.RefreshMultiDeckPowerRequest) (*pb.RefreshMultiDeckPowerResponse, error) {
|
||||
log.Printf("[DeckService] RefreshMultiDeckPower: %d entries", len(req.DeckPowerInfo))
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, info := range req.DeckPowerInfo {
|
||||
if info.DeckPower == nil {
|
||||
continue
|
||||
@@ -133,12 +124,7 @@ func (s *DeckServiceServer) RefreshMultiDeckPower(ctx context.Context, req *pb.R
|
||||
}
|
||||
})
|
||||
|
||||
result := userdata.ProjectTables(user, []string{
|
||||
"IUserDeck", "IUserDeckCharacter", "IUserDeckTypeNote",
|
||||
})
|
||||
return &pb.RefreshMultiDeckPowerResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(result),
|
||||
}, nil
|
||||
return &pb.RefreshMultiDeckPowerResponse{}, nil
|
||||
}
|
||||
|
||||
func deckSlotsFromProto(deck *pb.Deck) []store.DeckCharacterInput {
|
||||
@@ -171,47 +157,23 @@ func (s *DeckServiceServer) ReplaceDeck(ctx context.Context, req *pb.ReplaceDeck
|
||||
i+1, ch.UserCostumeUuid, ch.MainUserWeaponUuid, ch.SubUserWeaponUuid, ch.UserCompanionUuid, ch.UserThoughtUuid)
|
||||
}
|
||||
}
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserDeckSubWeaponGroup", oldUser, userdata.DeckSubWeaponRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid", "userWeaponUuid"}).
|
||||
Track("IUserDeckPartsGroup", oldUser, userdata.DeckPartsGroupRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid", "userPartsUuid"}).
|
||||
Track("IUserDeckCharacterDressupCostume", oldUser, userdata.DeckDressupCostumeRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid"})
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if req.Deck == nil {
|
||||
return
|
||||
}
|
||||
store.ApplyDeckReplacement(user, model.DeckType(req.DeckType), req.UserDeckNumber, deckSlotsFromProto(req.Deck), gametime.NowMillis())
|
||||
})
|
||||
|
||||
result := userdata.ProjectTables(user, []string{
|
||||
"IUserDeck", "IUserDeckCharacter", "IUserDeckSubWeaponGroup", "IUserDeckPartsGroup",
|
||||
"IUserDeckCharacterDressupCostume",
|
||||
})
|
||||
return &pb.ReplaceDeckResponse{
|
||||
DiffUserData: tracker.Apply(user, result),
|
||||
}, nil
|
||||
return &pb.ReplaceDeckResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *DeckServiceServer) ReplaceTripleDeck(ctx context.Context, req *pb.ReplaceTripleDeckRequest) (*pb.ReplaceTripleDeckResponse, error) {
|
||||
log.Printf("[DeckService] ReplaceTripleDeck: deckType=%d deckNumber=%d", req.DeckType, req.UserDeckNumber)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserDeckSubWeaponGroup", oldUser, userdata.DeckSubWeaponRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid", "userWeaponUuid"}).
|
||||
Track("IUserDeckPartsGroup", oldUser, userdata.DeckPartsGroupRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid", "userPartsUuid"}).
|
||||
Track("IUserDeckCharacterDressupCostume", oldUser, userdata.DeckDressupCostumeRecords,
|
||||
[]string{"userId", "userDeckCharacterUuid"})
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
for idx, detail := range []*pb.DeckDetail{req.DeckDetail01, req.DeckDetail02, req.DeckDetail03} {
|
||||
if detail == nil || detail.Deck == nil {
|
||||
@@ -231,11 +193,23 @@ func (s *DeckServiceServer) ReplaceTripleDeck(ctx context.Context, req *pb.Repla
|
||||
}
|
||||
})
|
||||
|
||||
result := userdata.ProjectTables(user, []string{
|
||||
"IUserDeck", "IUserDeckCharacter", "IUserDeckSubWeaponGroup", "IUserDeckPartsGroup",
|
||||
"IUserDeckCharacterDressupCostume",
|
||||
})
|
||||
return &pb.ReplaceTripleDeckResponse{
|
||||
DiffUserData: tracker.Apply(user, result),
|
||||
}, nil
|
||||
return &pb.ReplaceTripleDeckResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *DeckServiceServer) ReplaceMultiDeck(ctx context.Context, req *pb.ReplaceMultiDeckRequest) (*pb.ReplaceMultiDeckResponse, error) {
|
||||
log.Printf("[DeckService] ReplaceMultiDeck: %d entries", len(req.DeckDetail))
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
for idx, detail := range req.DeckDetail {
|
||||
if detail == nil || detail.Deck == nil {
|
||||
continue
|
||||
}
|
||||
log.Printf("[DeckService] ReplaceMultiDeck detail %d: deckType=%d deckNumber=%d", idx+1, detail.DeckType, detail.UserDeckNumber)
|
||||
store.ApplyDeckReplacement(user, model.DeckType(detail.DeckType), detail.UserDeckNumber, deckSlotsFromProto(detail.Deck), nowMillis)
|
||||
}
|
||||
})
|
||||
|
||||
return &pb.ReplaceMultiDeckResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type DokanServiceServer struct {
|
||||
@@ -23,8 +22,8 @@ func NewDokanServiceServer(users store.UserRepository, sessions store.SessionRep
|
||||
func (s *DokanServiceServer) RegisterDokanConfirmed(ctx context.Context, req *pb.RegisterDokanConfirmedRequest) (*pb.RegisterDokanConfirmedResponse, error) {
|
||||
log.Printf("[DokanService] RegisterDokanConfirmed: dokanIds=%v", req.DokanId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, id := range req.DokanId {
|
||||
user.DokanConfirmed[id] = true
|
||||
}
|
||||
@@ -33,9 +32,5 @@ func (s *DokanServiceServer) RegisterDokanConfirmed(ctx context.Context, req *pb
|
||||
return nil, fmt.Errorf("update user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserDokan"}))
|
||||
|
||||
return &pb.RegisterDokanConfirmedResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.RegisterDokanConfirmedResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,18 +18,6 @@ const (
|
||||
exploreRewardBaseCount = 1
|
||||
)
|
||||
|
||||
var exploreDiffTables = []string{
|
||||
"IUserExplore",
|
||||
"IUserExploreScore",
|
||||
}
|
||||
|
||||
var exploreFinishDiffTables = []string{
|
||||
"IUserExplore",
|
||||
"IUserExploreScore",
|
||||
"IUserMaterial",
|
||||
"IUserStatus",
|
||||
}
|
||||
|
||||
type ExploreServiceServer struct {
|
||||
pb.UnimplementedExploreServiceServer
|
||||
users store.UserRepository
|
||||
@@ -49,10 +36,10 @@ func (s *ExploreServiceServer) StartExplore(ctx context.Context, req *pb.StartEx
|
||||
return nil, fmt.Errorf("explore id=%d not found", req.ExploreId)
|
||||
}
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
explore := s.catalog.Explores[req.ExploreId]
|
||||
if req.UseConsumableItemId > 0 && explore.ConsumeItemCount > 0 {
|
||||
cur := user.ConsumableItems[req.UseConsumableItemId]
|
||||
@@ -71,11 +58,7 @@ func (s *ExploreServiceServer) StartExplore(ctx context.Context, req *pb.StartEx
|
||||
return nil, fmt.Errorf("start explore: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, exploreDiffTables))
|
||||
|
||||
return &pb.StartExploreResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.StartExploreResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *ExploreServiceServer) FinishExplore(ctx context.Context, req *pb.FinishExploreRequest) (*pb.FinishExploreResponse, error) {
|
||||
@@ -88,12 +71,12 @@ func (s *ExploreServiceServer) FinishExplore(ctx context.Context, req *pb.Finish
|
||||
|
||||
assetGradeIconId := s.catalog.GradeForScore(req.ExploreId, req.Score)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
rewardCount := int32(exploreRewardBaseCount) * explore.RewardLotteryCount
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
existing, exists := user.ExploreScores[req.ExploreId]
|
||||
if !exists || req.Score > existing.MaxScore {
|
||||
user.ExploreScores[req.ExploreId] = store.ExploreScoreState{
|
||||
@@ -123,8 +106,6 @@ func (s *ExploreServiceServer) FinishExplore(ctx context.Context, req *pb.Finish
|
||||
return nil, fmt.Errorf("finish explore: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, exploreFinishDiffTables))
|
||||
|
||||
rewards := []*pb.ExploreReward{
|
||||
{
|
||||
PossessionType: int32(model.PossessionTypeMaterial),
|
||||
@@ -137,17 +118,16 @@ func (s *ExploreServiceServer) FinishExplore(ctx context.Context, req *pb.Finish
|
||||
AcquireStaminaCount: exploreStaminaRecovery,
|
||||
ExploreReward: rewards,
|
||||
AssetGradeIconId: assetGradeIconId,
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ExploreServiceServer) RetireExplore(ctx context.Context, req *pb.RetireExploreRequest) (*pb.RetireExploreResponse, error) {
|
||||
log.Printf("[ExploreService] RetireExplore: exploreId=%d", req.ExploreId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.Explore = store.ExploreState{
|
||||
PlayingExploreId: 0,
|
||||
IsUseExploreTicket: false,
|
||||
@@ -159,9 +139,5 @@ func (s *ExploreServiceServer) RetireExplore(ctx context.Context, req *pb.Retire
|
||||
return nil, fmt.Errorf("retire explore: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserExplore"}))
|
||||
|
||||
return &pb.RetireExploreResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.RetireExploreResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -23,7 +22,7 @@ func NewFriendServiceServer(users store.UserRepository, sessions store.SessionRe
|
||||
|
||||
func (s *FriendServiceServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
|
||||
log.Printf("[FriendService] GetUser: playerId=%d", req.PlayerId)
|
||||
return &pb.GetUserResponse{DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetUserResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *FriendServiceServer) GetFriendList(ctx context.Context, req *pb.GetFriendListRequest) (*pb.GetFriendListResponse, error) {
|
||||
@@ -32,22 +31,19 @@ func (s *FriendServiceServer) GetFriendList(ctx context.Context, req *pb.GetFrie
|
||||
FriendUser: []*pb.FriendUser{},
|
||||
SendCheerCount: 0,
|
||||
ReceivedCheerCount: 0,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *FriendServiceServer) GetFriendRequestList(ctx context.Context, req *emptypb.Empty) (*pb.GetFriendRequestListResponse, error) {
|
||||
log.Printf("[FriendService] GetFriendRequestList")
|
||||
return &pb.GetFriendRequestListResponse{
|
||||
User: []*pb.User{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
User: []*pb.User{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *FriendServiceServer) SearchRecommendedUsers(ctx context.Context, req *emptypb.Empty) (*pb.SearchRecommendedUsersResponse, error) {
|
||||
log.Printf("[FriendService] SearchRecommendedUsers")
|
||||
return &pb.SearchRecommendedUsersResponse{
|
||||
Users: []*pb.User{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
Users: []*pb.User{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -11,25 +11,11 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
var gachaDiffTables = []string{
|
||||
"IUserGem",
|
||||
"IUserCostume",
|
||||
"IUserWeapon",
|
||||
"IUserConsumableItem",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserWeaponNote",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserCharacter",
|
||||
"IUserMaterial",
|
||||
}
|
||||
|
||||
type GachaServiceServer struct {
|
||||
pb.UnimplementedGachaServiceServer
|
||||
users store.UserRepository
|
||||
@@ -56,7 +42,7 @@ func (s *GachaServiceServer) GetGachaList(ctx context.Context, req *pb.GetGachaL
|
||||
log.Printf("[GachaService] GetGachaList: labels=%v", req.GachaLabelType)
|
||||
|
||||
catalog := s.catalog
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
user, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
@@ -82,7 +68,6 @@ func (s *GachaServiceServer) GetGachaList(ctx context.Context, req *pb.GetGachaL
|
||||
return &pb.GetGachaListResponse{
|
||||
Gacha: gachaList,
|
||||
ConvertedGachaMedal: toProtoConvertedGachaMedal(user.Gacha.ConvertedGachaMedal),
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -134,7 +119,7 @@ func (s *GachaServiceServer) GetGacha(ctx context.Context, req *pb.GetGachaReque
|
||||
|
||||
catalog := s.catalog
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
@@ -152,8 +137,7 @@ func (s *GachaServiceServer) GetGacha(ctx context.Context, req *pb.GetGachaReque
|
||||
}
|
||||
|
||||
return &pb.GetGachaResponse{
|
||||
Gacha: byId,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
Gacha: byId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -165,7 +149,7 @@ func (s *GachaServiceServer) Draw(ctx context.Context, req *pb.DrawRequest) (*pb
|
||||
return nil, fmt.Errorf("gacha %d not found", req.GachaId)
|
||||
}
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
execCount := req.ExecCount
|
||||
if execCount <= 0 {
|
||||
execCount = 1
|
||||
@@ -290,17 +274,11 @@ func (s *GachaServiceServer) Draw(ctx context.Context, req *pb.DrawRequest) (*pb
|
||||
bs := updatedUser.Gacha.BannerStates[entry.GachaId]
|
||||
nextGacha := toProtoGacha(*entry, &bs)
|
||||
|
||||
changedStoryIds := s.handler.Granter.DrainChangedStoryWeaponIds()
|
||||
diffOrder := append(gachaDiffTables, "IUserWeaponStory")
|
||||
diff := userdata.BuildDiffFromTablesOrdered(userdata.ProjectTables(updatedUser, diffOrder), diffOrder)
|
||||
userdata.AddWeaponStoryDiff(diff, updatedUser, changedStoryIds)
|
||||
|
||||
return &pb.DrawResponse{
|
||||
NextGacha: nextGacha,
|
||||
GachaResult: gachaResults,
|
||||
GachaBonus: bonuses,
|
||||
MenuGachaBadgeInfo: []*pb.MenuGachaBadgeInfo{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -312,7 +290,7 @@ func (s *GachaServiceServer) ResetBoxGacha(ctx context.Context, req *pb.ResetBox
|
||||
return nil, fmt.Errorf("gacha %d not found", req.GachaId)
|
||||
}
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
updatedUser, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if resetErr := s.handler.HandleResetBox(user, *entry); resetErr != nil {
|
||||
log.Printf("[GachaService] ResetBoxGacha error: %v", resetErr)
|
||||
@@ -325,14 +303,13 @@ func (s *GachaServiceServer) ResetBoxGacha(ctx context.Context, req *pb.ResetBox
|
||||
bs := updatedUser.Gacha.BannerStates[entry.GachaId]
|
||||
|
||||
return &pb.ResetBoxGachaResponse{
|
||||
Gacha: toProtoGacha(*entry, &bs),
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
Gacha: toProtoGacha(*entry, &bs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *GachaServiceServer) GetRewardGacha(ctx context.Context, req *emptypb.Empty) (*pb.GetRewardGachaResponse, error) {
|
||||
log.Printf("[GachaService] GetRewardGacha")
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
@@ -353,14 +330,13 @@ func (s *GachaServiceServer) GetRewardGacha(ctx context.Context, req *emptypb.Em
|
||||
Available: drawCount < maxCount,
|
||||
TodaysCurrentDrawCount: drawCount,
|
||||
DailyMaxCount: maxCount,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *GachaServiceServer) RewardDraw(ctx context.Context, req *pb.RewardDrawRequest) (*pb.RewardDrawResponse, error) {
|
||||
log.Printf("[GachaService] RewardDraw: placement=%q reward=%q amount=%q", req.PlacementName, req.RewardName, req.RewardAmount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
var items []gacha.DrawnItem
|
||||
updatedUser, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
@@ -393,12 +369,8 @@ func (s *GachaServiceServer) RewardDraw(ctx context.Context, req *pb.RewardDrawR
|
||||
})
|
||||
}
|
||||
|
||||
tables := userdata.FullClientTableMap(updatedUser)
|
||||
diff := userdata.BuildDiffFromTables(tables)
|
||||
|
||||
return &pb.RewardDrawResponse{
|
||||
RewardGachaResult: results,
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"log"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type GameplayServiceServer struct {
|
||||
@@ -23,6 +22,5 @@ func (s *GameplayServiceServer) CheckBeforeGamePlay(ctx context.Context, req *pb
|
||||
return &pb.CheckBeforeGamePlayResponse{
|
||||
IsExistUnreadPop: false,
|
||||
MenuGachaBadgeInfo: []*pb.MenuGachaBadgeInfo{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -30,7 +29,7 @@ func NewGiftServiceServer(users store.UserRepository, sessions store.SessionRepo
|
||||
func (s *GiftServiceServer) ReceiveGift(ctx context.Context, req *pb.ReceiveGiftRequest) (*pb.ReceiveGiftResponse, error) {
|
||||
log.Printf("[GiftService] ReceiveGift: giftUuids=%d", len(req.UserGiftUuid))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
received := make([]string, 0, len(req.UserGiftUuid))
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
@@ -54,7 +53,6 @@ func (s *GiftServiceServer) ReceiveGift(ctx context.Context, req *pb.ReceiveGift
|
||||
ReceivedGiftUuid: []string{},
|
||||
ExpiredGiftUuid: []string{},
|
||||
OverflowGiftUuid: []string{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -62,7 +60,6 @@ func (s *GiftServiceServer) ReceiveGift(ctx context.Context, req *pb.ReceiveGift
|
||||
ReceivedGiftUuid: received,
|
||||
ExpiredGiftUuid: []string{},
|
||||
OverflowGiftUuid: []string{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -70,7 +67,7 @@ func (s *GiftServiceServer) GetGiftList(ctx context.Context, req *pb.GetGiftList
|
||||
log.Printf("[GiftService] GetGiftList: rewardKinds=%v expirationType=%d ascending=%v nextCursor=%d previousCursor=%d getCount=%d",
|
||||
req.RewardKindType, req.ExpirationType, req.IsAscendingSort, req.NextCursor, req.PreviousCursor, req.GetCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
@@ -101,13 +98,12 @@ func (s *GiftServiceServer) GetGiftList(ctx context.Context, req *pb.GetGiftList
|
||||
TotalPageCount: pageCount(len(user.Gifts.NotReceived), int(req.GetCount)),
|
||||
NextCursor: 0,
|
||||
PreviousCursor: 0,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *GiftServiceServer) GetGiftReceiveHistoryList(ctx context.Context, req *emptypb.Empty) (*pb.GetGiftReceiveHistoryListResponse, error) {
|
||||
log.Printf("[GiftService] GetGiftReceiveHistoryList")
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
@@ -121,8 +117,7 @@ func (s *GiftServiceServer) GetGiftReceiveHistoryList(ctx context.Context, req *
|
||||
})
|
||||
}
|
||||
return &pb.GetGiftReceiveHistoryListResponse{
|
||||
Gift: items,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
Gift: items,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -27,8 +26,8 @@ func NewGimmickServiceServer(users store.UserRepository, sessions store.SessionR
|
||||
func (s *GimmickServiceServer) UpdateSequence(ctx context.Context, req *pb.UpdateSequenceRequest) (*pb.UpdateSequenceResponse, error) {
|
||||
log.Printf("[GimmickService] UpdateSequence: scheduleId=%d sequenceId=%d",
|
||||
req.GimmickSequenceScheduleId, req.GimmickSequenceId)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
key := store.GimmickSequenceKey{
|
||||
GimmickSequenceScheduleId: req.GimmickSequenceScheduleId,
|
||||
GimmickSequenceId: req.GimmickSequenceId,
|
||||
@@ -37,16 +36,14 @@ func (s *GimmickServiceServer) UpdateSequence(ctx context.Context, req *pb.Updat
|
||||
sequence.Key = key
|
||||
user.Gimmick.Sequences[key] = sequence
|
||||
})
|
||||
return &pb.UpdateSequenceResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserGimmickSequence"})),
|
||||
}, nil
|
||||
return &pb.UpdateSequenceResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *GimmickServiceServer) UpdateGimmickProgress(ctx context.Context, req *pb.UpdateGimmickProgressRequest) (*pb.UpdateGimmickProgressResponse, error) {
|
||||
log.Printf("[GimmickService] UpdateGimmickProgress: scheduleId=%d sequenceId=%d gimmickId=%d ornamentIndex=%d progressValueBit=%d flowType=%d",
|
||||
req.GimmickSequenceScheduleId, req.GimmickSequenceId, req.GimmickId, req.GimmickOrnamentIndex, req.ProgressValueBit, req.FlowType)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
progressKey := store.GimmickKey{
|
||||
GimmickSequenceScheduleId: req.GimmickSequenceScheduleId,
|
||||
@@ -74,18 +71,14 @@ func (s *GimmickServiceServer) UpdateGimmickProgress(ctx context.Context, req *p
|
||||
GimmickOrnamentReward: []*pb.GimmickReward{},
|
||||
IsSequenceCleared: false,
|
||||
GimmickSequenceClearReward: []*pb.GimmickReward{},
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{
|
||||
"IUserGimmick",
|
||||
"IUserGimmickOrnamentProgress",
|
||||
})),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *GimmickServiceServer) InitSequenceSchedule(ctx context.Context, _ *emptypb.Empty) (*pb.InitSequenceScheduleResponse, error) {
|
||||
log.Printf("[GimmickService] InitSequenceSchedule")
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
now := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
added := 0
|
||||
for _, key := range s.gimmickCatalog.ActiveScheduleKeys(*user, now) {
|
||||
if _, exists := user.Gimmick.Sequences[key]; !exists {
|
||||
@@ -97,15 +90,13 @@ func (s *GimmickServiceServer) InitSequenceSchedule(ctx context.Context, _ *empt
|
||||
log.Printf("[GimmickService] InitSequenceSchedule: added %d sequences (total %d)", added, len(user.Gimmick.Sequences))
|
||||
}
|
||||
})
|
||||
return &pb.InitSequenceScheduleResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, gimmickDiffTables)),
|
||||
}, nil
|
||||
return &pb.InitSequenceScheduleResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *GimmickServiceServer) Unlock(ctx context.Context, req *pb.UnlockRequest) (*pb.UnlockResponse, error) {
|
||||
log.Printf("[GimmickService] Unlock: gimmickKeys=%d", len(req.GimmickKey))
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, item := range req.GimmickKey {
|
||||
key := store.GimmickKey{
|
||||
GimmickSequenceScheduleId: item.GimmickSequenceScheduleId,
|
||||
@@ -118,7 +109,5 @@ func (s *GimmickServiceServer) Unlock(ctx context.Context, req *pb.UnlockRequest
|
||||
user.Gimmick.Unlocks[key] = unlock
|
||||
}
|
||||
})
|
||||
return &pb.UnlockResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserGimmickUnlock"})),
|
||||
}, nil
|
||||
return &pb.UnlockResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func parseListBin(data []byte) listBinIndex {
|
||||
return idx
|
||||
}
|
||||
|
||||
func loadListBinIndex(revision string) (listBinIndex, bool) {
|
||||
func loadListBinIndex(baseDir, revision string) (listBinIndex, bool) {
|
||||
listBinCacheMu.RLock()
|
||||
idx, ok := listBinCache[revision]
|
||||
listBinCacheMu.RUnlock()
|
||||
@@ -230,7 +230,7 @@ func loadListBinIndex(revision string) (listBinIndex, bool) {
|
||||
listBinInflight[revision] = load
|
||||
listBinCacheMu.Unlock()
|
||||
|
||||
filePath := filepath.Join("assets", "revisions", revision, "list.bin")
|
||||
filePath := filepath.Join(baseDir, "assets", "revisions", revision, "list.bin")
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
listBinCacheMu.Lock()
|
||||
@@ -250,7 +250,7 @@ func loadListBinIndex(revision string) (listBinIndex, bool) {
|
||||
return idx, true
|
||||
}
|
||||
|
||||
func loadInfoIndex(revision string) map[string]infoAlias {
|
||||
func loadInfoIndex(baseDir, revision string) map[string]infoAlias {
|
||||
infoCacheMu.RLock()
|
||||
m, ok := infoCache[revision]
|
||||
infoCacheMu.RUnlock()
|
||||
@@ -272,7 +272,7 @@ func loadInfoIndex(revision string) map[string]infoAlias {
|
||||
infoInflight[revision] = load
|
||||
infoCacheMu.Unlock()
|
||||
|
||||
filePath := filepath.Join("assets", "revisions", revision, "info.json")
|
||||
filePath := filepath.Join(baseDir, "assets", "revisions", revision, "info.json")
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
infoCacheMu.Lock()
|
||||
@@ -378,7 +378,7 @@ func hasNonASCII(s string) bool {
|
||||
// an en locale fallback is appended (marked IsLocaleFallback so callers can skip MD5 validation).
|
||||
// For paths with non-ASCII characters, mojibake (double-encoded) and fullwidth-to-ASCII
|
||||
// variants are also tried.
|
||||
func pathStrToFullPaths(revision, assetType, pathStr string) []pathCandidate {
|
||||
func pathStrToFullPaths(baseDir, revision, assetType, pathStr string) []pathCandidate {
|
||||
fsPath := strings.ReplaceAll(pathStr, ")", "/")
|
||||
if strings.Contains(fsPath, "..") || filepath.IsAbs(fsPath) || strings.HasPrefix(fsPath, "/") {
|
||||
return nil
|
||||
@@ -402,7 +402,7 @@ func pathStrToFullPaths(revision, assetType, pathStr string) []pathCandidate {
|
||||
if strings.Contains(pathStr, ")ko)") {
|
||||
entries = append(entries, tagged{strings.ReplaceAll(pathStr, ")ko)", ")en)"), true})
|
||||
}
|
||||
base := filepath.Join("assets", "revisions", revision)
|
||||
base := filepath.Join(baseDir, "assets", "revisions", revision)
|
||||
var out []pathCandidate
|
||||
seen := make(map[string]bool)
|
||||
for _, e := range entries {
|
||||
@@ -434,13 +434,13 @@ func appendUniqueCandidate(candidates []assetCandidate, seen map[string]bool, ca
|
||||
return append(candidates, candidate)
|
||||
}
|
||||
|
||||
func duplicateCandidatePath(candidate assetCandidate, assetType, targetRevision, targetBaseName string) string {
|
||||
root := filepath.Join("assets", "revisions", candidate.Revision, assetType)
|
||||
func duplicateCandidatePath(baseDir string, candidate assetCandidate, assetType, targetRevision, targetBaseName string) string {
|
||||
root := filepath.Join(baseDir, "assets", "revisions", candidate.Revision, assetType)
|
||||
rel, err := filepath.Rel(root, candidate.Path)
|
||||
if err != nil || strings.HasPrefix(rel, "..") || filepath.IsAbs(rel) {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join("assets", "revisions", targetRevision, assetType, filepath.Dir(rel), targetBaseName)
|
||||
return filepath.Join(baseDir, "assets", "revisions", targetRevision, assetType, filepath.Dir(rel), targetBaseName)
|
||||
}
|
||||
|
||||
// objectIdToFilePathCandidates returns candidate file paths for the object: list.bin path, locale fallbacks
|
||||
@@ -448,8 +448,8 @@ func duplicateCandidatePath(candidate assetCandidate, assetType, targetRevision,
|
||||
// The original locale path is tried first (with MD5 validation); locale fallbacks are tried after
|
||||
// (without MD5 validation, since the hash in list.bin refers to the original locale's content).
|
||||
// Callers should try each path until one exists on disk.
|
||||
func objectIdToFilePathCandidates(revision, assetType, objectId string) (candidates []assetCandidate, size int64, ok bool) {
|
||||
idx, ok := loadListBinIndex(revision)
|
||||
func objectIdToFilePathCandidates(baseDir, revision, assetType, objectId string) (candidates []assetCandidate, size int64, ok bool) {
|
||||
idx, ok := loadListBinIndex(baseDir, revision)
|
||||
if !ok || idx == nil {
|
||||
return nil, 0, false
|
||||
}
|
||||
@@ -457,7 +457,7 @@ func objectIdToFilePathCandidates(revision, assetType, objectId string) (candida
|
||||
if !ok || entry.Path == "" {
|
||||
return nil, 0, false
|
||||
}
|
||||
paths := pathStrToFullPaths(revision, assetType, entry.Path)
|
||||
paths := pathStrToFullPaths(baseDir, revision, assetType, entry.Path)
|
||||
if len(paths) == 0 {
|
||||
return nil, 0, false
|
||||
}
|
||||
@@ -474,15 +474,14 @@ func objectIdToFilePathCandidates(revision, assetType, objectId string) (candida
|
||||
ExpectedMD5: md5,
|
||||
})
|
||||
}
|
||||
// Add paths from info.json: when requested file is a "from-name" (duplicate not included), serve "to-name" instead.
|
||||
infoIndex := loadInfoIndex(revision)
|
||||
infoIndex := loadInfoIndex(baseDir, revision)
|
||||
if len(infoIndex) > 0 {
|
||||
for _, c := range candidates {
|
||||
alias, ok := infoIndex[filepath.Base(c.Path)]
|
||||
if !ok || alias.ToName == "" {
|
||||
continue
|
||||
}
|
||||
alt := duplicateCandidatePath(c, assetType, alias.ToRevision, alias.ToName)
|
||||
alt := duplicateCandidatePath(baseDir, c, assetType, alias.ToRevision, alias.ToName)
|
||||
if alt == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -29,9 +28,9 @@ func NewLoginBonusServiceServer(users store.UserRepository, sessions store.Sessi
|
||||
|
||||
func (s *LoginBonusServiceServer) ReceiveStamp(ctx context.Context, req *emptypb.Empty) (*pb.ReceiveStampResponse, error) {
|
||||
log.Printf("[LoginBonusService] ReceiveStamp")
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
now := gametime.NowMillis()
|
||||
nextStamp := user.LoginBonus.CurrentStampNumber + 1
|
||||
|
||||
@@ -64,9 +63,5 @@ func (s *LoginBonusServiceServer) ReceiveStamp(ctx context.Context, req *emptypb
|
||||
user.LoginBonus.LatestVersion = now
|
||||
})
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(user,
|
||||
[]string{"IUserLoginBonus"},
|
||||
))
|
||||
setCommonResponseTrailers(ctx, diff, false)
|
||||
return &pb.ReceiveStampResponse{DiffUserData: diff}, nil
|
||||
return &pb.ReceiveStampResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,14 +8,8 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
var materialDiffTables = []string{
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type MaterialServiceServer struct {
|
||||
pb.UnimplementedMaterialServiceServer
|
||||
users store.UserRepository
|
||||
@@ -31,13 +25,9 @@ func NewMaterialServiceServer(users store.UserRepository, sessions store.Session
|
||||
func (s *MaterialServiceServer) Sell(ctx context.Context, req *pb.MaterialSellRequest) (*pb.MaterialSellResponse, error) {
|
||||
log.Printf("[MaterialService] Sell: %d item(s)", len(req.MaterialPossession))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserMaterial", oldUser, userdata.SortedMaterialRecords, []string{"userId", "materialId"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
for _, item := range req.MaterialPossession {
|
||||
mat, ok := s.catalog.All[item.MaterialId]
|
||||
@@ -71,10 +61,5 @@ func (s *MaterialServiceServer) Sell(ctx context.Context, req *pb.MaterialSellRe
|
||||
return nil, fmt.Errorf("material sell: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(snapshot, materialDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.MaterialSellResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.MaterialSellResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type MissionServiceServer struct {
|
||||
@@ -23,15 +22,11 @@ func NewMissionServiceServer(users store.UserRepository, sessions store.SessionR
|
||||
func (s *MissionServiceServer) UpdateMissionProgress(ctx context.Context, req *pb.UpdateMissionProgressRequest) (*pb.UpdateMissionProgressResponse, error) {
|
||||
log.Printf("[MissionService] UpdateMissionProgress: cage=%v pictureBook=%v", req.CageMeasurableValues, req.PictureBookMeasurableValues)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
snapshot, err := s.users.LoadUser(userId)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
_, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("snapshot user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserMission"}))
|
||||
|
||||
return &pb.UpdateMissionProgressResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.UpdateMissionProgressResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type MovieServiceServer struct {
|
||||
@@ -24,10 +23,10 @@ func NewMovieServiceServer(users store.UserRepository, sessions store.SessionRep
|
||||
func (s *MovieServiceServer) SaveViewedMovie(ctx context.Context, req *pb.SaveViewedMovieRequest) (*pb.SaveViewedMovieResponse, error) {
|
||||
log.Printf("[MovieService] SaveViewedMovie: movieIds=%v", req.MovieId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
now := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, mid := range req.MovieId {
|
||||
user.ViewedMovies[mid] = now
|
||||
}
|
||||
@@ -36,9 +35,5 @@ func (s *MovieServiceServer) SaveViewedMovie(ctx context.Context, req *pb.SaveVi
|
||||
return nil, fmt.Errorf("update user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserMovie"}))
|
||||
|
||||
return &pb.SaveViewedMovieResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.SaveViewedMovieResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type NaviCutInServiceServer struct {
|
||||
@@ -23,17 +22,13 @@ func NewNaviCutInServiceServer(users store.UserRepository, sessions store.Sessio
|
||||
func (s *NaviCutInServiceServer) RegisterPlayed(ctx context.Context, req *pb.RegisterPlayedRequest) (*pb.RegisterPlayedResponse, error) {
|
||||
log.Printf("[NaviCutInService] RegisterPlayed: naviCutId=%d", req.NaviCutId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.NaviCutInPlayed[req.NaviCutId] = true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserNaviCutIn"}))
|
||||
|
||||
return &pb.RegisterPlayedResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.RegisterPlayedResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -23,20 +22,18 @@ func NewNotificationServiceServer(users store.UserRepository, sessions store.Ses
|
||||
|
||||
func (s *NotificationServiceServer) GetHeaderNotification(ctx context.Context, req *emptypb.Empty) (*pb.GetHeaderNotificationResponse, error) {
|
||||
log.Printf("[NotificationService] GetHeaderNotification")
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return &pb.GetHeaderNotificationResponse{
|
||||
GiftNotReceiveCount: 0,
|
||||
FriendRequestReceiveCount: 0,
|
||||
IsExistUnreadInformation: false,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
return &pb.GetHeaderNotificationResponse{
|
||||
GiftNotReceiveCount: int32(len(user.Gifts.NotReceived)),
|
||||
FriendRequestReceiveCount: user.Notifications.FriendRequestReceiveCount,
|
||||
IsExistUnreadInformation: user.Notifications.IsExistUnreadInformation,
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ const resourcesURLOriginal = "https://resources.app.nierreincarnation.com"
|
||||
type OctoHTTPServer struct {
|
||||
mux *http.ServeMux
|
||||
ResourcesBaseURL string // if non-empty and exactly 43 chars, list.bin is rewritten to use this base for asset URLs
|
||||
BaseDir string // root directory containing the assets/ tree; empty means current directory
|
||||
revisions *revisionTracker
|
||||
resolver *assetResolver
|
||||
}
|
||||
@@ -124,12 +125,13 @@ func fileMD5Hex(path string, info os.FileInfo) (string, error) {
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func NewOctoHTTPServer(resourcesBaseURL string) *OctoHTTPServer {
|
||||
func NewOctoHTTPServer(resourcesBaseURL, baseDir string) *OctoHTTPServer {
|
||||
s := &OctoHTTPServer{
|
||||
mux: http.NewServeMux(),
|
||||
ResourcesBaseURL: resourcesBaseURL,
|
||||
BaseDir: baseDir,
|
||||
revisions: newRevisionTracker(),
|
||||
resolver: newAssetResolver(),
|
||||
resolver: newAssetResolver(baseDir),
|
||||
}
|
||||
s.resolver.Prewarm("0")
|
||||
s.mux.HandleFunc("/", s.handleAll)
|
||||
@@ -226,7 +228,7 @@ func (s *OctoHTTPServer) handleOctoV2(w http.ResponseWriter, r *http.Request, pa
|
||||
requestedRevision := parts[len(parts)-1]
|
||||
if requestedRevision != "" {
|
||||
revision := "0"
|
||||
filePath := "assets/revisions/0/list.bin"
|
||||
filePath := filepath.Join(s.BaseDir, "assets", "revisions", "0", "list.bin")
|
||||
if requestedRevision != revision {
|
||||
log.Printf("[OctoV2] Resource list request revision=%s canonicalized to revision=%s", requestedRevision, revision)
|
||||
}
|
||||
@@ -266,7 +268,7 @@ func (s *OctoHTTPServer) serveOctoV1List(w http.ResponseWriter, r *http.Request,
|
||||
requestedRevision = parts[len(parts)-1]
|
||||
}
|
||||
revision := "0"
|
||||
filePath := "assets/revisions/0/list.bin"
|
||||
filePath := filepath.Join(s.BaseDir, "assets", "revisions", "0", "list.bin")
|
||||
if requestedRevision != revision {
|
||||
log.Printf("[OctoV1] list request revision=%s canonicalized to revision=%s", requestedRevision, revision)
|
||||
}
|
||||
@@ -316,11 +318,11 @@ func (s *OctoHTTPServer) serveUnsoAsset(w http.ResponseWriter, r *http.Request,
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
baseDir := filepath.Join("assets", "revisions")
|
||||
revDir := filepath.Join(s.BaseDir, "assets", "revisions")
|
||||
var triedPaths []string
|
||||
var md5Mismatches []string
|
||||
for _, candidate := range resolution.Candidates {
|
||||
rel, err := filepath.Rel(baseDir, candidate.Path)
|
||||
rel, err := filepath.Rel(revDir, candidate.Path)
|
||||
if err != nil || strings.Contains(rel, "..") || filepath.IsAbs(rel) {
|
||||
continue
|
||||
}
|
||||
@@ -409,9 +411,9 @@ func (s *OctoHTTPServer) serveDatabaseBinE(w http.ResponseWriter, r *http.Reques
|
||||
break
|
||||
}
|
||||
}
|
||||
filePath := "assets/release/database.bin.e"
|
||||
filePath := filepath.Join(s.BaseDir, "assets", "release", "database.bin.e")
|
||||
if version != "" {
|
||||
vPath := "assets/release/" + version + ".bin.e"
|
||||
vPath := filepath.Join(s.BaseDir, "assets", "release", version+".bin.e")
|
||||
if _, err := os.Stat(vPath); err == nil {
|
||||
filePath = vPath
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type OmikujiServiceServer struct {
|
||||
@@ -26,21 +25,18 @@ func NewOmikujiServiceServer(users store.UserRepository, sessions store.SessionR
|
||||
func (s *OmikujiServiceServer) OmikujiDraw(ctx context.Context, req *pb.OmikujiDrawRequest) (*pb.OmikujiDrawResponse, error) {
|
||||
log.Printf("[OmikujiService] OmikujiDraw: omikujiId=%d", req.OmikujiId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
now := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.DrawnOmikuji[req.OmikujiId] = now
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update user: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserOmikuji"}))
|
||||
|
||||
return &pb.OmikujiDrawResponse{
|
||||
OmikujiResultAssetId: s.catalog.LookupAssetId(req.OmikujiId),
|
||||
OmikujiItem: []*pb.OmikujiItem{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -10,16 +10,10 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
const partsMaxLevel = int32(15)
|
||||
|
||||
var partsDiffTables = []string{
|
||||
"IUserParts",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type PartsServiceServer struct {
|
||||
pb.UnimplementedPartsServiceServer
|
||||
users store.UserRepository
|
||||
@@ -35,13 +29,9 @@ func NewPartsServiceServer(users store.UserRepository, sessions store.SessionRep
|
||||
func (s *PartsServiceServer) Sell(ctx context.Context, req *pb.PartsSellRequest) (*pb.PartsSellResponse, error) {
|
||||
log.Printf("[PartsService] Sell: %d part(s)", len(req.UserPartsUuid))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserParts", oldUser, userdata.SortedPartsRecords, []string{"userId", "userPartsUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
for _, uuid := range req.UserPartsUuid {
|
||||
part, ok := user.Parts[uuid]
|
||||
@@ -81,23 +71,18 @@ func (s *PartsServiceServer) Sell(ctx context.Context, req *pb.PartsSellRequest)
|
||||
return nil, fmt.Errorf("parts sell: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(snapshot, partsDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.PartsSellResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.PartsSellResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *PartsServiceServer) Enhance(ctx context.Context, req *pb.PartsEnhanceRequest) (*pb.PartsEnhanceResponse, error) {
|
||||
log.Printf("[PartsService] Enhance: uuid=%s", req.UserPartsUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
isSuccess := false
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
part, ok := user.Parts[req.UserPartsUuid]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Enhance: part uuid=%s not found", req.UserPartsUuid)
|
||||
@@ -158,11 +143,8 @@ func (s *PartsServiceServer) Enhance(ctx context.Context, req *pb.PartsEnhanceRe
|
||||
return nil, fmt.Errorf("parts enhance: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, partsDiffTables))
|
||||
|
||||
return &pb.PartsEnhanceResponse{
|
||||
IsSuccess: isSuccess,
|
||||
DiffUserData: diff,
|
||||
IsSuccess: isSuccess,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -170,10 +152,10 @@ func (s *PartsServiceServer) ReplacePreset(ctx context.Context, req *pb.PartsRep
|
||||
log.Printf("[PartsService] ReplacePreset: preset=%d uuids=[%s, %s, %s]",
|
||||
req.UserPartsPresetNumber, req.UserPartsUuid01, req.UserPartsUuid02, req.UserPartsUuid03)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
preset := user.PartsPresets[req.UserPartsPresetNumber]
|
||||
preset.UserPartsPresetNumber = req.UserPartsPresetNumber
|
||||
preset.UserPartsUuid01 = req.UserPartsUuid01
|
||||
@@ -186,9 +168,5 @@ func (s *PartsServiceServer) ReplacePreset(ctx context.Context, req *pb.PartsRep
|
||||
return nil, fmt.Errorf("parts replace preset: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserPartsPreset"}))
|
||||
|
||||
return &pb.PartsReplacePresetResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.PartsReplacePresetResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type PortalCageServiceServer struct {
|
||||
@@ -23,17 +22,11 @@ func NewPortalCageServiceServer(users store.UserRepository, sessions store.Sessi
|
||||
func (s *PortalCageServiceServer) UpdatePortalCageSceneProgress(ctx context.Context, req *pb.UpdatePortalCageSceneProgressRequest) (*pb.UpdatePortalCageSceneProgressResponse, error) {
|
||||
log.Printf("[PortalCageService] UpdatePortalCageSceneProgress: portalCageSceneId=%d", req.PortalCageSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
now := gametime.NowMillis()
|
||||
user.PortalCageStatus.IsCurrentProgress = true
|
||||
user.PortalCageStatus.LatestVersion = now
|
||||
})
|
||||
|
||||
tables := userdata.ProjectTables(user,
|
||||
[]string{"IUserPortalCageStatus"},
|
||||
)
|
||||
return &pb.UpdatePortalCageSceneProgressResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTablesOrdered(tables, []string{"IUserPortalCageStatus"}),
|
||||
}, nil
|
||||
return &pb.UpdatePortalCageSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -32,25 +31,11 @@ func NewBigHuntServiceServer(
|
||||
return &BigHuntServiceServer{users: users, sessions: sessions, catalog: catalog, engine: engine}
|
||||
}
|
||||
|
||||
var bigHuntDiffTables = []string{
|
||||
"IUserBigHuntProgressStatus",
|
||||
"IUserBigHuntMaxScore",
|
||||
"IUserBigHuntStatus",
|
||||
"IUserBigHuntScheduleMaxScore",
|
||||
"IUserBigHuntWeeklyMaxScore",
|
||||
"IUserBigHuntWeeklyStatus",
|
||||
}
|
||||
|
||||
func buildBigHuntDiff(user store.UserState, tableNames []string) map[string]*pb.DiffData {
|
||||
tables := userdata.ProjectTables(user, tableNames)
|
||||
return userdata.BuildDiffFromTablesOrdered(tables, tableNames)
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) StartBigHuntQuest(ctx context.Context, req *pb.StartBigHuntQuestRequest) (*pb.StartBigHuntQuestResponse, error) {
|
||||
log.Printf("[BigHuntService] StartBigHuntQuest: bossQuestId=%d questId=%d deckNumber=%d isDryRun=%v",
|
||||
req.BigHuntBossQuestId, req.BigHuntQuestId, req.UserDeckNumber, req.IsDryRun)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
bhQuest, ok := s.catalog.QuestById[req.BigHuntQuestId]
|
||||
@@ -58,7 +43,7 @@ func (s *BigHuntServiceServer) StartBigHuntQuest(ctx context.Context, req *pb.St
|
||||
log.Printf("[BigHuntService] StartBigHuntQuest: unknown bigHuntQuestId=%d", req.BigHuntQuestId)
|
||||
}
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if ok {
|
||||
s.engine.HandleBigHuntQuestStart(user, bhQuest.QuestId, req.UserDeckNumber, nowMillis)
|
||||
}
|
||||
@@ -80,31 +65,27 @@ func (s *BigHuntServiceServer) StartBigHuntQuest(ctx context.Context, req *pb.St
|
||||
user.BigHuntStatuses[req.BigHuntBossQuestId] = st
|
||||
})
|
||||
|
||||
return &pb.StartBigHuntQuestResponse{
|
||||
DiffUserData: buildBigHuntDiff(user, append([]string{"IUserQuest"}, bigHuntDiffTables...)),
|
||||
}, nil
|
||||
return &pb.StartBigHuntQuestResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) UpdateBigHuntQuestSceneProgress(ctx context.Context, req *pb.UpdateBigHuntQuestSceneProgressRequest) (*pb.UpdateBigHuntQuestSceneProgressResponse, error) {
|
||||
log.Printf("[BigHuntService] UpdateBigHuntQuestSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.BigHuntProgress.CurrentQuestSceneId = req.QuestSceneId
|
||||
user.BigHuntProgress.LatestVersion = nowMillis
|
||||
})
|
||||
|
||||
return &pb.UpdateBigHuntQuestSceneProgressResponse{
|
||||
DiffUserData: buildBigHuntDiff(user, []string{"IUserBigHuntProgressStatus"}),
|
||||
}, nil
|
||||
return &pb.UpdateBigHuntQuestSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) FinishBigHuntQuest(ctx context.Context, req *pb.FinishBigHuntQuestRequest) (*pb.FinishBigHuntQuestResponse, error) {
|
||||
log.Printf("[BigHuntService] FinishBigHuntQuest: bossQuestId=%d questId=%d isRetired=%v",
|
||||
req.BigHuntBossQuestId, req.BigHuntQuestId, req.IsRetired)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
bhQuest := s.catalog.QuestById[req.BigHuntQuestId]
|
||||
@@ -114,7 +95,7 @@ func (s *BigHuntServiceServer) FinishBigHuntQuest(ctx context.Context, req *pb.F
|
||||
var scoreInfo *pb.BigHuntScoreInfo
|
||||
var scoreRewards []*pb.BigHuntReward
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleBigHuntQuestFinish(user, bhQuest.QuestId, req.IsRetired, false, nowMillis)
|
||||
|
||||
if req.IsRetired || user.BigHuntProgress.IsDryRun {
|
||||
@@ -229,18 +210,13 @@ func (s *BigHuntServiceServer) FinishBigHuntQuest(ctx context.Context, req *pb.F
|
||||
BattleReport: &pb.BigHuntBattleReport{
|
||||
BattleReportWave: []*pb.BigHuntBattleReportWave{},
|
||||
},
|
||||
DiffUserData: buildBigHuntDiff(user, append([]string{
|
||||
"IUserQuest",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
}, bigHuntDiffTables...)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) RestartBigHuntQuest(ctx context.Context, req *pb.RestartBigHuntQuestRequest) (*pb.RestartBigHuntQuestResponse, error) {
|
||||
log.Printf("[BigHuntService] RestartBigHuntQuest: bossQuestId=%d questId=%d", req.BigHuntBossQuestId, req.BigHuntQuestId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
bhQuest := s.catalog.QuestById[req.BigHuntQuestId]
|
||||
@@ -248,7 +224,7 @@ func (s *BigHuntServiceServer) RestartBigHuntQuest(ctx context.Context, req *pb.
|
||||
var battleBinary []byte
|
||||
var deckNumber int32
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleBigHuntQuestStart(user, bhQuest.QuestId, user.BigHuntDeckNumber, nowMillis)
|
||||
|
||||
user.BigHuntProgress.CurrentQuestSceneId = 0
|
||||
@@ -267,17 +243,16 @@ func (s *BigHuntServiceServer) RestartBigHuntQuest(ctx context.Context, req *pb.
|
||||
return &pb.RestartBigHuntQuestResponse{
|
||||
BattleBinary: battleBinary,
|
||||
DeckNumber: deckNumber,
|
||||
DiffUserData: buildBigHuntDiff(user, append([]string{"IUserQuest"}, bigHuntDiffTables...)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) SkipBigHuntQuest(ctx context.Context, req *pb.SkipBigHuntQuestRequest) (*pb.SkipBigHuntQuestResponse, error) {
|
||||
log.Printf("[BigHuntService] SkipBigHuntQuest: bossQuestId=%d skipCount=%d", req.BigHuntBossQuestId, req.SkipCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
st := user.BigHuntStatuses[req.BigHuntBossQuestId]
|
||||
st.DailyChallengeCount += req.SkipCount
|
||||
st.LatestChallengeDatetime = nowMillis
|
||||
@@ -286,15 +261,14 @@ func (s *BigHuntServiceServer) SkipBigHuntQuest(ctx context.Context, req *pb.Ski
|
||||
})
|
||||
|
||||
return &pb.SkipBigHuntQuestResponse{
|
||||
ScoreReward: []*pb.BigHuntReward{},
|
||||
DiffUserData: buildBigHuntDiff(user, bigHuntDiffTables),
|
||||
ScoreReward: []*pb.BigHuntReward{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) SaveBigHuntBattleInfo(ctx context.Context, req *pb.SaveBigHuntBattleInfoRequest) (*pb.SaveBigHuntBattleInfoResponse, error) {
|
||||
log.Printf("[BigHuntService] SaveBigHuntBattleInfo: elapsedFrames=%d", req.ElapsedFrameCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
var totalDamage int64
|
||||
@@ -306,7 +280,7 @@ func (s *BigHuntServiceServer) SaveBigHuntBattleInfo(ctx context.Context, req *p
|
||||
}
|
||||
}
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.BigHuntBattleBinary = req.BattleBinary
|
||||
|
||||
if req.BigHuntBattleDetail != nil {
|
||||
@@ -322,15 +296,13 @@ func (s *BigHuntServiceServer) SaveBigHuntBattleInfo(ctx context.Context, req *p
|
||||
user.BigHuntProgress.LatestVersion = nowMillis
|
||||
})
|
||||
|
||||
return &pb.SaveBigHuntBattleInfoResponse{
|
||||
DiffUserData: buildBigHuntDiff(user, []string{"IUserBigHuntProgressStatus"}),
|
||||
}, nil
|
||||
return &pb.SaveBigHuntBattleInfoResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *BigHuntServiceServer) GetBigHuntTopData(ctx context.Context, _ *emptypb.Empty) (*pb.GetBigHuntTopDataResponse, error) {
|
||||
log.Printf("[BigHuntService] GetBigHuntTopData")
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.LoadUser(userId)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
@@ -368,7 +340,6 @@ func (s *BigHuntServiceServer) GetBigHuntTopData(ctx context.Context, _ *emptypb
|
||||
WeeklyScoreReward: weeklyRewards,
|
||||
IsReceivedWeeklyScoreReward: ws.IsReceivedWeeklyReward,
|
||||
LastWeekWeeklyScoreReward: lastWeekRewards,
|
||||
DiffUserData: buildBigHuntDiff(user, bigHuntDiffTables),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -16,9 +15,9 @@ import (
|
||||
func (s *QuestServiceServer) StartEventQuest(ctx context.Context, req *pb.StartEventQuestRequest) (*pb.StartEventQuestResponse, error) {
|
||||
log.Printf("[QuestService] StartEventQuest: chapterId=%d questId=%d isBattleOnly=%v", req.EventQuestChapterId, req.QuestId, req.IsBattleOnly)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleEventQuestStart(user, req.EventQuestChapterId, req.QuestId, req.IsBattleOnly, req.UserDeckNumber, nowMillis)
|
||||
})
|
||||
|
||||
@@ -34,12 +33,6 @@ func (s *QuestServiceServer) StartEventQuest(ctx context.Context, req *pb.StartE
|
||||
|
||||
return &pb.StartEventQuestResponse{
|
||||
BattleDropReward: pbDrops,
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserStatus",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserEventQuestProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -47,34 +40,12 @@ func (s *QuestServiceServer) FinishEventQuest(ctx context.Context, req *pb.Finis
|
||||
log.Printf("[QuestService] FinishEventQuest: chapterId=%d questId=%d isRetired=%v isAnnihilated=%v", req.EventQuestChapterId, req.QuestId, req.IsRetired, req.IsAnnihilated)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var outcome questflow.FinishOutcome
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
outcome = s.engine.HandleEventQuestFinish(user, req.EventQuestChapterId, req.QuestId, req.IsRetired, req.IsAnnihilated, nowMillis)
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserEventQuestProgressStatus",
|
||||
"IUserStatus",
|
||||
"IUserGem",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, outcome.ChangedWeaponStoryIds)
|
||||
|
||||
return &pb.FinishEventQuestResponse{
|
||||
DropReward: toProtoRewards(outcome.DropRewards),
|
||||
FirstClearReward: toProtoRewards(outcome.FirstClearRewards),
|
||||
@@ -84,55 +55,31 @@ func (s *QuestServiceServer) FinishEventQuest(ctx context.Context, req *pb.Finis
|
||||
IsBigWin: outcome.IsBigWin,
|
||||
BigWinClearedQuestMissionIdList: outcome.BigWinClearedQuestMissionIds,
|
||||
UserStatusCampaignReward: []*pb.QuestReward{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) RestartEventQuest(ctx context.Context, req *pb.RestartEventQuestRequest) (*pb.RestartEventQuestResponse, error) {
|
||||
log.Printf("[QuestService] RestartEventQuest: chapterId=%d questId=%d", req.EventQuestChapterId, req.QuestId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleEventQuestRestart(user, req.EventQuestChapterId, req.QuestId, gametime.NowMillis())
|
||||
})
|
||||
|
||||
return &pb.RestartEventQuestResponse{
|
||||
BattleDropReward: []*pb.BattleDropReward{},
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserEventQuestProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) UpdateEventQuestSceneProgress(ctx context.Context, req *pb.UpdateEventQuestSceneProgressRequest) (*pb.UpdateEventQuestSceneProgressResponse, error) {
|
||||
log.Printf("[QuestService] UpdateEventQuestSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleEventQuestSceneProgress(user, req.QuestSceneId, gametime.NowMillis())
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserEventQuestProgressStatus",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, s.engine.Granter.DrainChangedStoryWeaponIds())
|
||||
|
||||
return &pb.UpdateEventQuestSceneProgressResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.UpdateEventQuestSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
const defaultGuerrillaFreeOpenMinutes = int32(60)
|
||||
@@ -140,16 +87,14 @@ const defaultGuerrillaFreeOpenMinutes = int32(60)
|
||||
func (s *QuestServiceServer) StartGuerrillaFreeOpen(ctx context.Context, req *emptypb.Empty) (*pb.StartGuerrillaFreeOpenResponse, error) {
|
||||
log.Printf("[QuestService] StartGuerrillaFreeOpen")
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.GuerrillaFreeOpen.StartDatetime = nowMillis
|
||||
user.GuerrillaFreeOpen.OpenMinutes = defaultGuerrillaFreeOpenMinutes
|
||||
user.GuerrillaFreeOpen.DailyOpenedCount++
|
||||
user.GuerrillaFreeOpen.LatestVersion = nowMillis
|
||||
})
|
||||
|
||||
return &pb.StartGuerrillaFreeOpenResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{"IUserEventQuestGuerrillaFreeOpen"}),
|
||||
}, nil
|
||||
return &pb.StartGuerrillaFreeOpenResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
func (s *QuestServiceServer) StartExtraQuest(ctx context.Context, req *pb.StartExtraQuestRequest) (*pb.StartExtraQuestResponse, error) {
|
||||
log.Printf("[QuestService] StartExtraQuest: questId=%d deckNumber=%d", req.QuestId, req.UserDeckNumber)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleExtraQuestStart(user, req.QuestId, req.UserDeckNumber, nowMillis)
|
||||
})
|
||||
|
||||
@@ -32,12 +31,6 @@ func (s *QuestServiceServer) StartExtraQuest(ctx context.Context, req *pb.StartE
|
||||
|
||||
return &pb.StartExtraQuestResponse{
|
||||
BattleDropReward: pbDrops,
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserStatus",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserExtraQuestProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -45,34 +38,12 @@ func (s *QuestServiceServer) FinishExtraQuest(ctx context.Context, req *pb.Finis
|
||||
log.Printf("[QuestService] FinishExtraQuest: questId=%d isRetired=%v isAnnihilated=%v", req.QuestId, req.IsRetired, req.IsAnnihilated)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var outcome questflow.FinishOutcome
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
outcome = s.engine.HandleExtraQuestFinish(user, req.QuestId, req.IsRetired, req.IsAnnihilated, nowMillis)
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserExtraQuestProgressStatus",
|
||||
"IUserStatus",
|
||||
"IUserGem",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, outcome.ChangedWeaponStoryIds)
|
||||
|
||||
return &pb.FinishExtraQuestResponse{
|
||||
DropReward: toProtoRewards(outcome.DropRewards),
|
||||
FirstClearReward: toProtoRewards(outcome.FirstClearRewards),
|
||||
@@ -81,16 +52,17 @@ func (s *QuestServiceServer) FinishExtraQuest(ctx context.Context, req *pb.Finis
|
||||
IsBigWin: outcome.IsBigWin,
|
||||
BigWinClearedQuestMissionIdList: outcome.BigWinClearedQuestMissionIds,
|
||||
UserStatusCampaignReward: []*pb.QuestReward{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) RestartExtraQuest(ctx context.Context, req *pb.RestartExtraQuestRequest) (*pb.RestartExtraQuestResponse, error) {
|
||||
log.Printf("[QuestService] RestartExtraQuest: questId=%d", req.QuestId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var deckNumber int32
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleExtraQuestRestart(user, req.QuestId, gametime.NowMillis())
|
||||
deckNumber = user.Quests[req.QuestId].UserDeckNumber
|
||||
})
|
||||
|
||||
drops := s.engine.BattleDropRewards(req.QuestId)
|
||||
@@ -105,40 +77,17 @@ func (s *QuestServiceServer) RestartExtraQuest(ctx context.Context, req *pb.Rest
|
||||
|
||||
return &pb.RestartExtraQuestResponse{
|
||||
BattleDropReward: pbDrops,
|
||||
DeckNumber: user.Quests[req.QuestId].UserDeckNumber,
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserExtraQuestProgressStatus",
|
||||
}),
|
||||
DeckNumber: deckNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) UpdateExtraQuestSceneProgress(ctx context.Context, req *pb.UpdateExtraQuestSceneProgressRequest) (*pb.UpdateExtraQuestSceneProgressResponse, error) {
|
||||
log.Printf("[QuestService] UpdateExtraQuestSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleExtraQuestSceneProgress(user, req.QuestSceneId, gametime.NowMillis())
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserExtraQuestProgressStatus",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, s.engine.Granter.DrainChangedStoryWeaponIds())
|
||||
|
||||
return &pb.UpdateExtraQuestSceneProgressResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.UpdateExtraQuestSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -28,91 +27,45 @@ func NewQuestServiceServer(users store.UserRepository, sessions store.SessionRep
|
||||
return &QuestServiceServer{users: users, sessions: sessions, engine: engine}
|
||||
}
|
||||
|
||||
func buildSelectedQuestDiff(user store.UserState, tableNames []string) map[string]*pb.DiffData {
|
||||
tables := userdata.ProjectTables(user, tableNames)
|
||||
return userdata.BuildDiffFromTablesOrdered(tables, tableNames)
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) UpdateMainFlowSceneProgress(ctx context.Context, req *pb.UpdateMainFlowSceneProgressRequest) (*pb.UpdateMainFlowSceneProgressResponse, error) {
|
||||
log.Printf("[QuestService] UpdateMainFlowSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleMainFlowSceneProgress(user, req.QuestSceneId, gametime.NowMillis())
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
"IUserMainQuestSeasonRoute",
|
||||
"IUserPortalCageStatus",
|
||||
"IUserSideStoryQuestSceneProgressStatus",
|
||||
"IUserQuest",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, s.engine.Granter.DrainChangedStoryWeaponIds())
|
||||
|
||||
return &pb.UpdateMainFlowSceneProgressResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.UpdateMainFlowSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) UpdateReplayFlowSceneProgress(ctx context.Context, req *pb.UpdateReplayFlowSceneProgressRequest) (*pb.UpdateReplayFlowSceneProgressResponse, error) {
|
||||
log.Printf("[QuestService] UpdateReplayFlowSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleReplayFlowSceneProgress(user, req.QuestSceneId, gametime.NowMillis())
|
||||
})
|
||||
|
||||
return &pb.UpdateReplayFlowSceneProgressResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestReplayFlowStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.UpdateReplayFlowSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) UpdateMainQuestSceneProgress(ctx context.Context, req *pb.UpdateMainQuestSceneProgressRequest) (*pb.UpdateMainQuestSceneProgressResponse, error) {
|
||||
log.Printf("[QuestService] UpdateMainQuestSceneProgress: questSceneId=%d", req.QuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleMainQuestSceneProgress(user, req.QuestSceneId)
|
||||
})
|
||||
|
||||
return &pb.UpdateMainQuestSceneProgressResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserStatus",
|
||||
"IUserCharacter",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.UpdateMainQuestSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) StartMainQuest(ctx context.Context, req *pb.StartMainQuestRequest) (*pb.StartMainQuestResponse, error) {
|
||||
log.Printf("[QuestService] StartMainQuest: %+v", req)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if req.IsReplayFlow {
|
||||
s.engine.HandleQuestStartReplay(user, req.QuestId, req.IsBattleOnly, req.UserDeckNumber, nowMillis)
|
||||
} else {
|
||||
@@ -132,16 +85,6 @@ func (s *QuestServiceServer) StartMainQuest(ctx context.Context, req *pb.StartMa
|
||||
|
||||
return &pb.StartMainQuestResponse{
|
||||
BattleDropReward: pbDrops,
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserStatus",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
"IUserMainQuestSeasonRoute",
|
||||
"IUserMainQuestReplayFlowStatus",
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -165,38 +108,12 @@ func (s *QuestServiceServer) FinishMainQuest(ctx context.Context, req *pb.Finish
|
||||
req.QuestId, req.IsMainFlow, req.IsRetired, req.IsAnnihilated, req.StorySkipType)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var outcome questflow.FinishOutcome
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
outcome = s.engine.HandleQuestFinish(user, req.QuestId, req.IsRetired, req.IsAnnihilated, nowMillis)
|
||||
})
|
||||
|
||||
diff := buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
"IUserMainQuestSeasonRoute",
|
||||
"IUserMainQuestReplayFlowStatus",
|
||||
"IUserStatus",
|
||||
"IUserGem",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
"IUserCompanion",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
})
|
||||
userdata.AddWeaponStoryDiff(diff, user, outcome.ChangedWeaponStoryIds)
|
||||
|
||||
return &pb.FinishMainQuestResponse{
|
||||
DropReward: toProtoRewards(outcome.DropRewards),
|
||||
FirstClearReward: toProtoRewards(outcome.FirstClearRewards),
|
||||
@@ -207,16 +124,17 @@ func (s *QuestServiceServer) FinishMainQuest(ctx context.Context, req *pb.Finish
|
||||
BigWinClearedQuestMissionIdList: outcome.BigWinClearedQuestMissionIds,
|
||||
ReplayFlowFirstClearReward: toProtoRewards(outcome.ReplayFlowFirstClearRewards),
|
||||
UserStatusCampaignReward: []*pb.QuestReward{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) RestartMainQuest(ctx context.Context, req *pb.RestartMainQuestRequest) (*pb.RestartMainQuestResponse, error) {
|
||||
log.Printf("[QuestService] RestartMainQuest: questId=%d isMainFlow=%v", req.QuestId, req.IsMainFlow)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var deckNumber int32
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.engine.HandleQuestRestart(user, req.QuestId, gametime.NowMillis())
|
||||
deckNumber = user.Quests[req.QuestId].UserDeckNumber
|
||||
})
|
||||
|
||||
drops := s.engine.BattleDropRewards(req.QuestId)
|
||||
@@ -231,33 +149,22 @@ func (s *QuestServiceServer) RestartMainQuest(ctx context.Context, req *pb.Resta
|
||||
|
||||
return &pb.RestartMainQuestResponse{
|
||||
BattleDropReward: pbDrops,
|
||||
DeckNumber: user.Quests[req.QuestId].UserDeckNumber,
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserStatus",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
"IUserMainQuestSeasonRoute",
|
||||
}),
|
||||
DeckNumber: deckNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) FinishAutoOrbit(ctx context.Context, req *emptypb.Empty) (*pb.FinishAutoOrbitResponse, error) {
|
||||
log.Printf("[QuestService] FinishAutoOrbit")
|
||||
return &pb.FinishAutoOrbitResponse{
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
return &pb.FinishAutoOrbitResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) SkipQuest(ctx context.Context, req *pb.SkipQuestRequest) (*pb.SkipQuestResponse, error) {
|
||||
log.Printf("[QuestService] SkipQuest: questId=%d skipCount=%d useEffectItems=%d", req.QuestId, req.SkipCount, len(req.UseEffectItem))
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
var outcome questflow.FinishOutcome
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, item := range req.UseEffectItem {
|
||||
log.Printf("[QuestService] SkipQuest UseEffectItem: consumableItemId=%d count=%d", item.ConsumableItemId, item.Count)
|
||||
user.ConsumableItems[item.ConsumableItemId] -= item.Count
|
||||
@@ -271,24 +178,14 @@ func (s *QuestServiceServer) SkipQuest(ctx context.Context, req *pb.SkipQuestReq
|
||||
return &pb.SkipQuestResponse{
|
||||
DropReward: toProtoRewards(outcome.DropRewards),
|
||||
UserStatusCampaignReward: []*pb.QuestReward{},
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserQuest",
|
||||
"IUserStatus",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserParts",
|
||||
"IUserPartsGroupNote",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) SetRoute(ctx context.Context, req *pb.SetRouteRequest) (*pb.SetRouteResponse, error) {
|
||||
log.Printf("[QuestService] SetRoute: mainQuestRouteId=%d", req.MainQuestRouteId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.MainQuest.CurrentMainQuestRouteId = req.MainQuestRouteId
|
||||
if seasonId, ok := s.engine.SeasonIdByRouteId[req.MainQuestRouteId]; ok {
|
||||
user.MainQuest.MainQuestSeasonId = seasonId
|
||||
@@ -298,30 +195,22 @@ func (s *QuestServiceServer) SetRoute(ctx context.Context, req *pb.SetRouteReque
|
||||
user.PortalCageStatus.LatestVersion = now
|
||||
})
|
||||
|
||||
return &pb.SetRouteResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserMainQuestSeasonRoute",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserPortalCageStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.SetRouteResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) SetQuestSceneChoice(ctx context.Context, req *pb.SetQuestSceneChoiceRequest) (*pb.SetQuestSceneChoiceResponse, error) {
|
||||
log.Printf("[QuestService] SetQuestSceneChoice: questSceneId=%d choiceNumber=%d",
|
||||
req.QuestSceneId, req.ChoiceNumber)
|
||||
return &pb.SetQuestSceneChoiceResponse{
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
return &pb.SetQuestSceneChoiceResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) ResetLimitContentQuestProgress(ctx context.Context, req *pb.ResetLimitContentQuestProgressRequest) (*pb.ResetLimitContentQuestProgressResponse, error) {
|
||||
log.Printf("[QuestService] ResetLimitContentQuestProgress: eventQuestChapterId=%d questId=%d",
|
||||
req.EventQuestChapterId, req.QuestId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if _, exists := user.SideStoryQuests[req.QuestId]; exists {
|
||||
user.SideStoryQuests[req.QuestId] = store.SideStoryQuestProgress{
|
||||
HeadSideStoryQuestSceneId: 0,
|
||||
@@ -339,20 +228,14 @@ func (s *QuestServiceServer) ResetLimitContentQuestProgress(ctx context.Context,
|
||||
}
|
||||
})
|
||||
|
||||
return &pb.ResetLimitContentQuestProgressResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserSideStoryQuest",
|
||||
"IUserSideStoryQuestSceneProgressStatus",
|
||||
"IUserQuestLimitContentStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.ResetLimitContentQuestProgressResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *QuestServiceServer) SetAutoSaleSetting(ctx context.Context, req *pb.SetAutoSaleSettingRequest) (*pb.SetAutoSaleSettingResponse, error) {
|
||||
log.Printf("[QuestService] SetAutoSaleSetting: items=%d", len(req.AutoSaleSettingItem))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.AutoSaleSettings = make(map[int32]store.AutoSaleSettingState, len(req.AutoSaleSettingItem))
|
||||
for itemType, itemValue := range req.AutoSaleSettingItem {
|
||||
user.AutoSaleSettings[itemType] = store.AutoSaleSettingState{
|
||||
@@ -362,9 +245,5 @@ func (s *QuestServiceServer) SetAutoSaleSetting(ctx context.Context, req *pb.Set
|
||||
}
|
||||
})
|
||||
|
||||
return &pb.SetAutoSaleSettingResponse{
|
||||
DiffUserData: buildSelectedQuestDiff(user, []string{
|
||||
"IUserAutoSaleSettingDetail",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.SetAutoSaleSettingResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type SideStoryQuestServiceServer struct {
|
||||
@@ -23,19 +22,14 @@ func NewSideStoryQuestServiceServer(users store.UserRepository, sessions store.S
|
||||
return &SideStoryQuestServiceServer{users: users, sessions: sessions, catalog: catalog}
|
||||
}
|
||||
|
||||
func buildSideStoryDiff(user store.UserState, tableNames []string) map[string]*pb.DiffData {
|
||||
tables := userdata.ProjectTables(user, tableNames)
|
||||
return userdata.BuildDiffFromTablesOrdered(tables, tableNames)
|
||||
}
|
||||
|
||||
func (s *SideStoryQuestServiceServer) MoveSideStoryQuestProgress(ctx context.Context, req *pb.MoveSideStoryQuestRequest) (*pb.MoveSideStoryQuestResponse, error) {
|
||||
log.Printf("[SideStoryQuestService] MoveSideStoryQuestProgress: sideStoryQuestId=%d", req.SideStoryQuestId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
firstSceneId := s.catalog.FirstSceneByQuestId[req.SideStoryQuestId]
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
existing, exists := user.SideStoryQuests[req.SideStoryQuestId]
|
||||
|
||||
var sceneId int32
|
||||
@@ -58,21 +52,16 @@ func (s *SideStoryQuestServiceServer) MoveSideStoryQuestProgress(ctx context.Con
|
||||
}
|
||||
})
|
||||
|
||||
return &pb.MoveSideStoryQuestResponse{
|
||||
DiffUserData: buildSideStoryDiff(user, []string{
|
||||
"IUserSideStoryQuest",
|
||||
"IUserSideStoryQuestSceneProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.MoveSideStoryQuestResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *SideStoryQuestServiceServer) UpdateSideStoryQuestSceneProgress(ctx context.Context, req *pb.UpdateSideStoryQuestSceneProgressRequest) (*pb.UpdateSideStoryQuestSceneProgressResponse, error) {
|
||||
log.Printf("[SideStoryQuestService] UpdateSideStoryQuestSceneProgress: sideStoryQuestId=%d sceneId=%d",
|
||||
req.SideStoryQuestId, req.SideStoryQuestSceneId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.SideStoryActiveProgress.CurrentSideStoryQuestSceneId = req.SideStoryQuestSceneId
|
||||
user.SideStoryActiveProgress.LatestVersion = nowMillis
|
||||
|
||||
@@ -84,10 +73,5 @@ func (s *SideStoryQuestServiceServer) UpdateSideStoryQuestSceneProgress(ctx cont
|
||||
user.SideStoryQuests[req.SideStoryQuestId] = progress
|
||||
})
|
||||
|
||||
return &pb.UpdateSideStoryQuestSceneProgressResponse{
|
||||
DiffUserData: buildSideStoryDiff(user, []string{
|
||||
"IUserSideStoryQuest",
|
||||
"IUserSideStoryQuestSceneProgressStatus",
|
||||
}),
|
||||
}, nil
|
||||
return &pb.UpdateSideStoryQuestSceneProgressResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ func NewRewardServiceServer(
|
||||
func (s *RewardServiceServer) ReceiveBigHuntReward(ctx context.Context, _ *emptypb.Empty) (*pb.ReceiveBigHuntRewardResponse, error) {
|
||||
log.Printf("[RewardService] ReceiveBigHuntReward")
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
weeklyVersion := gametime.WeeklyVersion(nowMillis)
|
||||
|
||||
@@ -42,7 +41,7 @@ func (s *RewardServiceServer) ReceiveBigHuntReward(ctx context.Context, _ *empty
|
||||
var weeklyRewards []*pb.BigHuntReward
|
||||
isReceived := false
|
||||
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
ws := user.BigHuntWeeklyStatuses[weeklyVersion]
|
||||
isReceived = ws.IsReceivedWeeklyReward
|
||||
|
||||
@@ -106,19 +105,11 @@ func (s *RewardServiceServer) ReceiveBigHuntReward(ctx context.Context, _ *empty
|
||||
weeklyScoreResults = []*pb.WeeklyScoreResult{}
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(user, []string{
|
||||
"IUserBigHuntWeeklyStatus",
|
||||
"IUserBigHuntWeeklyMaxScore",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
})
|
||||
|
||||
return &pb.ReceiveBigHuntRewardResponse{
|
||||
WeeklyScoreResult: weeklyScoreResults,
|
||||
WeeklyScoreReward: weeklyRewards,
|
||||
IsReceivedWeeklyScoreReward: isReceived,
|
||||
LastWeekWeeklyScoreReward: []*pb.BigHuntReward{},
|
||||
DiffUserData: userdata.BuildDiffFromTables(tables),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,10 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
var shopDiffTables = []string{
|
||||
"IUserShopItem",
|
||||
"IUserShopReplaceable",
|
||||
"IUserShopReplaceableLineup",
|
||||
"IUserGem",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
"IUserPremiumItem",
|
||||
"IUserStatus",
|
||||
"IUserCostume",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserCharacter",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponNote",
|
||||
}
|
||||
|
||||
type ShopServiceServer struct {
|
||||
pb.UnimplementedShopServiceServer
|
||||
users store.UserRepository
|
||||
@@ -49,10 +29,10 @@ func NewShopServiceServer(users store.UserRepository, sessions store.SessionRepo
|
||||
func (s *ShopServiceServer) Buy(ctx context.Context, req *pb.BuyRequest) (*pb.BuyResponse, error) {
|
||||
log.Printf("[ShopService] Buy: shopId=%d items=%v", req.ShopId, req.ShopItems)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for shopItemId, qty := range req.ShopItems {
|
||||
item, ok := s.catalog.Items[shopItemId]
|
||||
if !ok {
|
||||
@@ -88,23 +68,18 @@ func (s *ShopServiceServer) Buy(ctx context.Context, req *pb.BuyRequest) (*pb.Bu
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("shop buy: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, shopDiffTables))
|
||||
userdata.AddWeaponStoryDiff(diff, snapshot, s.granter.DrainChangedStoryWeaponIds())
|
||||
|
||||
return &pb.BuyResponse{
|
||||
OverflowPossession: []*pb.Possession{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ShopServiceServer) RefreshUserData(ctx context.Context, req *pb.RefreshRequest) (*pb.RefreshResponse, error) {
|
||||
log.Printf("[ShopService] RefreshUserData: isGemUsed=%v", req.IsGemUsed)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
if len(user.ShopReplaceableLineup) == 0 && len(s.catalog.ItemShopPool) > 0 {
|
||||
for i, itemId := range s.catalog.ItemShopPool {
|
||||
slot := int32(i + 1)
|
||||
@@ -131,18 +106,13 @@ func (s *ShopServiceServer) RefreshUserData(ctx context.Context, req *pb.Refresh
|
||||
return nil, fmt.Errorf("shop refresh: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, shopDiffTables))
|
||||
|
||||
return &pb.RefreshResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.RefreshResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *ShopServiceServer) GetCesaLimit(_ context.Context, _ *emptypb.Empty) (*pb.GetCesaLimitResponse, error) {
|
||||
log.Printf("[ShopService] GetCesaLimit")
|
||||
return &pb.GetCesaLimitResponse{
|
||||
CesaLimit: []*pb.CesaLimit{},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
CesaLimit: []*pb.CesaLimit{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -150,10 +120,10 @@ func (s *ShopServiceServer) CreatePurchaseTransaction(ctx context.Context, req *
|
||||
log.Printf("[ShopService] CreatePurchaseTransaction: shopId=%d shopItemId=%d productId=%s",
|
||||
req.ShopId, req.ShopItemId, req.ProductId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
item, ok := s.catalog.Items[req.ShopItemId]
|
||||
if !ok {
|
||||
log.Printf("[ShopService] CreatePurchaseTransaction: unknown shopItemId=%d", req.ShopItemId)
|
||||
@@ -193,28 +163,22 @@ func (s *ShopServiceServer) CreatePurchaseTransaction(ctx context.Context, req *
|
||||
|
||||
txId := fmt.Sprintf("tx_%d_%d_%d", userId, req.ShopItemId, nowMillis)
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, shopDiffTables))
|
||||
|
||||
return &pb.CreatePurchaseTransactionResponse{
|
||||
PurchaseTransactionId: txId,
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ShopServiceServer) PurchaseGooglePlayStoreProduct(ctx context.Context, req *pb.PurchaseGooglePlayStoreProductRequest) (*pb.PurchaseGooglePlayStoreProductResponse, error) {
|
||||
log.Printf("[ShopService] PurchaseGooglePlayStoreProduct: txId=%s", req.PurchaseTransactionId)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
snapshot, err := s.users.LoadUser(userId)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
_, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("purchase google play: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, shopDiffTables))
|
||||
|
||||
return &pb.PurchaseGooglePlayStoreProductResponse{
|
||||
OverflowPossession: []*pb.Possession{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,43 +8,7 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
var startedGameStartTables = []string{
|
||||
"IUserProfile",
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserCompanion",
|
||||
"IUserDeckCharacter",
|
||||
"IUserDeck",
|
||||
"IUserGem",
|
||||
"IUserMission",
|
||||
"IUserMainQuestFlowStatus",
|
||||
"IUserMainQuestMainFlowStatus",
|
||||
"IUserMainQuestProgressStatus",
|
||||
"IUserMainQuestSeasonRoute",
|
||||
"IUserQuest",
|
||||
"IUserQuestMission",
|
||||
"IUserTutorialProgress",
|
||||
"IUserWeaponNote",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserDeckTypeNote",
|
||||
"IUserDeckSubWeaponGroup",
|
||||
"IUserDeckPartsGroup",
|
||||
"IUserConsumableItem",
|
||||
"IUserMaterial",
|
||||
"IUserImportantItem",
|
||||
}
|
||||
|
||||
var gimmickDiffTables = []string{
|
||||
"IUserGimmick",
|
||||
"IUserGimmickOrnamentProgress",
|
||||
"IUserGimmickSequence",
|
||||
"IUserGimmickUnlock",
|
||||
}
|
||||
|
||||
func currentUserId(ctx context.Context, users store.UserRepository, sessions store.SessionRepository) int64 {
|
||||
func CurrentUserId(ctx context.Context, users store.UserRepository, sessions store.SessionRepository) int64 {
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
if vals := md.Get("x-apb-session-key"); len(vals) > 0 {
|
||||
if userId, err := sessions.ResolveUserId(vals[0]); err == nil {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
type TutorialServiceServer struct {
|
||||
@@ -25,10 +24,10 @@ func NewTutorialServiceServer(users store.UserRepository, sessions store.Session
|
||||
|
||||
func (s *TutorialServiceServer) SetTutorialProgress(ctx context.Context, req *pb.SetTutorialProgressRequest) (*pb.SetTutorialProgressResponse, error) {
|
||||
log.Printf("[TutorialService] SetTutorialProgress: type=%d phase=%d choice=%d", req.TutorialType, req.ProgressPhase, req.ChoiceId)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
var grants []questflow.RewardGrant
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
existing, exists := user.Tutorials[req.TutorialType]
|
||||
if !exists || req.ProgressPhase >= existing.ProgressPhase {
|
||||
user.Tutorials[req.TutorialType] = store.TutorialProgressState{
|
||||
@@ -42,22 +41,7 @@ func (s *TutorialServiceServer) SetTutorialProgress(ctx context.Context, req *pb
|
||||
store.EnsureDefaultDeck(user, nowMillis)
|
||||
}
|
||||
})
|
||||
tables := []string{"IUserTutorialProgress"}
|
||||
if req.TutorialType == int32(model.TutorialTypeMenuFirst) ||
|
||||
req.TutorialType == int32(model.TutorialTypeMenuSecond) {
|
||||
tables = append(tables,
|
||||
"IUserCharacter", "IUserCostume", "IUserWeapon",
|
||||
"IUserWeaponSkill", "IUserWeaponAbility",
|
||||
"IUserCompanion", "IUserDeckCharacter", "IUserDeck",
|
||||
)
|
||||
}
|
||||
if len(grants) > 0 {
|
||||
tables = append(tables, "IUserCompanion")
|
||||
}
|
||||
result := userdata.ProjectTables(user, tables)
|
||||
for _, t := range tables {
|
||||
log.Printf("[TutorialService] DiffTable %s -> %s", t, result[t])
|
||||
}
|
||||
|
||||
rewards := make([]*pb.TutorialChoiceReward, len(grants))
|
||||
for i, g := range grants {
|
||||
rewards[i] = &pb.TutorialChoiceReward{
|
||||
@@ -68,14 +52,13 @@ func (s *TutorialServiceServer) SetTutorialProgress(ctx context.Context, req *pb
|
||||
}
|
||||
return &pb.SetTutorialProgressResponse{
|
||||
TutorialChoiceReward: rewards,
|
||||
DiffUserData: userdata.BuildDiffFromTables(result),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *TutorialServiceServer) SetTutorialProgressAndReplaceDeck(ctx context.Context, req *pb.SetTutorialProgressAndReplaceDeckRequest) (*pb.SetTutorialProgressAndReplaceDeckResponse, error) {
|
||||
log.Printf("[TutorialService] SetTutorialProgressAndReplaceDeck: type=%d phase=%d deckType=%d deckNumber=%d", req.TutorialType, req.ProgressPhase, req.DeckType, req.UserDeckNumber)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
existing, exists := user.Tutorials[req.TutorialType]
|
||||
if !exists || req.ProgressPhase >= existing.ProgressPhase {
|
||||
user.Tutorials[req.TutorialType] = store.TutorialProgressState{
|
||||
@@ -87,12 +70,5 @@ func (s *TutorialServiceServer) SetTutorialProgressAndReplaceDeck(ctx context.Co
|
||||
store.ApplyDeckReplacement(user, model.DeckType(req.DeckType), req.UserDeckNumber, deckSlotsFromProto(req.Deck), gametime.NowMillis())
|
||||
}
|
||||
})
|
||||
return &pb.SetTutorialProgressAndReplaceDeckResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{
|
||||
"IUserTutorialProgress",
|
||||
"IUserDeck",
|
||||
"IUserDeckCharacter",
|
||||
"IUserDeckSubWeaponGroup",
|
||||
})),
|
||||
}, nil
|
||||
return &pb.SetTutorialProgressAndReplaceDeckResponse{}, nil
|
||||
}
|
||||
|
||||
+142
-90
@@ -2,73 +2,56 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type UserServiceServer struct {
|
||||
pb.UnimplementedUserServiceServer
|
||||
users store.UserRepository
|
||||
sessions store.SessionRepository
|
||||
authURL string
|
||||
}
|
||||
|
||||
func NewUserServiceServer(users store.UserRepository, sessions store.SessionRepository) *UserServiceServer {
|
||||
return &UserServiceServer{users: users, sessions: sessions}
|
||||
}
|
||||
|
||||
func setCommonResponseTrailers(ctx context.Context, diff map[string]*pb.DiffData, includeUpdateNames bool) {
|
||||
keys := make([]string, 0, len(diff))
|
||||
for key := range diff {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var pairs []string
|
||||
if includeUpdateNames && len(keys) > 0 {
|
||||
pairs = append(pairs, "x-apb-update-user-data-names", keys[0])
|
||||
for _, key := range keys[1:] {
|
||||
pairs[len(pairs)-1] += "," + key
|
||||
}
|
||||
}
|
||||
|
||||
if err := grpc.SetTrailer(ctx, metadata.Pairs(pairs...)); err != nil {
|
||||
log.Printf("[UserService] failed to set trailers: %v", err)
|
||||
func NewUserServiceServer(users store.UserRepository, sessions store.SessionRepository, authURL string) *UserServiceServer {
|
||||
if authURL != "" && !strings.Contains(authURL, "://") {
|
||||
authURL = "http://" + authURL
|
||||
}
|
||||
return &UserServiceServer{users: users, sessions: sessions, authURL: authURL}
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) RegisterUser(ctx context.Context, req *pb.RegisterUserRequest) (*pb.RegisterUserResponse, error) {
|
||||
userId, err := s.users.CreateUser(req.Uuid)
|
||||
platform := model.ClientPlatformFromContext(ctx)
|
||||
userId, err := s.users.CreateUser(req.Uuid, platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create user: %w", err)
|
||||
}
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load user: %w", err)
|
||||
}
|
||||
log.Printf("[UserService] RegisterUser: uuid=%s terminalId=%s -> userId=%d", req.Uuid, req.TerminalId, user.UserId)
|
||||
log.Printf("[UserService] RegisterUser: uuid=%s terminalId=%s platform=%s -> userId=%d", req.Uuid, req.TerminalId, platform, userId)
|
||||
|
||||
return &pb.RegisterUserResponse{
|
||||
UserId: user.UserId,
|
||||
Signature: fmt.Sprintf("sig_%d_%d", user.UserId, gametime.Now().Unix()),
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.FirstEntranceClientTableMap(user)),
|
||||
UserId: userId,
|
||||
Signature: fmt.Sprintf("sig_%d_%d", userId, gametime.Now().Unix()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) Auth(ctx context.Context, req *pb.AuthUserRequest) (*pb.AuthUserResponse, error) {
|
||||
log.Printf("[UserService] Auth: uuid=%s", req.Uuid)
|
||||
platform := model.ClientPlatformFromContext(ctx)
|
||||
log.Printf("[UserService] Auth: uuid=%s platform=%s", req.Uuid, platform)
|
||||
|
||||
session, err := s.sessions.CreateSession(req.Uuid, 24*time.Hour)
|
||||
if err != nil {
|
||||
@@ -84,7 +67,6 @@ func (s *UserServiceServer) Auth(ctx context.Context, req *pb.AuthUserRequest) (
|
||||
ExpireDatetime: timestamppb.New(session.ExpireAt),
|
||||
Signature: req.Signature,
|
||||
UserId: user.UserId,
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.FirstEntranceClientTableMap(user)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,81 +79,69 @@ func (s *UserServiceServer) GameStart(ctx context.Context, _ *emptypb.Empty) (*p
|
||||
}
|
||||
}
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.GameStartDatetime = gametime.NowMillis()
|
||||
})
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(user, startedGameStartTables))
|
||||
setCommonResponseTrailers(ctx, diff, true)
|
||||
|
||||
return &pb.GameStartResponse{
|
||||
// Apply only the starter outgame rows we need after title completion.
|
||||
// Keep IUser and other risky core-account rows out of GameStart diff.
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
return &pb.GameStartResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) TransferUser(ctx context.Context, req *pb.TransferUserRequest) (*pb.TransferUserResponse, error) {
|
||||
log.Printf("[UserService] TransferUser")
|
||||
userId, err := s.users.CreateUser(req.Uuid)
|
||||
platform := model.ClientPlatformFromContext(ctx)
|
||||
log.Printf("[UserService] TransferUser: platform=%s", platform)
|
||||
userId, err := s.users.CreateUser(req.Uuid, platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create user: %w", err)
|
||||
}
|
||||
return &pb.TransferUserResponse{
|
||||
UserId: userId,
|
||||
Signature: "transferred-sig",
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
UserId: userId,
|
||||
Signature: "transferred-sig",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetUserName(ctx context.Context, req *pb.SetUserNameRequest) (*pb.SetUserNameResponse, error) {
|
||||
log.Printf("[UserService] SetUserName: %s", req.Name)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
user.Profile.Name = req.Name
|
||||
user.Profile.NameUpdateDatetime = nowMillis
|
||||
})
|
||||
return &pb.SetUserNameResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserProfile"})),
|
||||
}, nil
|
||||
return &pb.SetUserNameResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetUserMessage(ctx context.Context, req *pb.SetUserMessageRequest) (*pb.SetUserMessageResponse, error) {
|
||||
log.Printf("[UserService] SetUserMessage: %s", req.Message)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
user.Profile.Message = req.Message
|
||||
user.Profile.MessageUpdateDatetime = nowMillis
|
||||
})
|
||||
return &pb.SetUserMessageResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserProfile"})),
|
||||
}, nil
|
||||
return &pb.SetUserMessageResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetUserFavoriteCostumeId(ctx context.Context, req *pb.SetUserFavoriteCostumeIdRequest) (*pb.SetUserFavoriteCostumeIdResponse, error) {
|
||||
log.Printf("[UserService] SetUserFavoriteCostumeId: %d", req.FavoriteCostumeId)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
nowMillis := gametime.NowMillis()
|
||||
user.Profile.FavoriteCostumeId = req.FavoriteCostumeId
|
||||
user.Profile.FavoriteCostumeIdUpdateDatetime = nowMillis
|
||||
})
|
||||
return &pb.SetUserFavoriteCostumeIdResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserProfile"})),
|
||||
}, nil
|
||||
return &pb.SetUserFavoriteCostumeIdResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetUserProfile(ctx context.Context, req *pb.GetUserProfileRequest) (*pb.GetUserProfileResponse, error) {
|
||||
log.Printf("[UserService] GetUserProfile: playerId=%d", req.PlayerId)
|
||||
userId := req.PlayerId
|
||||
if userId == 0 {
|
||||
userId = currentUserId(ctx, s.users, s.sessions)
|
||||
userId = CurrentUserId(ctx, s.users, s.sessions)
|
||||
}
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return &pb.GetUserProfileResponse{DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetUserProfileResponse{}, nil
|
||||
}
|
||||
|
||||
deckCharacters := []*pb.ProfileDeckCharacter{}
|
||||
@@ -210,66 +180,148 @@ func (s *UserServiceServer) GetUserProfile(ctx context.Context, req *pb.GetUserP
|
||||
HistoryItem: []*pb.PlayHistoryItem{},
|
||||
HistoryCategoryGraphItem: []*pb.PlayHistoryCategoryGraphItem{},
|
||||
},
|
||||
DiffUserData: userdata.EmptyDiff(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetBirthYearMonth(ctx context.Context, req *pb.SetBirthYearMonthRequest) (*pb.SetBirthYearMonthResponse, error) {
|
||||
log.Printf("[UserService] SetBirthYearMonth: %d/%d", req.BirthYear, req.BirthMonth)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
_, _ = s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.BirthYear = req.BirthYear
|
||||
user.BirthMonth = req.BirthMonth
|
||||
})
|
||||
return &pb.SetBirthYearMonthResponse{DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.SetBirthYearMonthResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetBirthYearMonth(ctx context.Context, _ *emptypb.Empty) (*pb.GetBirthYearMonthResponse, error) {
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return &pb.GetBirthYearMonthResponse{BirthYear: 2000, BirthMonth: 1, DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetBirthYearMonthResponse{BirthYear: 2000, BirthMonth: 1}, nil
|
||||
}
|
||||
return &pb.GetBirthYearMonthResponse{BirthYear: user.BirthYear, BirthMonth: user.BirthMonth, DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetBirthYearMonthResponse{BirthYear: user.BirthYear, BirthMonth: user.BirthMonth}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetChargeMoney(ctx context.Context, _ *emptypb.Empty) (*pb.GetChargeMoneyResponse, error) {
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return &pb.GetChargeMoneyResponse{ChargeMoneyThisMonth: 0, DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetChargeMoneyResponse{ChargeMoneyThisMonth: 0}, nil
|
||||
}
|
||||
return &pb.GetChargeMoneyResponse{ChargeMoneyThisMonth: user.ChargeMoneyThisMonth, DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetChargeMoneyResponse{ChargeMoneyThisMonth: user.ChargeMoneyThisMonth}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetUserSetting(ctx context.Context, req *pb.SetUserSettingRequest) (*pb.SetUserSettingResponse, error) {
|
||||
log.Printf("[UserService] SetUserSetting: isNotifyPurchaseAlert=%v", req.IsNotifyPurchaseAlert)
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
user, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
user.Setting.IsNotifyPurchaseAlert = req.IsNotifyPurchaseAlert
|
||||
})
|
||||
return &pb.SetUserSettingResponse{
|
||||
DiffUserData: userdata.BuildDiffFromTables(userdata.ProjectTables(user, []string{"IUserSetting"})),
|
||||
}, nil
|
||||
return &pb.SetUserSettingResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetAndroidArgs(ctx context.Context, req *pb.GetAndroidArgsRequest) (*pb.GetAndroidArgsResponse, error) {
|
||||
return &pb.GetAndroidArgsResponse{Nonce: "Mama", ApiKey: "1234567890", DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetAndroidArgsResponse{Nonce: "Mama", ApiKey: "1234567890"}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetBackupToken(ctx context.Context, req *pb.GetBackupTokenRequest) (*pb.GetBackupTokenResponse, error) {
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
user, err := s.users.LoadUser(userId)
|
||||
if err != nil {
|
||||
return &pb.GetBackupTokenResponse{BackupToken: "mock-backup-token", DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetBackupTokenResponse{BackupToken: "mock-backup-token"}, nil
|
||||
}
|
||||
return &pb.GetBackupTokenResponse{BackupToken: user.BackupToken, DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetBackupTokenResponse{BackupToken: user.BackupToken}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) CheckTransferSetting(ctx context.Context, _ *emptypb.Empty) (*pb.CheckTransferSettingResponse, error) {
|
||||
return &pb.CheckTransferSettingResponse{DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.CheckTransferSettingResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) GetUserGamePlayNote(ctx context.Context, req *pb.GetUserGamePlayNoteRequest) (*pb.GetUserGamePlayNoteResponse, error) {
|
||||
return &pb.GetUserGamePlayNoteResponse{DiffUserData: userdata.EmptyDiff()}, nil
|
||||
return &pb.GetUserGamePlayNoteResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) resolveAuthToken(token string) (facebookId int64, err error) {
|
||||
if s.authURL == "" {
|
||||
return 0, status.Error(codes.FailedPrecondition, "auth server not configured (--auth-url)")
|
||||
}
|
||||
|
||||
resp, err := http.Get(s.authURL + "/me?access_token=" + token)
|
||||
if err != nil {
|
||||
return 0, status.Errorf(codes.Internal, "auth server unreachable: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return 0, status.Error(codes.Unauthenticated, "invalid or expired token")
|
||||
}
|
||||
|
||||
var body struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return 0, status.Errorf(codes.Internal, "decode auth response: %v", err)
|
||||
}
|
||||
if body.ID == "" {
|
||||
return 0, status.Error(codes.Unauthenticated, "auth server returned empty id")
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(body.ID, 10, 64)
|
||||
if err != nil {
|
||||
return 0, status.Errorf(codes.Internal, "invalid auth id %q: %v", body.ID, err)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) SetFacebookAccount(ctx context.Context, req *pb.SetFacebookAccountRequest) (*pb.SetFacebookAccountResponse, error) {
|
||||
log.Printf("[UserService] SetFacebookAccount")
|
||||
|
||||
fbId, err := s.resolveAuthToken(req.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
if err := s.users.SetFacebookId(userId, fbId); err != nil {
|
||||
return nil, fmt.Errorf("set facebook id: %w", err)
|
||||
}
|
||||
log.Printf("[UserService] linked facebook_id=%d to user_id=%d", fbId, userId)
|
||||
return &pb.SetFacebookAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) UnsetFacebookAccount(ctx context.Context, _ *emptypb.Empty) (*pb.UnsetFacebookAccountResponse, error) {
|
||||
log.Printf("[UserService] UnsetFacebookAccount")
|
||||
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
if err := s.users.ClearFacebookId(userId); err != nil {
|
||||
return nil, fmt.Errorf("clear facebook id: %w", err)
|
||||
}
|
||||
log.Printf("[UserService] unlinked facebook from user_id=%d", userId)
|
||||
return &pb.UnsetFacebookAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *UserServiceServer) TransferUserByFacebook(ctx context.Context, req *pb.TransferUserByFacebookRequest) (*pb.TransferUserByFacebookResponse, error) {
|
||||
log.Printf("[UserService] TransferUserByFacebook: uuid=%s", req.Uuid)
|
||||
|
||||
fbId, err := s.resolveAuthToken(req.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userId, err := s.users.GetUserByFacebookId(fbId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.NotFound, "no account linked to this login")
|
||||
}
|
||||
|
||||
if err := s.users.UpdateUUID(userId, req.Uuid); err != nil {
|
||||
return nil, fmt.Errorf("update uuid: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[UserService] transferred facebook_id=%d -> user_id=%d with new uuid=%s", fbId, userId, req.Uuid)
|
||||
|
||||
return &pb.TransferUserByFacebookResponse{
|
||||
UserId: userId,
|
||||
Signature: fmt.Sprintf("fb_transfer_%d_%d", userId, gametime.Now().Unix()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -11,35 +11,8 @@ import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
var weaponDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
var limitBreakDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponSkill",
|
||||
"IUserWeaponAbility",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
"IUserWeaponNote",
|
||||
}
|
||||
|
||||
var weaponAwakenDiffTables = []string{
|
||||
"IUserWeapon",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserMaterial",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type WeaponServiceServer struct {
|
||||
pb.UnimplementedWeaponServiceServer
|
||||
users store.UserRepository
|
||||
@@ -55,10 +28,10 @@ func NewWeaponServiceServer(users store.UserRepository, sessions store.SessionRe
|
||||
func (s *WeaponServiceServer) Protect(ctx context.Context, req *pb.ProtectRequest) (*pb.ProtectResponse, error) {
|
||||
log.Printf("[WeaponService] Protect: uuids=%v", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, uuid := range req.UserWeaponUuid {
|
||||
weapon, ok := user.Weapons[uuid]
|
||||
if !ok {
|
||||
@@ -71,17 +44,16 @@ func (s *WeaponServiceServer) Protect(ctx context.Context, req *pb.ProtectReques
|
||||
}
|
||||
})
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserWeapon"}))
|
||||
return &pb.ProtectResponse{DiffUserData: diff}, nil
|
||||
return &pb.ProtectResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) Unprotect(ctx context.Context, req *pb.UnprotectRequest) (*pb.UnprotectResponse, error) {
|
||||
log.Printf("[WeaponService] Unprotect: uuids=%v", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, _ := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
for _, uuid := range req.UserWeaponUuid {
|
||||
weapon, ok := user.Weapons[uuid]
|
||||
if !ok {
|
||||
@@ -94,18 +66,16 @@ func (s *WeaponServiceServer) Unprotect(ctx context.Context, req *pb.UnprotectRe
|
||||
}
|
||||
})
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, []string{"IUserWeapon"}))
|
||||
return &pb.UnprotectResponse{DiffUserData: diff}, nil
|
||||
return &pb.UnprotectResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) EnhanceByMaterial(ctx context.Context, req *pb.EnhanceByMaterialRequest) (*pb.EnhanceByMaterialResponse, error) {
|
||||
log.Printf("[WeaponService] EnhanceByMaterial: uuid=%s materials=%v", req.UserWeaponUuid, req.Materials)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
var changedStoryIds []int32
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] EnhanceByMaterial: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -157,35 +127,24 @@ func (s *WeaponServiceServer) EnhanceByMaterial(ctx context.Context, req *pb.Enh
|
||||
user.Weapons[req.UserWeaponUuid] = weapon
|
||||
log.Printf("[WeaponService] EnhanceByMaterial: weaponId=%d +%d exp -> total=%d level=%d", weapon.WeaponId, totalExp, weapon.Exp, weapon.Level)
|
||||
|
||||
changedStoryIds = s.checkWeaponStoryUnlocks(user, weapon.WeaponId, weapon.Level, nowMillis)
|
||||
s.checkWeaponStoryUnlocks(user, weapon.WeaponId, weapon.Level, nowMillis)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("weapon enhance by material: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, weaponDiffTables))
|
||||
userdata.AddWeaponStoryDiff(diff, snapshot, changedStoryIds)
|
||||
|
||||
return &pb.EnhanceByMaterialResponse{
|
||||
IsGreatSuccess: false,
|
||||
SurplusEnhanceMaterial: map[int32]int32{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) Sell(ctx context.Context, req *pb.SellRequest) (*pb.SellResponse, error) {
|
||||
log.Printf("[WeaponService] Sell: uuids=%v", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
for _, uuid := range req.UserWeaponUuid {
|
||||
weapon, ok := user.Weapons[uuid]
|
||||
@@ -225,21 +184,16 @@ func (s *WeaponServiceServer) Sell(ctx context.Context, req *pb.SellRequest) (*p
|
||||
return nil, fmt.Errorf("weapon sell: %w", err)
|
||||
}
|
||||
|
||||
sellDiffTables := []string{"IUserWeapon", "IUserWeaponSkill", "IUserWeaponAbility", "IUserWeaponAwaken", "IUserConsumableItem"}
|
||||
tables := userdata.ProjectTables(snapshot, sellDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.SellResponse{DiffUserData: diff}, nil
|
||||
return &pb.SellResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) Evolve(ctx context.Context, req *pb.EvolveRequest) (*pb.EvolveResponse, error) {
|
||||
log.Printf("[WeaponService] Evolve: uuid=%s", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
var changedStoryIds []int32
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] Evolve: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -298,25 +252,22 @@ func (s *WeaponServiceServer) Evolve(ctx context.Context, req *pb.EvolveRequest)
|
||||
|
||||
log.Printf("[WeaponService] Evolve: weaponId %d -> %d", wm.WeaponId, evolvedId)
|
||||
|
||||
changedStoryIds = s.checkWeaponStoryUnlocks(user, evolvedId, weapon.Level, nowMillis)
|
||||
s.checkWeaponStoryUnlocks(user, evolvedId, weapon.Level, nowMillis)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("weapon evolve: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, weaponDiffTables))
|
||||
userdata.AddWeaponStoryDiff(diff, snapshot, changedStoryIds)
|
||||
|
||||
return &pb.EvolveResponse{DiffUserData: diff}, nil
|
||||
return &pb.EvolveResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) EnhanceSkill(ctx context.Context, req *pb.EnhanceSkillRequest) (*pb.EnhanceSkillResponse, error) {
|
||||
log.Printf("[WeaponService] EnhanceSkill: uuid=%s skillId=%d addLevel=%d", req.UserWeaponUuid, req.SkillId, req.AddLevelCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] EnhanceSkill: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -330,7 +281,7 @@ func (s *WeaponServiceServer) EnhanceSkill(ctx context.Context, req *pb.EnhanceS
|
||||
}
|
||||
|
||||
groupRows := s.catalog.SkillGroupsByGroupId[wm.WeaponSkillGroupId]
|
||||
var skillGroup *masterdata.WeaponSkillGroupRow
|
||||
var skillGroup *masterdata.EntityMWeaponSkillGroup
|
||||
for i := range groupRows {
|
||||
if groupRows[i].SkillId == req.SkillId {
|
||||
skillGroup = &groupRows[i]
|
||||
@@ -403,18 +354,16 @@ func (s *WeaponServiceServer) EnhanceSkill(ctx context.Context, req *pb.EnhanceS
|
||||
return nil, fmt.Errorf("weapon enhance skill: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, weaponDiffTables))
|
||||
|
||||
return &pb.EnhanceSkillResponse{DiffUserData: diff}, nil
|
||||
return &pb.EnhanceSkillResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) EnhanceAbility(ctx context.Context, req *pb.EnhanceAbilityRequest) (*pb.EnhanceAbilityResponse, error) {
|
||||
log.Printf("[WeaponService] EnhanceAbility: uuid=%s abilityId=%d addLevel=%d", req.UserWeaponUuid, req.AbilityId, req.AddLevelCount)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] EnhanceAbility: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -428,7 +377,7 @@ func (s *WeaponServiceServer) EnhanceAbility(ctx context.Context, req *pb.Enhanc
|
||||
}
|
||||
|
||||
groupRows := s.catalog.AbilityGroupsByGroupId[wm.WeaponAbilityGroupId]
|
||||
var abilityGroup *masterdata.WeaponAbilityGroupRow
|
||||
var abilityGroup *masterdata.EntityMWeaponAbilityGroup
|
||||
for i := range groupRows {
|
||||
if groupRows[i].AbilityId == req.AbilityId {
|
||||
abilityGroup = &groupRows[i]
|
||||
@@ -501,18 +450,16 @@ func (s *WeaponServiceServer) EnhanceAbility(ctx context.Context, req *pb.Enhanc
|
||||
return nil, fmt.Errorf("weapon enhance ability: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, weaponDiffTables))
|
||||
|
||||
return &pb.EnhanceAbilityResponse{DiffUserData: diff}, nil
|
||||
return &pb.EnhanceAbilityResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) LimitBreakByMaterial(ctx context.Context, req *pb.LimitBreakByMaterialRequest) (*pb.LimitBreakByMaterialResponse, error) {
|
||||
log.Printf("[WeaponService] LimitBreakByMaterial: uuid=%s materials=%v", req.UserWeaponUuid, req.Materials)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] LimitBreakByMaterial: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -572,25 +519,16 @@ func (s *WeaponServiceServer) LimitBreakByMaterial(ctx context.Context, req *pb.
|
||||
return nil, fmt.Errorf("weapon limit break by material: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, limitBreakDiffTables))
|
||||
|
||||
return &pb.LimitBreakByMaterialResponse{DiffUserData: diff}, nil
|
||||
return &pb.LimitBreakByMaterialResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) LimitBreakByWeapon(ctx context.Context, req *pb.LimitBreakByWeaponRequest) (*pb.LimitBreakByWeaponResponse, error) {
|
||||
log.Printf("[WeaponService] LimitBreakByWeapon: uuid=%s materialUuids=%v", req.UserWeaponUuid, req.MaterialUserWeaponUuids)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] LimitBreakByWeapon: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -658,27 +596,16 @@ func (s *WeaponServiceServer) LimitBreakByWeapon(ctx context.Context, req *pb.Li
|
||||
return nil, fmt.Errorf("weapon limit break by weapon: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(snapshot, limitBreakDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.LimitBreakByWeaponResponse{DiffUserData: diff}, nil
|
||||
return &pb.LimitBreakByWeaponResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.EnhanceByWeaponRequest) (*pb.EnhanceByWeaponResponse, error) {
|
||||
log.Printf("[WeaponService] EnhanceByWeapon: uuid=%s materialUuids=%v", req.UserWeaponUuid, req.MaterialUserWeaponUuids)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
oldUser, _ := s.users.LoadUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserWeapon", oldUser, userdata.SortedWeaponRecords, []string{"userId", "userWeaponUuid"}).
|
||||
Track("IUserWeaponSkill", oldUser, userdata.SortedWeaponSkillRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAbility", oldUser, userdata.SortedWeaponAbilityRecords, []string{"userId", "userWeaponUuid", "slotNumber"}).
|
||||
Track("IUserWeaponAwaken", oldUser, userdata.SortedWeaponAwakenRecords, []string{"userId", "userWeaponUuid"})
|
||||
|
||||
var changedStoryIds []int32
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] EnhanceByWeapon: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -740,77 +667,63 @@ func (s *WeaponServiceServer) EnhanceByWeapon(ctx context.Context, req *pb.Enhan
|
||||
user.Weapons[req.UserWeaponUuid] = weapon
|
||||
log.Printf("[WeaponService] EnhanceByWeapon: weaponId=%d +%d exp -> total=%d level=%d", weapon.WeaponId, totalExp, weapon.Exp, weapon.Level)
|
||||
|
||||
changedStoryIds = s.checkWeaponStoryUnlocks(user, weapon.WeaponId, weapon.Level, nowMillis)
|
||||
s.checkWeaponStoryUnlocks(user, weapon.WeaponId, weapon.Level, nowMillis)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("weapon enhance by weapon: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.ProjectTables(snapshot, weaponDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
userdata.AddWeaponStoryDiff(diff, snapshot, changedStoryIds)
|
||||
|
||||
return &pb.EnhanceByWeaponResponse{
|
||||
IsGreatSuccess: false,
|
||||
SurplusEnhanceWeapon: []string{},
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) checkWeaponStoryUnlocks(user *store.UserState, weaponId, level int32, nowMillis int64) []int32 {
|
||||
func (s *WeaponServiceServer) checkWeaponStoryUnlocks(user *store.UserState, weaponId, level int32, nowMillis int64) {
|
||||
wm, ok := s.catalog.Weapons[weaponId]
|
||||
if !ok || wm.WeaponStoryReleaseConditionGroupId == 0 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
evoOrder, hasEvo := s.catalog.EvolutionOrder[weaponId]
|
||||
conditions := s.catalog.ReleaseConditionsByGroupId[wm.WeaponStoryReleaseConditionGroupId]
|
||||
|
||||
changed := false
|
||||
for _, cond := range conditions {
|
||||
granted := false
|
||||
switch cond.WeaponStoryReleaseConditionType {
|
||||
switch model.WeaponStoryReleaseConditionType(cond.WeaponStoryReleaseConditionType) {
|
||||
case model.WeaponStoryReleaseConditionTypeAcquisition:
|
||||
granted = store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
case model.WeaponStoryReleaseConditionTypeReachSpecifiedLevel:
|
||||
if level >= cond.ConditionValue {
|
||||
granted = store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
case model.WeaponStoryReleaseConditionTypeReachInitialMaxLevel:
|
||||
if maxFunc, ok := s.catalog.MaxLevelByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
||||
if level >= maxFunc.Evaluate(0) {
|
||||
granted = store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
}
|
||||
case model.WeaponStoryReleaseConditionTypeReachOnceEvolvedMaxLevel:
|
||||
if hasEvo && evoOrder >= 1 {
|
||||
if maxFunc, ok := s.catalog.MaxLevelByEnhanceId[wm.WeaponSpecificEnhanceId]; ok {
|
||||
if level >= maxFunc.Evaluate(0) {
|
||||
granted = store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
}
|
||||
}
|
||||
case model.WeaponStoryReleaseConditionTypeReachSpecifiedEvolutionCount:
|
||||
if hasEvo && evoOrder >= cond.ConditionValue {
|
||||
granted = store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
}
|
||||
if granted {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
return []int32{weaponId}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WeaponServiceServer) Awaken(ctx context.Context, req *pb.WeaponAwakenRequest) (*pb.WeaponAwakenResponse, error) {
|
||||
log.Printf("[WeaponService] Awaken: uuid=%s", req.UserWeaponUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
_, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
weapon, ok := user.Weapons[req.UserWeaponUuid]
|
||||
if !ok {
|
||||
log.Printf("[WeaponService] Awaken: weapon uuid=%s not found", req.UserWeaponUuid)
|
||||
@@ -857,7 +770,5 @@ func (s *WeaponServiceServer) Awaken(ctx context.Context, req *pb.WeaponAwakenRe
|
||||
return nil, fmt.Errorf("weapon awaken: %w", err)
|
||||
}
|
||||
|
||||
diff := userdata.BuildDiffFromTables(userdata.ProjectTables(snapshot, weaponAwakenDiffTables))
|
||||
|
||||
return &pb.WeaponAwakenResponse{DiffUserData: diff}, nil
|
||||
return &pb.WeaponAwakenResponse{}, nil
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ const (
|
||||
defaultChargeMoneyThisMonth = int64(0)
|
||||
)
|
||||
|
||||
func SeedUserState(userId int64, uuid string, nowMillis int64) *UserState {
|
||||
func SeedUserState(userId int64, uuid string, nowMillis int64, platform model.ClientPlatform) *UserState {
|
||||
user := &UserState{
|
||||
UserId: userId,
|
||||
Uuid: uuid,
|
||||
PlayerId: userId,
|
||||
OsType: 2,
|
||||
PlatformType: 2,
|
||||
OsType: platform.OsType,
|
||||
PlatformType: platform.PlatformType,
|
||||
UserRestrictionType: 0,
|
||||
RegisterDatetime: nowMillis,
|
||||
GameStartDatetime: nowMillis,
|
||||
|
||||
@@ -10,19 +10,21 @@ import (
|
||||
|
||||
func (s *SQLiteStore) LoadUser(userId int64) (store.UserState, error) {
|
||||
var u store.UserState
|
||||
var fbId sql.NullInt64
|
||||
|
||||
err := s.db.QueryRow(`SELECT user_id, uuid, player_id, os_type, platform_type, user_restriction_type,
|
||||
register_datetime, game_start_datetime, latest_version, birth_year, birth_month,
|
||||
backup_token, charge_money_this_month FROM users WHERE user_id = ?`, userId).Scan(
|
||||
backup_token, charge_money_this_month, facebook_id FROM users WHERE user_id = ?`, userId).Scan(
|
||||
&u.UserId, &u.Uuid, &u.PlayerId, &u.OsType, &u.PlatformType, &u.UserRestrictionType,
|
||||
&u.RegisterDatetime, &u.GameStartDatetime, &u.LatestVersion, &u.BirthYear, &u.BirthMonth,
|
||||
&u.BackupToken, &u.ChargeMoneyThisMonth)
|
||||
&u.BackupToken, &u.ChargeMoneyThisMonth, &fbId)
|
||||
if err == sql.ErrNoRows {
|
||||
return u, store.ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return u, fmt.Errorf("load users: %w", err)
|
||||
}
|
||||
u.FacebookId = fbId.Int64
|
||||
|
||||
initMaps(&u)
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (s *SQLiteStore) CreateUser(uuid string) (int64, error) {
|
||||
func (s *SQLiteStore) CreateUser(uuid string, platform model.ClientPlatform) (int64, error) {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("begin tx: %w", err)
|
||||
@@ -24,8 +25,8 @@ func (s *SQLiteStore) CreateUser(uuid string) (int64, error) {
|
||||
|
||||
res, err := tx.Exec(`INSERT INTO users (uuid, player_id, os_type, platform_type, user_restriction_type,
|
||||
register_datetime, game_start_datetime, latest_version, birth_year, birth_month,
|
||||
backup_token, charge_money_this_month) VALUES (?, 0, 2, 2, 0, ?, ?, 0, 2000, 1, 'mock-backup-token', 0)`,
|
||||
uuid, nowMillis, nowMillis)
|
||||
backup_token, charge_money_this_month) VALUES (?, 0, ?, ?, 0, ?, ?, 0, 2000, 1, 'mock-backup-token', 0)`,
|
||||
uuid, platform.OsType, platform.PlatformType, nowMillis, nowMillis)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert user: %w", err)
|
||||
}
|
||||
@@ -39,7 +40,7 @@ func (s *SQLiteStore) CreateUser(uuid string) (int64, error) {
|
||||
return 0, fmt.Errorf("update player_id: %w", err)
|
||||
}
|
||||
|
||||
user := store.SeedUserState(userId, uuid, nowMillis)
|
||||
user := store.SeedUserState(userId, uuid, nowMillis, platform)
|
||||
if err := writeUserState(tx, userId, user); err != nil {
|
||||
return 0, fmt.Errorf("write seed state: %w", err)
|
||||
}
|
||||
@@ -188,6 +189,52 @@ func (s *SQLiteStore) ImportUser(u *store.UserState) error {
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("commit: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStore) SetFacebookId(userId int64, facebookId int64) error {
|
||||
_, err := s.db.Exec(`UPDATE users SET facebook_id = ? WHERE user_id = ?`, facebookId, userId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("set facebook_id: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStore) GetUserByFacebookId(facebookId int64) (int64, error) {
|
||||
var userId int64
|
||||
err := s.db.QueryRow(`SELECT user_id FROM users WHERE facebook_id = ?`, facebookId).Scan(&userId)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, store.ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("query user by facebook_id: %w", err)
|
||||
}
|
||||
return userId, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStore) GetFacebookId(userId int64) (int64, error) {
|
||||
var fbId sql.NullInt64
|
||||
err := s.db.QueryRow(`SELECT facebook_id FROM users WHERE user_id = ?`, userId).Scan(&fbId)
|
||||
if err != nil {
|
||||
return 0, store.ErrNotFound
|
||||
}
|
||||
return fbId.Int64, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStore) ClearFacebookId(userId int64) error {
|
||||
_, err := s.db.Exec(`UPDATE users SET facebook_id = NULL WHERE user_id = ?`, userId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("clear facebook_id: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStore) UpdateUUID(userId int64, newUuid string) error {
|
||||
_, err := s.db.Exec(`UPDATE users SET uuid = ? WHERE user_id = ?`, newUuid, userId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update uuid: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package store
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("store: not found")
|
||||
@@ -10,11 +12,16 @@ var ErrNotFound = errors.New("store: not found")
|
||||
type Clock func() time.Time
|
||||
|
||||
type UserRepository interface {
|
||||
CreateUser(uuid string) (int64, error)
|
||||
CreateUser(uuid string, platform model.ClientPlatform) (int64, error)
|
||||
GetUserByUUID(uuid string) (int64, error)
|
||||
LoadUser(userId int64) (UserState, error)
|
||||
UpdateUser(userId int64, mutate func(*UserState)) (UserState, error)
|
||||
DefaultUserId() (int64, error)
|
||||
SetFacebookId(userId int64, facebookId int64) error
|
||||
GetUserByFacebookId(facebookId int64) (int64, error)
|
||||
GetFacebookId(userId int64) (int64, error)
|
||||
ClearFacebookId(userId int64) error
|
||||
UpdateUUID(userId int64, newUuid string) error
|
||||
}
|
||||
|
||||
type SessionRepository interface {
|
||||
|
||||
@@ -31,6 +31,7 @@ type UserState struct {
|
||||
BirthMonth int32
|
||||
BackupToken string
|
||||
ChargeMoneyThisMonth int64
|
||||
FacebookId int64
|
||||
|
||||
Setting UserSettingState
|
||||
Status UserStatusState
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func mapsEqualSimple[K comparable, V comparable](a, b map[K]V) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for k, va := range a {
|
||||
if vb, ok := b[k]; !ok || va != vb {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func mapsEqualStruct[K comparable, V comparable](a, b map[K]V) bool {
|
||||
return mapsEqualSimple(a, b)
|
||||
}
|
||||
|
||||
func mapsEqualSliceValues[K comparable, V comparable](a, b map[K][]V) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for k, va := range a {
|
||||
vb, ok := b[k]
|
||||
if !ok || !slices.Equal(va, vb) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func gimmickStateEqual(a, b store.GimmickState) bool {
|
||||
return mapsEqualStruct(a.Progress, b.Progress) &&
|
||||
mapsEqualStruct(a.OrnamentProgress, b.OrnamentProgress) &&
|
||||
mapsEqualStruct(a.Sequences, b.Sequences) &&
|
||||
mapsEqualStruct(a.Unlocks, b.Unlocks)
|
||||
}
|
||||
|
||||
func ChangedTables(before, after *store.UserState) []string {
|
||||
var changed []string
|
||||
add := func(name string) { changed = append(changed, name) }
|
||||
|
||||
if before.UserId != after.UserId || before.PlayerId != after.PlayerId ||
|
||||
before.OsType != after.OsType || before.PlatformType != after.PlatformType ||
|
||||
before.UserRestrictionType != after.UserRestrictionType ||
|
||||
before.RegisterDatetime != after.RegisterDatetime ||
|
||||
before.GameStartDatetime != after.GameStartDatetime ||
|
||||
before.LatestVersion != after.LatestVersion {
|
||||
add("IUser")
|
||||
}
|
||||
if before.Setting != after.Setting {
|
||||
add("IUserSetting")
|
||||
}
|
||||
if before.Status != after.Status {
|
||||
add("IUserStatus")
|
||||
}
|
||||
if before.Gem != after.Gem {
|
||||
add("IUserGem")
|
||||
}
|
||||
if before.Profile != after.Profile {
|
||||
add("IUserProfile")
|
||||
}
|
||||
if before.Login != after.Login {
|
||||
add("IUserLogin")
|
||||
}
|
||||
if before.LoginBonus != after.LoginBonus {
|
||||
add("IUserLoginBonus")
|
||||
}
|
||||
if before.PortalCageStatus != after.PortalCageStatus {
|
||||
add("IUserPortalCageStatus")
|
||||
}
|
||||
if before.GuerrillaFreeOpen != after.GuerrillaFreeOpen {
|
||||
add("IUserEventQuestGuerrillaFreeOpen")
|
||||
}
|
||||
if before.ShopReplaceable != after.ShopReplaceable {
|
||||
add("IUserShopReplaceable")
|
||||
}
|
||||
if before.Explore != after.Explore {
|
||||
add("IUserExplore")
|
||||
}
|
||||
if before.BigHuntProgress != after.BigHuntProgress {
|
||||
add("IUserBigHuntProgressStatus")
|
||||
}
|
||||
if before.FacebookId != after.FacebookId {
|
||||
add("IUserFacebook")
|
||||
}
|
||||
|
||||
if before.MainQuest != after.MainQuest {
|
||||
add("IUserMainQuestFlowStatus")
|
||||
add("IUserMainQuestMainFlowStatus")
|
||||
add("IUserMainQuestProgressStatus")
|
||||
add("IUserMainQuestSeasonRoute")
|
||||
add("IUserMainQuestReplayFlowStatus")
|
||||
}
|
||||
if before.EventQuest != after.EventQuest {
|
||||
add("IUserEventQuestProgressStatus")
|
||||
}
|
||||
if before.ExtraQuest != after.ExtraQuest {
|
||||
add("IUserExtraQuestProgressStatus")
|
||||
}
|
||||
if before.SideStoryActiveProgress != after.SideStoryActiveProgress {
|
||||
add("IUserSideStoryQuestSceneProgressStatus")
|
||||
}
|
||||
|
||||
if !mapsEqualStruct(before.Tutorials, after.Tutorials) {
|
||||
add("IUserTutorialProgress")
|
||||
}
|
||||
if !mapsEqualStruct(before.Missions, after.Missions) {
|
||||
add("IUserMission")
|
||||
}
|
||||
if !mapsEqualStruct(before.Characters, after.Characters) {
|
||||
add("IUserCharacter")
|
||||
}
|
||||
if !mapsEqualStruct(before.Costumes, after.Costumes) {
|
||||
add("IUserCostume")
|
||||
}
|
||||
if !mapsEqualStruct(before.Weapons, after.Weapons) {
|
||||
add("IUserWeapon")
|
||||
}
|
||||
if !mapsEqualStruct(before.WeaponStories, after.WeaponStories) {
|
||||
add("IUserWeaponStory")
|
||||
}
|
||||
if !mapsEqualStruct(before.WeaponNotes, after.WeaponNotes) {
|
||||
add("IUserWeaponNote")
|
||||
}
|
||||
if !mapsEqualStruct(before.Companions, after.Companions) {
|
||||
add("IUserCompanion")
|
||||
}
|
||||
if !mapsEqualStruct(before.Thoughts, after.Thoughts) {
|
||||
add("IUserThought")
|
||||
}
|
||||
if !mapsEqualSimple(before.ConsumableItems, after.ConsumableItems) {
|
||||
add("IUserConsumableItem")
|
||||
}
|
||||
if !mapsEqualSimple(before.Materials, after.Materials) {
|
||||
add("IUserMaterial")
|
||||
}
|
||||
if !mapsEqualSimple(before.ImportantItems, after.ImportantItems) {
|
||||
add("IUserImportantItem")
|
||||
}
|
||||
if !mapsEqualSimple(before.PremiumItems, after.PremiumItems) {
|
||||
add("IUserPremiumItem")
|
||||
}
|
||||
if !mapsEqualStruct(before.Parts, after.Parts) {
|
||||
add("IUserParts")
|
||||
}
|
||||
if !mapsEqualStruct(before.PartsGroupNotes, after.PartsGroupNotes) {
|
||||
add("IUserPartsGroupNote")
|
||||
}
|
||||
if !mapsEqualStruct(before.PartsPresets, after.PartsPresets) {
|
||||
add("IUserPartsPreset")
|
||||
}
|
||||
if !mapsEqualStruct(before.CostumeActiveSkills, after.CostumeActiveSkills) {
|
||||
add("IUserCostumeActiveSkill")
|
||||
}
|
||||
if !mapsEqualSliceValues(before.WeaponSkills, after.WeaponSkills) {
|
||||
add("IUserWeaponSkill")
|
||||
}
|
||||
if !mapsEqualSliceValues(before.WeaponAbilities, after.WeaponAbilities) {
|
||||
add("IUserWeaponAbility")
|
||||
}
|
||||
if !mapsEqualStruct(before.WeaponAwakens, after.WeaponAwakens) {
|
||||
add("IUserWeaponAwaken")
|
||||
}
|
||||
if !mapsEqualStruct(before.DeckTypeNotes, after.DeckTypeNotes) {
|
||||
add("IUserDeckTypeNote")
|
||||
}
|
||||
if !mapsEqualStruct(before.DeckCharacters, after.DeckCharacters) {
|
||||
add("IUserDeckCharacter")
|
||||
add("IUserDeckCharacterDressupCostume")
|
||||
}
|
||||
if !mapsEqualStruct(before.Decks, after.Decks) {
|
||||
add("IUserDeck")
|
||||
}
|
||||
if !mapsEqualSliceValues(before.DeckSubWeapons, after.DeckSubWeapons) {
|
||||
add("IUserDeckSubWeaponGroup")
|
||||
}
|
||||
if !mapsEqualSliceValues(before.DeckParts, after.DeckParts) {
|
||||
add("IUserDeckPartsGroup")
|
||||
}
|
||||
if !mapsEqualStruct(before.Quests, after.Quests) {
|
||||
add("IUserQuest")
|
||||
}
|
||||
if !mapsEqualStruct(before.QuestMissions, after.QuestMissions) {
|
||||
add("IUserQuestMission")
|
||||
}
|
||||
if !mapsEqualStruct(before.SideStoryQuests, after.SideStoryQuests) {
|
||||
add("IUserSideStoryQuest")
|
||||
}
|
||||
if !mapsEqualStruct(before.QuestLimitContentStatus, after.QuestLimitContentStatus) {
|
||||
add("IUserQuestLimitContentStatus")
|
||||
}
|
||||
if !mapsEqualSimple(before.NaviCutInPlayed, after.NaviCutInPlayed) {
|
||||
add("IUserNaviCutIn")
|
||||
}
|
||||
if !mapsEqualSimple(before.ViewedMovies, after.ViewedMovies) {
|
||||
add("IUserMovie")
|
||||
}
|
||||
if !mapsEqualSimple(before.ContentsStories, after.ContentsStories) {
|
||||
add("IUserContentsStory")
|
||||
}
|
||||
if !mapsEqualSimple(before.DrawnOmikuji, after.DrawnOmikuji) {
|
||||
add("IUserOmikuji")
|
||||
}
|
||||
if !mapsEqualSimple(before.DokanConfirmed, after.DokanConfirmed) {
|
||||
add("IUserDokan")
|
||||
}
|
||||
if !mapsEqualStruct(before.ShopItems, after.ShopItems) {
|
||||
add("IUserShopItem")
|
||||
}
|
||||
if !mapsEqualStruct(before.ShopReplaceableLineup, after.ShopReplaceableLineup) {
|
||||
add("IUserShopReplaceableLineup")
|
||||
}
|
||||
if !mapsEqualStruct(before.ExploreScores, after.ExploreScores) {
|
||||
add("IUserExploreScore")
|
||||
}
|
||||
if !mapsEqualStruct(before.CharacterBoards, after.CharacterBoards) {
|
||||
add("IUserCharacterBoard")
|
||||
}
|
||||
if !mapsEqualStruct(before.CharacterBoardAbilities, after.CharacterBoardAbilities) {
|
||||
add("IUserCharacterBoardAbility")
|
||||
}
|
||||
if !mapsEqualStruct(before.CharacterBoardStatusUps, after.CharacterBoardStatusUps) {
|
||||
add("IUserCharacterBoardStatusUp")
|
||||
}
|
||||
if !mapsEqualStruct(before.CostumeAwakenStatusUps, after.CostumeAwakenStatusUps) {
|
||||
add("IUserCostumeAwakenStatusUp")
|
||||
}
|
||||
if !mapsEqualStruct(before.CostumeLotteryEffects, after.CostumeLotteryEffects) {
|
||||
add("IUserCostumeLotteryEffect")
|
||||
}
|
||||
if !mapsEqualStruct(before.CostumeLotteryEffectPending, after.CostumeLotteryEffectPending) {
|
||||
add("IUserCostumeLotteryEffectPending")
|
||||
}
|
||||
if !mapsEqualStruct(before.AutoSaleSettings, after.AutoSaleSettings) {
|
||||
add("IUserAutoSaleSettingDetail")
|
||||
}
|
||||
if !mapsEqualStruct(before.CharacterRebirths, after.CharacterRebirths) {
|
||||
add("IUserCharacterRebirth")
|
||||
}
|
||||
if !mapsEqualStruct(before.CageOrnamentRewards, after.CageOrnamentRewards) {
|
||||
add("IUserCageOrnamentReward")
|
||||
}
|
||||
|
||||
if !mapsEqualStruct(before.BigHuntMaxScores, after.BigHuntMaxScores) {
|
||||
add("IUserBigHuntMaxScore")
|
||||
}
|
||||
if !mapsEqualStruct(before.BigHuntStatuses, after.BigHuntStatuses) {
|
||||
add("IUserBigHuntStatus")
|
||||
}
|
||||
if !mapsEqualStruct(before.BigHuntScheduleMaxScores, after.BigHuntScheduleMaxScores) {
|
||||
add("IUserBigHuntScheduleMaxScore")
|
||||
}
|
||||
if !mapsEqualStruct(before.BigHuntWeeklyMaxScores, after.BigHuntWeeklyMaxScores) {
|
||||
add("IUserBigHuntWeeklyMaxScore")
|
||||
}
|
||||
if !mapsEqualStruct(before.BigHuntWeeklyStatuses, after.BigHuntWeeklyStatuses) {
|
||||
add("IUserBigHuntWeeklyStatus")
|
||||
}
|
||||
|
||||
if !gimmickStateEqual(before.Gimmick, after.Gimmick) {
|
||||
if !mapsEqualStruct(before.Gimmick.Progress, after.Gimmick.Progress) {
|
||||
add("IUserGimmick")
|
||||
}
|
||||
if !mapsEqualStruct(before.Gimmick.OrnamentProgress, after.Gimmick.OrnamentProgress) {
|
||||
add("IUserGimmickOrnamentProgress")
|
||||
}
|
||||
if !mapsEqualStruct(before.Gimmick.Sequences, after.Gimmick.Sequences) {
|
||||
add("IUserGimmickSequence")
|
||||
}
|
||||
if !mapsEqualStruct(before.Gimmick.Unlocks, after.Gimmick.Unlocks) {
|
||||
add("IUserGimmickUnlock")
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
func ComputeDelta(before, after *store.UserState, changedTables []string) map[string]*pb.DiffData {
|
||||
diff := make(map[string]*pb.DiffData, len(changedTables))
|
||||
for _, table := range changedTables {
|
||||
afterJSON := projectTable(table, *after)
|
||||
deleteKeys := "[]"
|
||||
if kf := keyFieldsForTable(table); len(kf) > 0 {
|
||||
beforeJSON := projectTable(table, *before)
|
||||
deleteKeys = ComputeDeleteKeys(
|
||||
parseJSONRecords(beforeJSON),
|
||||
parseJSONRecords(afterJSON),
|
||||
kf,
|
||||
)
|
||||
}
|
||||
diff[table] = &pb.DiffData{
|
||||
UpdateRecordsJson: afterJSON,
|
||||
DeleteKeysJson: deleteKeys,
|
||||
}
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func AllTableNames() []string {
|
||||
return slices.Sorted(maps.Keys(projectors))
|
||||
}
|
||||
|
||||
func SortedChangedNames(tables []string) string {
|
||||
sorted := make([]string, len(tables))
|
||||
copy(sorted, tables)
|
||||
sort.Strings(sorted)
|
||||
return strings.Join(sorted, ",")
|
||||
}
|
||||
|
||||
func parseJSONRecords(jsonStr string) []map[string]any {
|
||||
if jsonStr == "" || jsonStr == "[]" {
|
||||
return nil
|
||||
}
|
||||
var records []map[string]any
|
||||
if err := json.Unmarshal([]byte(jsonStr), &records); err != nil {
|
||||
return nil
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func keyFieldsForTable(table string) []string {
|
||||
switch table {
|
||||
case "IUserWeapon":
|
||||
return []string{"userId", "userWeaponUuid"}
|
||||
case "IUserWeaponSkill":
|
||||
return []string{"userId", "userWeaponUuid", "slotNumber"}
|
||||
case "IUserWeaponAbility":
|
||||
return []string{"userId", "userWeaponUuid", "slotNumber"}
|
||||
case "IUserWeaponAwaken":
|
||||
return []string{"userId", "userWeaponUuid"}
|
||||
case "IUserCostume":
|
||||
return []string{"userId", "userCostumeUuid"}
|
||||
case "IUserCompanion":
|
||||
return []string{"userId", "userCompanionUuid"}
|
||||
case "IUserThought":
|
||||
return []string{"userId", "userThoughtUuid"}
|
||||
case "IUserParts":
|
||||
return []string{"userId", "userPartsUuid"}
|
||||
case "IUserDeckCharacter":
|
||||
return []string{"userId", "userDeckCharacterUuid"}
|
||||
case "IUserDeck":
|
||||
return []string{"userId", "deckType", "userDeckNumber"}
|
||||
case "IUserDeckSubWeaponGroup":
|
||||
return []string{"userId", "userDeckCharacterUuid", "sortOrder"}
|
||||
case "IUserDeckPartsGroup":
|
||||
return []string{"userId", "userDeckCharacterUuid", "sortOrder"}
|
||||
case "IUserDeckCharacterDressupCostume":
|
||||
return []string{"userId", "userDeckCharacterUuid"}
|
||||
case "IUserCharacter":
|
||||
return []string{"userId", "characterId"}
|
||||
case "IUserConsumableItem":
|
||||
return []string{"userId", "consumableItemId"}
|
||||
case "IUserMaterial":
|
||||
return []string{"userId", "materialId"}
|
||||
case "IUserImportantItem":
|
||||
return []string{"userId", "importantItemId"}
|
||||
case "IUserPremiumItem":
|
||||
return []string{"userId", "premiumItemId"}
|
||||
case "IUserQuest":
|
||||
return []string{"userId", "questId"}
|
||||
case "IUserQuestMission":
|
||||
return []string{"userId", "questId", "questMissionId"}
|
||||
case "IUserMission":
|
||||
return []string{"userId", "missionId"}
|
||||
case "IUserWeaponStory":
|
||||
return []string{"userId", "weaponId"}
|
||||
case "IUserWeaponNote":
|
||||
return []string{"userId", "weaponId"}
|
||||
case "IUserTutorialProgress":
|
||||
return []string{"userId", "tutorialType"}
|
||||
case "IUserGimmick":
|
||||
return []string{"userId", "gimmickSequenceScheduleId", "gimmickSequenceId", "gimmickId"}
|
||||
case "IUserGimmickOrnamentProgress":
|
||||
return []string{"userId", "gimmickSequenceScheduleId", "gimmickSequenceId", "gimmickId", "gimmickOrnamentIndex"}
|
||||
case "IUserGimmickSequence":
|
||||
return []string{"userId", "gimmickSequenceScheduleId", "gimmickSequenceId"}
|
||||
case "IUserGimmickUnlock":
|
||||
return []string{"userId", "gimmickSequenceScheduleId", "gimmickSequenceId", "gimmickId"}
|
||||
case "IUserCostumeActiveSkill":
|
||||
return []string{"userId", "userCostumeUuid"}
|
||||
case "IUserCostumeAwakenStatusUp":
|
||||
return []string{"userId", "userCostumeUuid", "statusCalculationType"}
|
||||
case "IUserCostumeLotteryEffect":
|
||||
return []string{"userId", "userCostumeUuid", "slotNumber"}
|
||||
case "IUserCostumeLotteryEffectPending":
|
||||
return []string{"userId", "userCostumeUuid"}
|
||||
case "IUserCharacterBoard":
|
||||
return []string{"userId", "characterBoardId"}
|
||||
case "IUserCharacterBoardAbility":
|
||||
return []string{"userId", "characterId", "abilityId"}
|
||||
case "IUserCharacterBoardStatusUp":
|
||||
return []string{"userId", "characterId", "statusCalculationType"}
|
||||
case "IUserExploreScore":
|
||||
return []string{"userId", "exploreId"}
|
||||
case "IUserPartsGroupNote":
|
||||
return []string{"userId", "partsGroupId"}
|
||||
case "IUserPartsPreset":
|
||||
return []string{"userId", "userPartsPresetNumber"}
|
||||
case "IUserCageOrnamentReward":
|
||||
return []string{"userId", "cageOrnamentId"}
|
||||
case "IUserAutoSaleSettingDetail":
|
||||
return []string{"userId", "possessionAutoSaleItemType"}
|
||||
case "IUserCharacterRebirth":
|
||||
return []string{"userId", "characterId"}
|
||||
case "IUserShopItem":
|
||||
return []string{"userId", "shopItemId"}
|
||||
case "IUserShopReplaceableLineup":
|
||||
return []string{"userId", "slotNumber"}
|
||||
case "IUserNaviCutIn":
|
||||
return []string{"userId", "naviCutInId"}
|
||||
case "IUserMovie":
|
||||
return []string{"userId", "movieId"}
|
||||
case "IUserContentsStory":
|
||||
return []string{"userId", "contentsStoryId"}
|
||||
case "IUserOmikuji":
|
||||
return []string{"userId", "omikujiId"}
|
||||
case "IUserDokan":
|
||||
return []string{"userId", "dokanId"}
|
||||
case "IUserSideStoryQuest":
|
||||
return []string{"userId", "sideStoryQuestId"}
|
||||
case "IUserQuestLimitContentStatus":
|
||||
return []string{"userId", "questId"}
|
||||
case "IUserBigHuntMaxScore":
|
||||
return []string{"userId", "bigHuntBossId"}
|
||||
case "IUserBigHuntStatus":
|
||||
return []string{"userId", "bigHuntBossQuestId"}
|
||||
case "IUserBigHuntScheduleMaxScore":
|
||||
return []string{"userId", "bigHuntScheduleId", "bigHuntBossId"}
|
||||
case "IUserBigHuntWeeklyMaxScore":
|
||||
return []string{"userId", "bigHuntWeeklyVersion", "attributeType"}
|
||||
case "IUserBigHuntWeeklyStatus":
|
||||
return []string{"userId", "bigHuntWeeklyVersion"}
|
||||
case "IUserDeckTypeNote":
|
||||
return []string{"userId", "deckType"}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserBigHuntProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentBigHuntBossQuestId": user.BigHuntProgress.CurrentBigHuntBossQuestId,
|
||||
"currentBigHuntQuestId": user.BigHuntProgress.CurrentBigHuntQuestId,
|
||||
@@ -39,7 +40,7 @@ func init() {
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
@@ -63,7 +64,7 @@ func init() {
|
||||
"latestVersion": st.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
@@ -97,7 +98,7 @@ func init() {
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
@@ -130,7 +131,7 @@ func init() {
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
@@ -153,7 +154,7 @@ func init() {
|
||||
"latestVersion": ws.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,19 +4,20 @@ import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserCharacterBoard", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCharacterBoardRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterBoardAbility", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardAbilityRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCharacterBoardAbilityRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterBoardStatusUp", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardStatusUpRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCharacterBoardStatusUpRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic("IUserCharacterBoardCompleteReward")
|
||||
|
||||
@@ -5,31 +5,32 @@ import (
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserDeck", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckCharacter", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckCharacterRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckCharacterRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckSubWeaponGroup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckSubWeaponGroupRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckSubWeaponGroupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckTypeNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckTypeNoteRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckTypeNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckPartsGroup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckPartsGroupRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckPartsGroupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckCharacterDressupCostume", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckDressupCostumeRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckDressupCostumeRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
|
||||
@@ -4,23 +4,24 @@ import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserGimmick", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedGimmickRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickOrnamentProgress", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickOrnamentProgressRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedGimmickOrnamentProgressRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickSequence", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickSequenceRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedGimmickSequenceRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickUnlock", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickUnlockRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedGimmickUnlockRecords(user)...)
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,111 +6,112 @@ import (
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserCharacter", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCharacterRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostume", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCostumeRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeapon", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedWeaponRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponStory", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedWeaponStoryRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedWeaponStoryRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedWeaponNoteRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedWeaponNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCompanion", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCompanionRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCompanionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserThought", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedThoughtRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedThoughtRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserConsumableItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedConsumableItemRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedConsumableItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMaterial", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedMaterialRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedMaterialRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserImportantItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedImportantItemRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedImportantItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPremiumItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPremiumItemRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedPremiumItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserParts", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedPartsRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedPartsRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeActiveSkill", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeActiveSkillRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCostumeActiveSkillRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponSkill", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponSkillRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedWeaponSkillRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponAbility", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponAbilityRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedWeaponAbilityRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserExplore", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(exploreRecord(user))
|
||||
s, _ := utils.EncodeJSONMaps(exploreRecord(user))
|
||||
return s
|
||||
})
|
||||
register("IUserExploreScore", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedExploreScoreRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedExploreScoreRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPartsGroupNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPartsGroupNoteRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedPartsGroupNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPartsPreset", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPartsPresetRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedPartsPresetRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeAwakenStatusUp", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeAwakenStatusUpRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCostumeAwakenStatusUpRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserAutoSaleSettingDetail", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedAutoSaleSettingRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedAutoSaleSettingRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterRebirth", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterRebirthRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCharacterRebirthRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCageOrnamentReward", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCageOrnamentRewardRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCageOrnamentRewardRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponAwaken", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponAwakenRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedWeaponAwakenRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeLotteryEffect", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeLotteryEffectRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedCostumeLotteryEffectRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeLotteryEffectPending", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedCostumeLotteryEffectPendingRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(SortedCostumeLotteryEffectPendingRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
@@ -285,7 +286,7 @@ func WeaponStoryRecordsForIds(user store.UserState, weaponIds []int32) string {
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func sortedQuestRecords(user store.UserState) []map[string]any {
|
||||
@@ -60,15 +61,15 @@ func sortedQuestMissionRecords(user store.UserState) []map[string]any {
|
||||
|
||||
func init() {
|
||||
register("IUserQuest", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedQuestRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedQuestRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserQuestMission", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedQuestMissionRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedQuestMissionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestFlowType": user.MainQuest.CurrentQuestFlowType,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
@@ -76,7 +77,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestMainFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentMainQuestRouteId": user.MainQuest.CurrentMainQuestRouteId,
|
||||
"currentQuestSceneId": user.MainQuest.CurrentQuestSceneId,
|
||||
@@ -87,7 +88,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestSceneId": user.MainQuest.ProgressQuestSceneId,
|
||||
"headQuestSceneId": user.MainQuest.ProgressHeadQuestSceneId,
|
||||
@@ -97,7 +98,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestSeasonRoute", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"mainQuestSeasonId": user.MainQuest.MainQuestSeasonId,
|
||||
"mainQuestRouteId": user.MainQuest.CurrentMainQuestRouteId,
|
||||
@@ -106,7 +107,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserEventQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentEventQuestChapterId": user.EventQuest.CurrentEventQuestChapterId,
|
||||
"currentQuestId": user.EventQuest.CurrentQuestId,
|
||||
@@ -117,7 +118,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserExtraQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestId": user.ExtraQuest.CurrentQuestId,
|
||||
"currentQuestSceneId": user.ExtraQuest.CurrentQuestSceneId,
|
||||
@@ -127,7 +128,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestReplayFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentHeadQuestSceneId": user.MainQuest.ReplayFlowHeadQuestSceneId,
|
||||
"currentQuestSceneId": user.MainQuest.ReplayFlowCurrentQuestSceneId,
|
||||
@@ -136,7 +137,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserSideStoryQuestSceneProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentSideStoryQuestId": user.SideStoryActiveProgress.CurrentSideStoryQuestId,
|
||||
"currentSideStoryQuestSceneId": user.SideStoryActiveProgress.CurrentSideStoryQuestSceneId,
|
||||
@@ -164,7 +165,7 @@ func init() {
|
||||
"latestVersion": progress.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
register("IUserQuestLimitContentStatus", func(user store.UserState) string {
|
||||
@@ -187,7 +188,7 @@ func init() {
|
||||
"latestVersion": st.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
s, _ := utils.EncodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/utils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUser", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"playerId": user.PlayerId,
|
||||
"osType": user.OsType,
|
||||
@@ -22,15 +23,15 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserSetting", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserSetting{
|
||||
UserId: user.UserId,
|
||||
IsNotifyPurchaseAlert: user.Setting.IsNotifyPurchaseAlert,
|
||||
LatestVersion: user.Setting.LatestVersion,
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"isNotifyPurchaseAlert": user.Setting.IsNotifyPurchaseAlert,
|
||||
"latestVersion": user.Setting.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"level": user.Status.Level,
|
||||
"exp": user.Status.Exp,
|
||||
@@ -41,16 +42,16 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserGem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserGem{
|
||||
UserId: user.UserId,
|
||||
PaidGem: user.Gem.PaidGem,
|
||||
FreeGem: user.Gem.FreeGem,
|
||||
LatestVersion: gametime.NowMillis(),
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"paidGem": user.Gem.PaidGem,
|
||||
"freeGem": user.Gem.FreeGem,
|
||||
"latestVersion": gametime.NowMillis(),
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserProfile", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"name": user.Profile.Name,
|
||||
"nameUpdateDatetime": user.Profile.NameUpdateDatetime,
|
||||
@@ -63,58 +64,58 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserLogin", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserLogin{
|
||||
UserId: user.UserId,
|
||||
TotalLoginCount: user.Login.TotalLoginCount,
|
||||
ContinualLoginCount: user.Login.ContinualLoginCount,
|
||||
MaxContinualLoginCount: user.Login.MaxContinualLoginCount,
|
||||
LastLoginDatetime: user.Login.LastLoginDatetime,
|
||||
LastComebackLoginDatetime: user.Login.LastComebackLoginDatetime,
|
||||
LatestVersion: user.Login.LatestVersion,
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"totalLoginCount": user.Login.TotalLoginCount,
|
||||
"continualLoginCount": user.Login.ContinualLoginCount,
|
||||
"maxContinualLoginCount": user.Login.MaxContinualLoginCount,
|
||||
"lastLoginDatetime": user.Login.LastLoginDatetime,
|
||||
"lastComebackLoginDatetime": user.Login.LastComebackLoginDatetime,
|
||||
"latestVersion": user.Login.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserLoginBonus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserLoginBonus{
|
||||
UserId: user.UserId,
|
||||
LoginBonusId: user.LoginBonus.LoginBonusId,
|
||||
CurrentPageNumber: user.LoginBonus.CurrentPageNumber,
|
||||
CurrentStampNumber: user.LoginBonus.CurrentStampNumber,
|
||||
LatestRewardReceiveDatetime: user.LoginBonus.LatestRewardReceiveDatetime,
|
||||
LatestVersion: user.LoginBonus.LatestVersion,
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"loginBonusId": user.LoginBonus.LoginBonusId,
|
||||
"currentPageNumber": user.LoginBonus.CurrentPageNumber,
|
||||
"currentStampNumber": user.LoginBonus.CurrentStampNumber,
|
||||
"latestRewardReceiveDatetime": user.LoginBonus.LatestRewardReceiveDatetime,
|
||||
"latestVersion": user.LoginBonus.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserTutorialProgress", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedTutorialRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedTutorialRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMission", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedMissionRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedMissionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserNaviCutIn", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedNaviCutInRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedNaviCutInRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMovie", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedMovieRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedMovieRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserContentsStory", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedContentsStoryRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedContentsStoryRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserOmikuji", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedOmikujiRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedOmikujiRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDokan", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDokanRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedDokanRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPortalCageStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"isCurrentProgress": user.PortalCageStatus.IsCurrentProgress,
|
||||
"dropItemStartDatetime": user.PortalCageStatus.DropItemStartDatetime,
|
||||
@@ -124,7 +125,7 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserEventQuestGuerrillaFreeOpen", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"startDatetime": user.GuerrillaFreeOpen.StartDatetime,
|
||||
"openMinutes": user.GuerrillaFreeOpen.OpenMinutes,
|
||||
@@ -135,11 +136,11 @@ func init() {
|
||||
})
|
||||
|
||||
register("IUserShopItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedShopItemRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedShopItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserShopReplaceable", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"lineupUpdateCount": user.ShopReplaceable.LineupUpdateCount,
|
||||
"latestLineupUpdateDatetime": user.ShopReplaceable.LatestLineupUpdateDatetime,
|
||||
@@ -148,11 +149,26 @@ func init() {
|
||||
return s
|
||||
})
|
||||
register("IUserShopReplaceableLineup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedShopReplaceableLineupRecords(user)...)
|
||||
s, _ := utils.EncodeJSONMaps(sortedShopReplaceableLineupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
|
||||
registerStatic()
|
||||
register("IUserFacebook", func(user store.UserState) string {
|
||||
return ProjectFacebook(user.UserId, user.FacebookId)
|
||||
})
|
||||
registerStatic("IUserApple")
|
||||
}
|
||||
|
||||
func ProjectFacebook(userId int64, facebookId int64) string {
|
||||
if facebookId == 0 {
|
||||
return "[]"
|
||||
}
|
||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||
"userId": userId,
|
||||
"facebookId": facebookId,
|
||||
"latestVersion": gametime.NowMillis(),
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func sortedTutorialRecords(user store.UserState) []map[string]any {
|
||||
|
||||
@@ -99,6 +99,8 @@ func FullClientTableMap(user store.UserState) map[string]string {
|
||||
"IUserBigHuntScheduleMaxScore": projectTable("IUserBigHuntScheduleMaxScore", user),
|
||||
"IUserBigHuntWeeklyMaxScore": projectTable("IUserBigHuntWeeklyMaxScore", user),
|
||||
"IUserBigHuntWeeklyStatus": projectTable("IUserBigHuntWeeklyStatus", user),
|
||||
"IUserFacebook": projectTable("IUserFacebook", user),
|
||||
"IUserApple": projectTable("IUserApple", user),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
// EntityIUser mirrors the game's EntityIUser [MessagePackObject] with [Key(0..7)].
|
||||
// Serialized as a MessagePack array of 8 elements.
|
||||
type EntityIUser struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
PlayerId int64 // Key(1)
|
||||
OsType int32 // Key(2) — 2 = Android
|
||||
PlatformType int32 // Key(3) — 2 = GooglePlay
|
||||
UserRestrictionType int32 // Key(4) — 0 = None
|
||||
RegisterDatetime int64 // Key(5) — unix millis
|
||||
GameStartDatetime int64 // Key(6) — unix millis
|
||||
LatestVersion int64 // Key(7)
|
||||
}
|
||||
|
||||
// EntityIUserSetting mirrors EntityIUserSetting [Key(0..2)].
|
||||
type EntityIUserSetting struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 `json:"userId"` // Key(0)
|
||||
IsNotifyPurchaseAlert bool `json:"isNotifyPurchaseAlert"` // Key(1)
|
||||
LatestVersion int64 `json:"latestVersion"` // Key(2)
|
||||
}
|
||||
|
||||
// EntityIUserTutorialProgress mirrors EntityIUserTutorialProgress [Key(0..4)].
|
||||
type EntityIUserTutorialProgress struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
TutorialType int32 // Key(1)
|
||||
ProgressPhase int32 // Key(2)
|
||||
ChoiceId int32 // Key(3)
|
||||
LatestVersion int64 // Key(4)
|
||||
}
|
||||
|
||||
// EntityIUserQuest mirrors EntityIUserQuest [Key(0..9)].
|
||||
type EntityIUserQuest struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
QuestId int32 // Key(1)
|
||||
QuestStateType int32 // Key(2) — 2 = Cleared
|
||||
IsBattleOnly bool // Key(3)
|
||||
LatestStartDatetime int64 // Key(4) — unix millis
|
||||
ClearCount int32 // Key(5)
|
||||
DailyClearCount int32 // Key(6)
|
||||
LastClearDatetime int64 // Key(7) — unix millis
|
||||
ShortestClearFrames int32 // Key(8)
|
||||
LatestVersion int64 // Key(9)
|
||||
}
|
||||
|
||||
// EntityIUserMainQuestFlowStatus mirrors EntityIUserMainQuestFlowStatus [Key(0..2)].
|
||||
type EntityIUserMainQuestFlowStatus struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
CurrentQuestFlowType int32 // Key(1) // QuestFlowType: 0=UNKNOWN, 1=MAIN_FLOW, 2=SUB_FLOW, 3=REPLAY_FLOW, 4=ANOTHER_ROUTE_REPLAY_FLOW
|
||||
LatestVersion int64 // Key(2)
|
||||
}
|
||||
|
||||
// EntityIUserMainQuestMainFlowStatus mirrors EntityIUserMainQuestMainFlowStatus [Key(0..5)].
|
||||
type EntityIUserMainQuestMainFlowStatus struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
CurrentMainQuestRouteId int32 // Key(1)
|
||||
CurrentQuestSceneId int32 // Key(2)
|
||||
HeadQuestSceneId int32 // Key(3)
|
||||
IsReachedLastQuestScene bool // Key(4)
|
||||
LatestVersion int64 // Key(5)
|
||||
}
|
||||
|
||||
// EntityIUserMainQuestProgressStatus mirrors EntityIUserMainQuestProgressStatus [Key(0..4)].
|
||||
// This table is used by ActivePlayerToEntityPlayingMainQuestStatus (0x2AB4A48).
|
||||
type EntityIUserMainQuestProgressStatus struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
CurrentQuestSceneId int32 // Key(1)
|
||||
HeadQuestSceneId int32 // Key(2)
|
||||
CurrentQuestFlowType int32 // Key(3) // QuestFlowType: 0=UNKNOWN, 1=MAIN_FLOW, 2=SUB_FLOW, 3=REPLAY_FLOW, 4=ANOTHER_ROUTE_REPLAY_FLOW
|
||||
LatestVersion int64 // Key(4)
|
||||
}
|
||||
|
||||
// EntityIUserMainQuestSeasonRoute mirrors EntityIUserMainQuestSeasonRoute [Key(0..3)].
|
||||
type EntityIUserMainQuestSeasonRoute struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
MainQuestSeasonId int32 // Key(1)
|
||||
MainQuestRouteId int32 // Key(2)
|
||||
LatestVersion int64 // Key(3)
|
||||
}
|
||||
|
||||
// EntityIUserStatus mirrors EntityIUserStatus [Key(0..5)].
|
||||
type EntityIUserStatus struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
Level int32 // Key(1)
|
||||
Exp int32 // Key(2)
|
||||
StaminaMilliValue int32 // Key(3)
|
||||
StaminaUpdateDatetime int64 // Key(4)
|
||||
LatestVersion int64 // Key(5)
|
||||
}
|
||||
|
||||
// EntityIUserGem mirrors EntityIUserGem [Key(0..3)].
|
||||
type EntityIUserGem struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 `json:"userId"` // Key(0)
|
||||
PaidGem int32 `json:"paidGem"` // Key(1)
|
||||
FreeGem int32 `json:"freeGem"` // Key(2)
|
||||
LatestVersion int64 `json:"latestVersion"` // Key(3)
|
||||
}
|
||||
|
||||
// EntityIUserProfile mirrors EntityIUserProfile [Key(0..7)].
|
||||
type EntityIUserProfile struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
Name string // Key(1)
|
||||
NameUpdateDatetime int64 // Key(2)
|
||||
Message string // Key(3)
|
||||
MessageUpdateDatetime int64 // Key(4)
|
||||
FavoriteCostumeId int32 // Key(5)
|
||||
FavoriteCostumeIdUpdateDatetime int64 // Key(6)
|
||||
LatestVersion int64 // Key(7)
|
||||
}
|
||||
|
||||
// EntityIUserCharacter mirrors EntityIUserCharacter [Key(0..4)].
|
||||
type EntityIUserCharacter struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
CharacterId int32 // Key(1)
|
||||
Level int32 // Key(2)
|
||||
Exp int32 // Key(3)
|
||||
LatestVersion int64 // Key(4)
|
||||
}
|
||||
|
||||
// EntityIUserCostume mirrors EntityIUserCostume [Key(0..9)].
|
||||
type EntityIUserCostume struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
UserCostumeUuid string // Key(1)
|
||||
CostumeId int32 // Key(2)
|
||||
LimitBreakCount int32 // Key(3)
|
||||
Level int32 // Key(4)
|
||||
Exp int32 // Key(5)
|
||||
HeadupDisplayViewId int32 // Key(6)
|
||||
AcquisitionDatetime int64 // Key(7)
|
||||
AwakenCount int32 // Key(8)
|
||||
LatestVersion int64 // Key(9)
|
||||
}
|
||||
|
||||
// EntityIUserWeapon mirrors EntityIUserWeapon [Key(0..8)].
|
||||
type EntityIUserWeapon struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
UserWeaponUuid string // Key(1)
|
||||
WeaponId int32 // Key(2)
|
||||
Level int32 // Key(3)
|
||||
Exp int32 // Key(4)
|
||||
LimitBreakCount int32 // Key(5)
|
||||
IsProtected bool // Key(6)
|
||||
AcquisitionDatetime int64 // Key(7)
|
||||
LatestVersion int64 // Key(8)
|
||||
}
|
||||
|
||||
// EntityIUserCompanion mirrors EntityIUserCompanion [Key(0..6)].
|
||||
type EntityIUserCompanion struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
UserCompanionUuid string // Key(1)
|
||||
CompanionId int32 // Key(2)
|
||||
HeadupDisplayViewId int32 // Key(3)
|
||||
Level int32 // Key(4)
|
||||
AcquisitionDatetime int64 // Key(5)
|
||||
LatestVersion int64 // Key(6)
|
||||
}
|
||||
|
||||
// EntityIUserDeckCharacter mirrors EntityIUserDeckCharacter [Key(0..7)].
|
||||
type EntityIUserDeckCharacter struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
UserDeckCharacterUuid string // Key(1)
|
||||
UserCostumeUuid string // Key(2)
|
||||
MainUserWeaponUuid string // Key(3)
|
||||
UserCompanionUuid string // Key(4)
|
||||
Power int32 // Key(5)
|
||||
UserThoughtUuid string // Key(6)
|
||||
LatestVersion int64 // Key(7)
|
||||
}
|
||||
|
||||
// EntityIUserDeck mirrors EntityIUserDeck [Key(0..8)].
|
||||
type EntityIUserDeck struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
DeckType int32 // Key(1)
|
||||
UserDeckNumber int32 // Key(2)
|
||||
UserDeckCharacterUuid01 string // Key(3)
|
||||
UserDeckCharacterUuid02 string // Key(4)
|
||||
UserDeckCharacterUuid03 string // Key(5)
|
||||
Name string // Key(6)
|
||||
Power int32 // Key(7)
|
||||
LatestVersion int64 // Key(8)
|
||||
}
|
||||
|
||||
// EntityIUserLogin mirrors EntityIUserLogin [Key(0..6)].
|
||||
type EntityIUserLogin struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 `json:"userId"` // Key(0)
|
||||
TotalLoginCount int32 `json:"totalLoginCount"` // Key(1)
|
||||
ContinualLoginCount int32 `json:"continualLoginCount"` // Key(2)
|
||||
MaxContinualLoginCount int32 `json:"maxContinualLoginCount"` // Key(3)
|
||||
LastLoginDatetime int64 `json:"lastLoginDatetime"` // Key(4)
|
||||
LastComebackLoginDatetime int64 `json:"lastComebackLoginDatetime"` // Key(5)
|
||||
LatestVersion int64 `json:"latestVersion"` // Key(6)
|
||||
}
|
||||
|
||||
// EntityIUserLoginBonus mirrors EntityIUserLoginBonus [Key(0..5)].
|
||||
type EntityIUserLoginBonus struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 `json:"userId"` // Key(0)
|
||||
LoginBonusId int32 `json:"loginBonusId"` // Key(1)
|
||||
CurrentPageNumber int32 `json:"currentPageNumber"` // Key(2)
|
||||
CurrentStampNumber int32 `json:"currentStampNumber"` // Key(3)
|
||||
LatestRewardReceiveDatetime int64 `json:"latestRewardReceiveDatetime"` // Key(4)
|
||||
LatestVersion int64 `json:"latestVersion"` // Key(5)
|
||||
}
|
||||
|
||||
// EntityIUserMission mirrors EntityIUserMission [Key(0..6)].
|
||||
type EntityIUserMission struct {
|
||||
_msgpack struct{} `msgpack:",asArray"`
|
||||
UserId int64 // Key(0)
|
||||
MissionId int32 // Key(1)
|
||||
StartDatetime int64 // Key(2)
|
||||
ProgressValue int32 // Key(3)
|
||||
MissionProgressStatusType int32 // Key(4)
|
||||
ClearDatetime int64 // Key(5)
|
||||
LatestVersion int64 // Key(6)
|
||||
}
|
||||
|
||||
// EncodeRecords serializes a slice of entities to the client-expected format:
|
||||
// a JSON array of base64-encoded MessagePack byte strings.
|
||||
func EncodeRecords(entities ...any) (string, error) {
|
||||
b64List := make([]string, 0, len(entities))
|
||||
for _, e := range entities {
|
||||
data, err := msgpack.Marshal(e)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("msgpack marshal: %w", err)
|
||||
}
|
||||
b64List = append(b64List, base64.StdEncoding.EncodeToString(data))
|
||||
}
|
||||
jsonBytes, err := json.Marshal(b64List)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json marshal: %w", err)
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
func encodeJSONRecords(entities ...any) (string, error) {
|
||||
jsonBytes, err := json.Marshal(entities)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json marshal records: %w", err)
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
func encodeJSONMaps(records ...map[string]any) (string, error) {
|
||||
jsonBytes, err := json.Marshal(records)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json marshal maps: %w", err)
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
// DefaultUserData returns pre-built user data tables for a fresh user.
|
||||
// We provide BOTH msgpack-encoded (base64) and plain JSON variants.
|
||||
// The server tries msgpack first; if the client doesn't accept it, switch to JSON.
|
||||
func DefaultUserData(userId int64) map[string]string {
|
||||
now := gametime.Now().Unix()
|
||||
|
||||
userRecord, _ := EncodeRecords(&EntityIUser{
|
||||
UserId: userId,
|
||||
PlayerId: userId,
|
||||
OsType: 2,
|
||||
PlatformType: 2,
|
||||
RegisterDatetime: now,
|
||||
})
|
||||
|
||||
settingRecord, _ := EncodeRecords(&EntityIUserSetting{
|
||||
UserId: userId,
|
||||
})
|
||||
|
||||
data := map[string]string{
|
||||
"user": userRecord,
|
||||
"user_setting": settingRecord,
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -3,19 +3,20 @@ package utils
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"lunar-tear/server/internal/masterdata/memorydb"
|
||||
)
|
||||
|
||||
func ReadJSON[T any](filename string) ([]T, error) {
|
||||
path := filepath.Join("assets", "master_data", filename)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
var out []T
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s: %w", path, err)
|
||||
}
|
||||
return out, nil
|
||||
// ReadTable deserializes a master data table from the in-memory binary store.
|
||||
// The key is the snake_case table name as it appears in the binary header
|
||||
// (e.g. "m_weapon", "m_costume").
|
||||
func ReadTable[T any](key string) ([]T, error) {
|
||||
return memorydb.ReadTable[T](key)
|
||||
}
|
||||
|
||||
func EncodeJSONMaps(records ...map[string]any) (string, error) {
|
||||
jsonBytes, err := json.Marshal(records)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json marshal maps: %w", err)
|
||||
}
|
||||
return string(jsonBytes), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user