mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Initial commit
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
type DiffSet struct {
|
||||
updates map[string]string
|
||||
deletes map[string]string
|
||||
}
|
||||
|
||||
func NewDiffSet(tables map[string]string) *DiffSet {
|
||||
return &DiffSet{
|
||||
updates: tables,
|
||||
deletes: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *DiffSet) WithDeletes(table, deleteKeysJSON string) *DiffSet {
|
||||
if deleteKeysJSON != "" && deleteKeysJSON != "[]" {
|
||||
ds.deletes[table] = deleteKeysJSON
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
func (ds *DiffSet) Build() map[string]*pb.DiffData {
|
||||
diff := make(map[string]*pb.DiffData, len(ds.updates))
|
||||
for table, payload := range ds.updates {
|
||||
diff[table] = ds.entry(table, payload)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func (ds *DiffSet) BuildOrdered(order []string) map[string]*pb.DiffData {
|
||||
diff := make(map[string]*pb.DiffData, len(order))
|
||||
for _, table := range order {
|
||||
payload := ds.updates[table]
|
||||
diff[table] = ds.entry(table, payload)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func (ds *DiffSet) entry(table, payload string) *pb.DiffData {
|
||||
if payload == "" {
|
||||
payload = "[]"
|
||||
}
|
||||
deleteKeys := "[]"
|
||||
if dk, ok := ds.deletes[table]; ok {
|
||||
deleteKeys = dk
|
||||
}
|
||||
return &pb.DiffData{
|
||||
UpdateRecordsJson: payload,
|
||||
DeleteKeysJson: deleteKeys,
|
||||
}
|
||||
}
|
||||
|
||||
type trackedTable struct {
|
||||
tableName string
|
||||
keyFields []string
|
||||
oldRecords []map[string]any
|
||||
recordsFn func(store.UserState) []map[string]any
|
||||
}
|
||||
|
||||
type DeleteTracker struct {
|
||||
entries []trackedTable
|
||||
}
|
||||
|
||||
func NewDeleteTracker() *DeleteTracker {
|
||||
return &DeleteTracker{}
|
||||
}
|
||||
|
||||
func (dt *DeleteTracker) Track(tableName string, old store.UserState, recordsFn func(store.UserState) []map[string]any, keyFields []string) *DeleteTracker {
|
||||
dt.entries = append(dt.entries, trackedTable{
|
||||
tableName: tableName,
|
||||
keyFields: keyFields,
|
||||
oldRecords: recordsFn(old),
|
||||
recordsFn: recordsFn,
|
||||
})
|
||||
return dt
|
||||
}
|
||||
|
||||
func (dt *DeleteTracker) Apply(newState store.UserState, tables map[string]string) map[string]*pb.DiffData {
|
||||
ds := NewDiffSet(tables)
|
||||
for _, e := range dt.entries {
|
||||
newRecords := e.recordsFn(newState)
|
||||
ds.WithDeletes(e.tableName, ComputeDeleteKeys(e.oldRecords, newRecords, e.keyFields))
|
||||
}
|
||||
return ds.Build()
|
||||
}
|
||||
|
||||
func ComputeDeleteKeys(oldRecords, newRecords []map[string]any, keyFields []string) string {
|
||||
if len(oldRecords) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
|
||||
newSet := make(map[string]struct{}, len(newRecords))
|
||||
for _, r := range newRecords {
|
||||
newSet[compositeKey(r, keyFields)] = struct{}{}
|
||||
}
|
||||
|
||||
var deleted []map[string]any
|
||||
for _, r := range oldRecords {
|
||||
if _, exists := newSet[compositeKey(r, keyFields)]; !exists {
|
||||
deleted = append(deleted, r)
|
||||
}
|
||||
}
|
||||
|
||||
if len(deleted) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
b, err := json.Marshal(deleted)
|
||||
if err != nil {
|
||||
return "[]"
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func compositeKey(record map[string]any, fields []string) string {
|
||||
var sb strings.Builder
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
sb.WriteByte('|')
|
||||
}
|
||||
sb.WriteString(fmt.Sprint(record[f]))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
)
|
||||
|
||||
func EmptyDiff() map[string]*pb.DiffData {
|
||||
return map[string]*pb.DiffData{}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserBigHuntProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentBigHuntBossQuestId": user.BigHuntProgress.CurrentBigHuntBossQuestId,
|
||||
"currentBigHuntQuestId": user.BigHuntProgress.CurrentBigHuntQuestId,
|
||||
"currentQuestSceneId": user.BigHuntProgress.CurrentQuestSceneId,
|
||||
"isDryRun": user.BigHuntProgress.IsDryRun,
|
||||
"latestVersion": user.BigHuntProgress.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserBigHuntMaxScore", func(user store.UserState) string {
|
||||
if len(user.BigHuntMaxScores) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
ids := make([]int, 0, len(user.BigHuntMaxScores))
|
||||
for id := range user.BigHuntMaxScores {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
ms := user.BigHuntMaxScores[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"bigHuntBossId": int32(id),
|
||||
"maxScore": ms.MaxScore,
|
||||
"maxScoreUpdateDatetime": ms.MaxScoreUpdateDatetime,
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserBigHuntStatus", func(user store.UserState) string {
|
||||
if len(user.BigHuntStatuses) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
ids := make([]int, 0, len(user.BigHuntStatuses))
|
||||
for id := range user.BigHuntStatuses {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
st := user.BigHuntStatuses[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"bigHuntBossQuestId": int32(id),
|
||||
"dailyChallengeCount": st.DailyChallengeCount,
|
||||
"latestChallengeDatetime": st.LatestChallengeDatetime,
|
||||
"latestVersion": st.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserBigHuntScheduleMaxScore", func(user store.UserState) string {
|
||||
if len(user.BigHuntScheduleMaxScores) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
type sortableKey struct {
|
||||
ScheduleId int32
|
||||
BossId int32
|
||||
}
|
||||
keys := make([]sortableKey, 0, len(user.BigHuntScheduleMaxScores))
|
||||
for k := range user.BigHuntScheduleMaxScores {
|
||||
keys = append(keys, sortableKey{k.BigHuntScheduleId, k.BigHuntBossId})
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].ScheduleId != keys[j].ScheduleId {
|
||||
return keys[i].ScheduleId < keys[j].ScheduleId
|
||||
}
|
||||
return keys[i].BossId < keys[j].BossId
|
||||
})
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
ms := user.BigHuntScheduleMaxScores[store.BigHuntScheduleScoreKey{BigHuntScheduleId: k.ScheduleId, BigHuntBossId: k.BossId}]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"bigHuntScheduleId": k.ScheduleId,
|
||||
"bigHuntBossId": k.BossId,
|
||||
"maxScore": ms.MaxScore,
|
||||
"maxScoreUpdateDatetime": ms.MaxScoreUpdateDatetime,
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserBigHuntWeeklyMaxScore", func(user store.UserState) string {
|
||||
if len(user.BigHuntWeeklyMaxScores) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
type sortableKey struct {
|
||||
WeeklyVersion int64
|
||||
AttributeType int32
|
||||
}
|
||||
keys := make([]sortableKey, 0, len(user.BigHuntWeeklyMaxScores))
|
||||
for k := range user.BigHuntWeeklyMaxScores {
|
||||
keys = append(keys, sortableKey{k.BigHuntWeeklyVersion, k.AttributeType})
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].WeeklyVersion != keys[j].WeeklyVersion {
|
||||
return keys[i].WeeklyVersion < keys[j].WeeklyVersion
|
||||
}
|
||||
return keys[i].AttributeType < keys[j].AttributeType
|
||||
})
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
ms := user.BigHuntWeeklyMaxScores[store.BigHuntWeeklyScoreKey{BigHuntWeeklyVersion: k.WeeklyVersion, AttributeType: k.AttributeType}]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"bigHuntWeeklyVersion": k.WeeklyVersion,
|
||||
"attributeType": k.AttributeType,
|
||||
"maxScore": ms.MaxScore,
|
||||
"latestVersion": ms.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserBigHuntWeeklyStatus", func(user store.UserState) string {
|
||||
if len(user.BigHuntWeeklyStatuses) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
versions := make([]int64, 0, len(user.BigHuntWeeklyStatuses))
|
||||
for v := range user.BigHuntWeeklyStatuses {
|
||||
versions = append(versions, v)
|
||||
}
|
||||
sort.Slice(versions, func(i, j int) bool { return versions[i] < versions[j] })
|
||||
records := make([]map[string]any, 0, len(versions))
|
||||
for _, v := range versions {
|
||||
ws := user.BigHuntWeeklyStatuses[v]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"bigHuntWeeklyVersion": v,
|
||||
"isReceivedWeeklyReward": ws.IsReceivedWeeklyReward,
|
||||
"latestVersion": ws.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserCharacterBoard", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterBoardAbility", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardAbilityRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterBoardStatusUp", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterBoardStatusUpRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic("IUserCharacterBoardCompleteReward")
|
||||
}
|
||||
|
||||
func sortedCharacterBoardRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.CharacterBoards))
|
||||
for id := range user.CharacterBoards {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.CharacterBoards[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"characterBoardId": row.CharacterBoardId,
|
||||
"panelReleaseBit1": row.PanelReleaseBit1,
|
||||
"panelReleaseBit2": row.PanelReleaseBit2,
|
||||
"panelReleaseBit3": row.PanelReleaseBit3,
|
||||
"panelReleaseBit4": row.PanelReleaseBit4,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCharacterBoardAbilityRecords(user store.UserState) []map[string]any {
|
||||
type entry struct {
|
||||
key store.CharacterBoardAbilityKey
|
||||
state store.CharacterBoardAbilityState
|
||||
}
|
||||
entries := make([]entry, 0, len(user.CharacterBoardAbilities))
|
||||
for k, v := range user.CharacterBoardAbilities {
|
||||
entries = append(entries, entry{k, v})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
if entries[i].key.CharacterId != entries[j].key.CharacterId {
|
||||
return entries[i].key.CharacterId < entries[j].key.CharacterId
|
||||
}
|
||||
return entries[i].key.AbilityId < entries[j].key.AbilityId
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"characterId": e.state.CharacterId,
|
||||
"abilityId": e.state.AbilityId,
|
||||
"level": e.state.Level,
|
||||
"latestVersion": e.state.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCharacterBoardStatusUpRecords(user store.UserState) []map[string]any {
|
||||
type entry struct {
|
||||
key store.CharacterBoardStatusUpKey
|
||||
state store.CharacterBoardStatusUpState
|
||||
}
|
||||
entries := make([]entry, 0, len(user.CharacterBoardStatusUps))
|
||||
for k, v := range user.CharacterBoardStatusUps {
|
||||
entries = append(entries, entry{k, v})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
if entries[i].key.CharacterId != entries[j].key.CharacterId {
|
||||
return entries[i].key.CharacterId < entries[j].key.CharacterId
|
||||
}
|
||||
return entries[i].key.StatusCalculationType < entries[j].key.StatusCalculationType
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"characterId": e.state.CharacterId,
|
||||
"statusCalculationType": e.state.StatusCalculationType,
|
||||
"hp": e.state.Hp,
|
||||
"attack": e.state.Attack,
|
||||
"vitality": e.state.Vitality,
|
||||
"agility": e.state.Agility,
|
||||
"criticalRatio": e.state.CriticalRatio,
|
||||
"criticalAttack": e.state.CriticalAttack,
|
||||
"latestVersion": e.state.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserDeck", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckCharacter", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckCharacterRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckSubWeaponGroup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckSubWeaponGroupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckTypeNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckTypeNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckPartsGroup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckPartsGroupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckCharacterDressupCostume", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDeckDressupCostumeRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
"IUserDeckLimitContentRestricted",
|
||||
)
|
||||
}
|
||||
|
||||
func sortedDeckRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.DeckKey, 0, len(user.Decks))
|
||||
for key := range user.Decks {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].DeckType != keys[j].DeckType {
|
||||
return keys[i].DeckType < keys[j].DeckType
|
||||
}
|
||||
return keys[i].UserDeckNumber < keys[j].UserDeckNumber
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Decks[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"deckType": row.DeckType,
|
||||
"userDeckNumber": row.UserDeckNumber,
|
||||
"userDeckCharacterUuid01": row.UserDeckCharacterUuid01,
|
||||
"userDeckCharacterUuid02": row.UserDeckCharacterUuid02,
|
||||
"userDeckCharacterUuid03": row.UserDeckCharacterUuid03,
|
||||
"name": row.Name,
|
||||
"power": row.Power,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedDeckCharacterRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.DeckCharacters)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.DeckCharacters[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userDeckCharacterUuid": row.UserDeckCharacterUuid,
|
||||
"userCostumeUuid": row.UserCostumeUuid,
|
||||
"mainUserWeaponUuid": row.MainUserWeaponUuid,
|
||||
"userCompanionUuid": row.UserCompanionUuid,
|
||||
"power": row.Power,
|
||||
"userThoughtUuid": row.UserThoughtUuid,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedDeckSubWeaponGroupRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.DeckSubWeapons)
|
||||
records := make([]map[string]any, 0)
|
||||
for _, dcUuid := range keys {
|
||||
weapons := user.DeckSubWeapons[dcUuid]
|
||||
var lv int64
|
||||
if dc, ok := user.DeckCharacters[dcUuid]; ok {
|
||||
lv = dc.LatestVersion
|
||||
}
|
||||
for idx, weaponUuid := range weapons {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userDeckCharacterUuid": dcUuid,
|
||||
"userWeaponUuid": weaponUuid,
|
||||
"sortOrder": int32(idx + 1),
|
||||
"latestVersion": lv,
|
||||
})
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedDeckTypeNoteRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.DeckTypeNotes))
|
||||
for id := range user.DeckTypeNotes {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.DeckTypeNotes[model.DeckType(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"deckType": row.DeckType,
|
||||
"maxDeckPower": row.MaxDeckPower,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedDeckPartsGroupRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.DeckParts)
|
||||
records := make([]map[string]any, 0)
|
||||
for _, dcUuid := range keys {
|
||||
parts := user.DeckParts[dcUuid]
|
||||
var lv int64
|
||||
if dc, ok := user.DeckCharacters[dcUuid]; ok {
|
||||
lv = dc.LatestVersion
|
||||
}
|
||||
for idx, partsUuid := range parts {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userDeckCharacterUuid": dcUuid,
|
||||
"userPartsUuid": partsUuid,
|
||||
"sortOrder": int32(idx + 1),
|
||||
"latestVersion": lv,
|
||||
})
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func DeckSubWeaponRecords(user store.UserState) []map[string]any {
|
||||
return sortedDeckSubWeaponGroupRecords(user)
|
||||
}
|
||||
|
||||
func DeckPartsGroupRecords(user store.UserState) []map[string]any {
|
||||
return sortedDeckPartsGroupRecords(user)
|
||||
}
|
||||
|
||||
func sortedDeckDressupCostumeRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.DeckCharacters)
|
||||
records := make([]map[string]any, 0)
|
||||
for _, key := range keys {
|
||||
row := user.DeckCharacters[key]
|
||||
if row.DressupCostumeId == 0 {
|
||||
continue
|
||||
}
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userDeckCharacterUuid": row.UserDeckCharacterUuid,
|
||||
"dressupCostumeId": row.DressupCostumeId,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func DeckDressupCostumeRecords(user store.UserState) []map[string]any {
|
||||
return sortedDeckDressupCostumeRecords(user)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserGimmick", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickOrnamentProgress", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickOrnamentProgressRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickSequence", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickSequenceRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserGimmickUnlock", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedGimmickUnlockRecords(user)...)
|
||||
return s
|
||||
})
|
||||
}
|
||||
|
||||
func sortedGimmickRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.GimmickKey, 0, len(user.Gimmick.Progress))
|
||||
for key := range user.Gimmick.Progress {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return compareGimmickKey(keys[i], keys[j]) < 0
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Gimmick.Progress[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"gimmickSequenceScheduleId": row.Key.GimmickSequenceScheduleId,
|
||||
"gimmickSequenceId": row.Key.GimmickSequenceId,
|
||||
"gimmickId": row.Key.GimmickId,
|
||||
"isGimmickCleared": row.IsGimmickCleared,
|
||||
"startDatetime": row.StartDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedGimmickOrnamentProgressRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.GimmickOrnamentKey, 0, len(user.Gimmick.OrnamentProgress))
|
||||
for key := range user.Gimmick.OrnamentProgress {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return compareGimmickOrnamentKey(keys[i], keys[j]) < 0
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Gimmick.OrnamentProgress[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"gimmickSequenceScheduleId": row.Key.GimmickSequenceScheduleId,
|
||||
"gimmickSequenceId": row.Key.GimmickSequenceId,
|
||||
"gimmickId": row.Key.GimmickId,
|
||||
"gimmickOrnamentIndex": row.Key.GimmickOrnamentIndex,
|
||||
"progressValueBit": row.ProgressValueBit,
|
||||
"baseDatetime": row.BaseDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedGimmickSequenceRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.GimmickSequenceKey, 0, len(user.Gimmick.Sequences))
|
||||
for key := range user.Gimmick.Sequences {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].GimmickSequenceScheduleId != keys[j].GimmickSequenceScheduleId {
|
||||
return keys[i].GimmickSequenceScheduleId < keys[j].GimmickSequenceScheduleId
|
||||
}
|
||||
return keys[i].GimmickSequenceId < keys[j].GimmickSequenceId
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Gimmick.Sequences[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"gimmickSequenceScheduleId": row.Key.GimmickSequenceScheduleId,
|
||||
"gimmickSequenceId": row.Key.GimmickSequenceId,
|
||||
"isGimmickSequenceCleared": row.IsGimmickSequenceCleared,
|
||||
"clearDatetime": row.ClearDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedGimmickUnlockRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.GimmickKey, 0, len(user.Gimmick.Unlocks))
|
||||
for key := range user.Gimmick.Unlocks {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return compareGimmickKey(keys[i], keys[j]) < 0
|
||||
})
|
||||
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Gimmick.Unlocks[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"gimmickSequenceScheduleId": row.Key.GimmickSequenceScheduleId,
|
||||
"gimmickSequenceId": row.Key.GimmickSequenceId,
|
||||
"gimmickId": row.Key.GimmickId,
|
||||
"isUnlocked": row.IsUnlocked,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUserCharacter", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostume", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeapon", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponStory", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedWeaponStoryRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedWeaponNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCompanion", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCompanionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserThought", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedThoughtRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserConsumableItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedConsumableItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMaterial", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedMaterialRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserImportantItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedImportantItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPremiumItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPremiumItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserParts", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedPartsRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeActiveSkill", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeActiveSkillRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponSkill", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponSkillRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserWeaponAbility", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(SortedWeaponAbilityRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserExplore", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(exploreRecord(user))
|
||||
return s
|
||||
})
|
||||
register("IUserExploreScore", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedExploreScoreRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPartsGroupNote", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPartsGroupNoteRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPartsPreset", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedPartsPresetRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCostumeAwakenStatusUp", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCostumeAwakenStatusUpRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserAutoSaleSettingDetail", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedAutoSaleSettingRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCharacterRebirth", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCharacterRebirthRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserCageOrnamentReward", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedCageOrnamentRewardRecords(user)...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
"IUserCostumeLevelBonusReleaseStatus",
|
||||
"IUserCostumeLotteryEffect",
|
||||
"IUserCostumeLotteryEffectAbility",
|
||||
"IUserCostumeLotteryEffectStatusUp",
|
||||
"IUserCostumeLotteryEffectPending",
|
||||
"IUserWeaponAwaken",
|
||||
"IUserPartsPresetTag",
|
||||
"IUserPartsStatusSub",
|
||||
)
|
||||
}
|
||||
|
||||
func sortedCharacterRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.Characters))
|
||||
for id := range user.Characters {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.Characters[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"characterId": row.CharacterId,
|
||||
"level": row.Level,
|
||||
"exp": row.Exp,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCostumeRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.Costumes)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Costumes[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userCostumeUuid": row.UserCostumeUuid,
|
||||
"costumeId": row.CostumeId,
|
||||
"limitBreakCount": row.LimitBreakCount,
|
||||
"level": row.Level,
|
||||
"exp": row.Exp,
|
||||
"headupDisplayViewId": row.HeadupDisplayViewId,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"awakenCount": row.AwakenCount,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedAutoSaleSettingRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.AutoSaleSettings))
|
||||
for id := range user.AutoSaleSettings {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.AutoSaleSettings[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"possessionAutoSaleItemType": row.PossessionAutoSaleItemType,
|
||||
"possessionAutoSaleItemValue": row.PossessionAutoSaleItemValue,
|
||||
"latestVersion": gametime.NowMillis(),
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCostumeAwakenStatusUpRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.CostumeAwakenStatusKey, 0, len(user.CostumeAwakenStatusUps))
|
||||
for k := range user.CostumeAwakenStatusUps {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].UserCostumeUuid != keys[j].UserCostumeUuid {
|
||||
return keys[i].UserCostumeUuid < keys[j].UserCostumeUuid
|
||||
}
|
||||
return keys[i].StatusCalculationType < keys[j].StatusCalculationType
|
||||
})
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
row := user.CostumeAwakenStatusUps[k]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userCostumeUuid": row.UserCostumeUuid,
|
||||
"statusCalculationType": int32(row.StatusCalculationType),
|
||||
"hp": row.Hp,
|
||||
"attack": row.Attack,
|
||||
"vitality": row.Vitality,
|
||||
"agility": row.Agility,
|
||||
"criticalRatio": row.CriticalRatio,
|
||||
"criticalAttack": row.CriticalAttack,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedWeaponRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.Weapons)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Weapons[key]
|
||||
uuid := row.UserWeaponUuid
|
||||
if uuid == "" {
|
||||
log.Printf("[userdata] sortedWeaponRecords: using key as fallback for weapon key=%q (empty userWeaponUuid)", key)
|
||||
uuid = key
|
||||
}
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userWeaponUuid": uuid,
|
||||
"weaponId": row.WeaponId,
|
||||
"level": row.Level,
|
||||
"exp": row.Exp,
|
||||
"limitBreakCount": row.LimitBreakCount,
|
||||
"isProtected": row.IsProtected,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedWeaponStoryRecords(user store.UserState) []map[string]any {
|
||||
if user.WeaponStories == nil {
|
||||
return []map[string]any{}
|
||||
}
|
||||
weaponIdsInWeapons := make(map[int32]bool)
|
||||
for _, row := range user.Weapons {
|
||||
weaponIdsInWeapons[row.WeaponId] = true
|
||||
}
|
||||
weaponIds := make([]int32, 0, len(user.WeaponStories))
|
||||
for weaponId := range user.WeaponStories {
|
||||
if weaponIdsInWeapons[weaponId] {
|
||||
weaponIds = append(weaponIds, weaponId)
|
||||
}
|
||||
}
|
||||
sort.Slice(weaponIds, func(i, j int) bool { return weaponIds[i] < weaponIds[j] })
|
||||
records := make([]map[string]any, 0, len(weaponIds))
|
||||
for _, weaponId := range weaponIds {
|
||||
row := user.WeaponStories[weaponId]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"weaponId": row.WeaponId,
|
||||
"releasedMaxStoryIndex": row.ReleasedMaxStoryIndex,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedWeaponNoteRecords(user store.UserState) []map[string]any {
|
||||
weaponIds := make([]int32, 0, len(user.WeaponNotes))
|
||||
for id := range user.WeaponNotes {
|
||||
weaponIds = append(weaponIds, id)
|
||||
}
|
||||
sort.Slice(weaponIds, func(i, j int) bool { return weaponIds[i] < weaponIds[j] })
|
||||
records := make([]map[string]any, 0, len(weaponIds))
|
||||
for _, id := range weaponIds {
|
||||
row := user.WeaponNotes[id]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"weaponId": row.WeaponId,
|
||||
"maxLevel": row.MaxLevel,
|
||||
"maxLimitBreakCount": row.MaxLimitBreakCount,
|
||||
"firstAcquisitionDatetime": row.FirstAcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCompanionRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.Companions)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Companions[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userCompanionUuid": row.UserCompanionUuid,
|
||||
"companionId": row.CompanionId,
|
||||
"headupDisplayViewId": row.HeadupDisplayViewId,
|
||||
"level": row.Level,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedThoughtRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.Thoughts)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Thoughts[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userThoughtUuid": row.UserThoughtUuid,
|
||||
"thoughtId": row.ThoughtId,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedConsumableItemRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.ConsumableItems))
|
||||
for id := range user.ConsumableItems {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"consumableItemId": int32(id),
|
||||
"count": user.ConsumableItems[int32(id)],
|
||||
"firstAcquisitionDatetime": nowMillis,
|
||||
"latestVersion": nowMillis,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedMaterialRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.Materials))
|
||||
for id := range user.Materials {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"materialId": int32(id),
|
||||
"count": user.Materials[int32(id)],
|
||||
"firstAcquisitionDatetime": nowMillis,
|
||||
"latestVersion": nowMillis,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedImportantItemRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.ImportantItems))
|
||||
for id := range user.ImportantItems {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"importantItemId": int32(id),
|
||||
"count": user.ImportantItems[int32(id)],
|
||||
"firstAcquisitionDatetime": nowMillis,
|
||||
"latestVersion": nowMillis,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedPremiumItemRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.PremiumItems))
|
||||
for id := range user.PremiumItems {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
nowMillis := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
acqTime := user.PremiumItems[int32(id)]
|
||||
if acqTime == 0 {
|
||||
acqTime = nowMillis
|
||||
}
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"premiumItemId": int32(id),
|
||||
"acquisitionDatetime": acqTime,
|
||||
"latestVersion": nowMillis,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedPartsRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.Parts)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.Parts[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userPartsUuid": row.UserPartsUuid,
|
||||
"partsId": row.PartsId,
|
||||
"level": row.Level,
|
||||
"partsStatusMainId": row.PartsStatusMainId,
|
||||
"isProtected": row.IsProtected,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedPartsGroupNoteRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.PartsGroupNotes))
|
||||
for id := range user.PartsGroupNotes {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.PartsGroupNotes[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"partsGroupId": row.PartsGroupId,
|
||||
"firstAcquisitionDatetime": row.FirstAcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedPartsPresetRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.PartsPresets))
|
||||
for id := range user.PartsPresets {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.PartsPresets[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userPartsPresetNumber": row.UserPartsPresetNumber,
|
||||
"userPartsUuid01": row.UserPartsUuid01,
|
||||
"userPartsUuid02": row.UserPartsUuid02,
|
||||
"userPartsUuid03": row.UserPartsUuid03,
|
||||
"name": row.Name,
|
||||
"userPartsPresetTagNumber": row.UserPartsPresetTagNumber,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCostumeActiveSkillRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.CostumeActiveSkills)
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.CostumeActiveSkills[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userCostumeUuid": row.UserCostumeUuid,
|
||||
"level": row.Level,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedWeaponSkillRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.WeaponSkills)
|
||||
records := make([]map[string]any, 0)
|
||||
for _, key := range keys {
|
||||
for _, row := range user.WeaponSkills[key] {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userWeaponUuid": row.UserWeaponUuid,
|
||||
"slotNumber": row.SlotNumber,
|
||||
"level": row.Level,
|
||||
"latestVersion": int64(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func SortedWeaponAbilityRecords(user store.UserState) []map[string]any {
|
||||
keys := sortedStringKeys(user.WeaponAbilities)
|
||||
records := make([]map[string]any, 0)
|
||||
for _, key := range keys {
|
||||
for _, row := range user.WeaponAbilities[key] {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"userWeaponUuid": row.UserWeaponUuid,
|
||||
"slotNumber": row.SlotNumber,
|
||||
"level": row.Level,
|
||||
"latestVersion": int64(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func exploreRecord(user store.UserState) map[string]any {
|
||||
return map[string]any{
|
||||
"userId": user.UserId,
|
||||
"isUseExploreTicket": user.Explore.IsUseExploreTicket,
|
||||
"playingExploreId": user.Explore.PlayingExploreId,
|
||||
"latestPlayDatetime": user.Explore.LatestPlayDatetime,
|
||||
"latestVersion": user.Explore.LatestVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func sortedCharacterRebirthRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.CharacterRebirths))
|
||||
for id := range user.CharacterRebirths {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.CharacterRebirths[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"characterId": row.CharacterId,
|
||||
"rebirthCount": row.RebirthCount,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedExploreScoreRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.ExploreScores))
|
||||
for id := range user.ExploreScores {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.ExploreScores[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"exploreId": row.ExploreId,
|
||||
"maxScore": row.MaxScore,
|
||||
"maxScoreUpdateDatetime": row.MaxScoreUpdateDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedCageOrnamentRewardRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.CageOrnamentRewards))
|
||||
for id := range user.CageOrnamentRewards {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.CageOrnamentRewards[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"cageOrnamentId": row.CageOrnamentId,
|
||||
"acquisitionDatetime": row.AcquisitionDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func sortedQuestRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.Quests))
|
||||
for id := range user.Quests {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.Quests[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"questId": row.QuestId,
|
||||
"questStateType": row.QuestStateType,
|
||||
"isBattleOnly": row.IsBattleOnly,
|
||||
"latestStartDatetime": row.LatestStartDatetime,
|
||||
"clearCount": row.ClearCount,
|
||||
"dailyClearCount": row.DailyClearCount,
|
||||
"lastClearDatetime": row.LastClearDatetime,
|
||||
"shortestClearFrames": row.ShortestClearFrames,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedQuestMissionRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.QuestMissionKey, 0, len(user.QuestMissions))
|
||||
for key := range user.QuestMissions {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].QuestId != keys[j].QuestId {
|
||||
return keys[i].QuestId < keys[j].QuestId
|
||||
}
|
||||
return keys[i].QuestMissionId < keys[j].QuestMissionId
|
||||
})
|
||||
records := make([]map[string]any, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
row := user.QuestMissions[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"questId": row.QuestId,
|
||||
"questMissionId": row.QuestMissionId,
|
||||
"progressValue": row.ProgressValue,
|
||||
"isClear": row.IsClear,
|
||||
"latestClearDatetime": row.LatestClearDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("IUserQuest", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedQuestRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserQuestMission", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedQuestMissionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestFlowType": user.MainQuest.CurrentQuestFlowType,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestMainFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentMainQuestRouteId": user.MainQuest.CurrentMainQuestRouteId,
|
||||
"currentQuestSceneId": user.MainQuest.CurrentQuestSceneId,
|
||||
"headQuestSceneId": user.MainQuest.HeadQuestSceneId,
|
||||
"isReachedLastQuestScene": user.MainQuest.IsReachedLastQuestScene,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestSceneId": user.MainQuest.ProgressQuestSceneId,
|
||||
"headQuestSceneId": user.MainQuest.ProgressHeadQuestSceneId,
|
||||
"currentQuestFlowType": user.MainQuest.ProgressQuestFlowType,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestSeasonRoute", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"mainQuestSeasonId": user.MainQuest.MainQuestSeasonId,
|
||||
"mainQuestRouteId": user.MainQuest.CurrentMainQuestRouteId,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserEventQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentEventQuestChapterId": user.EventQuest.CurrentEventQuestChapterId,
|
||||
"currentQuestId": user.EventQuest.CurrentQuestId,
|
||||
"currentQuestSceneId": user.EventQuest.CurrentQuestSceneId,
|
||||
"headQuestSceneId": user.EventQuest.HeadQuestSceneId,
|
||||
"latestVersion": user.EventQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserExtraQuestProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentQuestId": user.ExtraQuest.CurrentQuestId,
|
||||
"currentQuestSceneId": user.ExtraQuest.CurrentQuestSceneId,
|
||||
"headQuestSceneId": user.ExtraQuest.HeadQuestSceneId,
|
||||
"latestVersion": user.ExtraQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserMainQuestReplayFlowStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentHeadQuestSceneId": user.MainQuest.ReplayFlowHeadQuestSceneId,
|
||||
"currentQuestSceneId": user.MainQuest.ReplayFlowCurrentQuestSceneId,
|
||||
"latestVersion": user.MainQuest.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserSideStoryQuestSceneProgressStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"currentSideStoryQuestId": user.SideStoryActiveProgress.CurrentSideStoryQuestId,
|
||||
"currentSideStoryQuestSceneId": user.SideStoryActiveProgress.CurrentSideStoryQuestSceneId,
|
||||
"latestVersion": user.SideStoryActiveProgress.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserSideStoryQuest", func(user store.UserState) string {
|
||||
if len(user.SideStoryQuests) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
ids := make([]int, 0, len(user.SideStoryQuests))
|
||||
for id := range user.SideStoryQuests {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
progress := user.SideStoryQuests[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"sideStoryQuestId": int32(id),
|
||||
"headSideStoryQuestSceneId": progress.HeadSideStoryQuestSceneId,
|
||||
"sideStoryQuestStateType": progress.SideStoryQuestStateType,
|
||||
"latestVersion": progress.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
register("IUserQuestLimitContentStatus", func(user store.UserState) string {
|
||||
if len(user.QuestLimitContentStatus) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
ids := make([]int, 0, len(user.QuestLimitContentStatus))
|
||||
for id := range user.QuestLimitContentStatus {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
st := user.QuestLimitContentStatus[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"questId": int32(id),
|
||||
"limitContentQuestStatusType": st.LimitContentQuestStatusType,
|
||||
"eventQuestChapterId": st.EventQuestChapterId,
|
||||
"latestVersion": st.LatestVersion,
|
||||
})
|
||||
}
|
||||
s, _ := encodeJSONMaps(records...)
|
||||
return s
|
||||
})
|
||||
registerStatic(
|
||||
"IUserEventQuestDailyGroupCompleteReward",
|
||||
"IUserEventQuestLabyrinthSeason",
|
||||
"IUserEventQuestLabyrinthStage",
|
||||
"IUserEventQuestTowerAccumulationReward",
|
||||
"IUserQuestReplayFlowRewardGroup",
|
||||
"IUserQuestAutoOrbit",
|
||||
"IUserQuestSceneChoice",
|
||||
"IUserQuestSceneChoiceHistory",
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("IUser", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"playerId": user.PlayerId,
|
||||
"osType": user.OsType,
|
||||
"platformType": user.PlatformType,
|
||||
"userRestrictionType": user.UserRestrictionType,
|
||||
"registerDatetime": user.RegisterDatetime,
|
||||
"gameStartDatetime": user.GameStartDatetime,
|
||||
"latestVersion": user.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserSetting", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserSetting{
|
||||
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{
|
||||
"userId": user.UserId,
|
||||
"level": user.Status.Level,
|
||||
"exp": user.Status.Exp,
|
||||
"staminaMilliValue": user.Status.StaminaMilliValue,
|
||||
"staminaUpdateDatetime": user.Status.StaminaUpdateDatetime,
|
||||
"latestVersion": user.Status.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserGem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONRecords(&EntityIUserGem{
|
||||
UserId: user.UserId,
|
||||
PaidGem: user.Gem.PaidGem,
|
||||
FreeGem: user.Gem.FreeGem,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserProfile", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"name": user.Profile.Name,
|
||||
"nameUpdateDatetime": user.Profile.NameUpdateDatetime,
|
||||
"message": user.Profile.Message,
|
||||
"messageUpdateDatetime": user.Profile.MessageUpdateDatetime,
|
||||
"favoriteCostumeId": user.Profile.FavoriteCostumeId,
|
||||
"favoriteCostumeIdUpdateDatetime": user.Profile.FavoriteCostumeIdUpdateDatetime,
|
||||
"latestVersion": user.Profile.LatestVersion,
|
||||
})
|
||||
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,
|
||||
})
|
||||
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,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserTutorialProgress", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedTutorialRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMission", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedMissionRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserNaviCutIn", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedNaviCutInRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserMovie", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedMovieRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserContentsStory", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedContentsStoryRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserOmikuji", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedOmikujiRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDokan", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedDokanRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserPortalCageStatus", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"isCurrentProgress": user.PortalCageStatus.IsCurrentProgress,
|
||||
"dropItemStartDatetime": user.PortalCageStatus.DropItemStartDatetime,
|
||||
"currentDropItemCount": user.PortalCageStatus.CurrentDropItemCount,
|
||||
"latestVersion": user.PortalCageStatus.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserEventQuestGuerrillaFreeOpen", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"startDatetime": user.GuerrillaFreeOpen.StartDatetime,
|
||||
"openMinutes": user.GuerrillaFreeOpen.OpenMinutes,
|
||||
"dailyOpenedCount": user.GuerrillaFreeOpen.DailyOpenedCount,
|
||||
"latestVersion": user.GuerrillaFreeOpen.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
|
||||
register("IUserShopItem", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedShopItemRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserShopReplaceable", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(map[string]any{
|
||||
"userId": user.UserId,
|
||||
"lineupUpdateCount": user.ShopReplaceable.LineupUpdateCount,
|
||||
"latestLineupUpdateDatetime": user.ShopReplaceable.LatestLineupUpdateDatetime,
|
||||
"latestVersion": user.ShopReplaceable.LatestVersion,
|
||||
})
|
||||
return s
|
||||
})
|
||||
register("IUserShopReplaceableLineup", func(user store.UserState) string {
|
||||
s, _ := encodeJSONMaps(sortedShopReplaceableLineupRecords(user)...)
|
||||
return s
|
||||
})
|
||||
|
||||
registerStatic()
|
||||
}
|
||||
|
||||
func sortedTutorialRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.Tutorials))
|
||||
for id := range user.Tutorials {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.Tutorials[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"tutorialType": row.TutorialType,
|
||||
"progressPhase": row.ProgressPhase,
|
||||
"choiceId": row.ChoiceId,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedMissionRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.Missions))
|
||||
for id := range user.Missions {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.Missions[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"missionId": row.MissionId,
|
||||
"startDatetime": row.StartDatetime,
|
||||
"progressValue": row.ProgressValue,
|
||||
"missionProgressStatusType": row.MissionProgressStatusType,
|
||||
"clearDatetime": row.ClearDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedNaviCutInRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int32, 0, len(user.NaviCutInPlayed))
|
||||
for id := range user.NaviCutInPlayed {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"naviCutInId": id,
|
||||
"playDatetime": now,
|
||||
"latestVersion": now,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedContentsStoryRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int32, 0, len(user.ContentsStories))
|
||||
for id := range user.ContentsStories {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"contentsStoryId": id,
|
||||
"playDatetime": user.ContentsStories[id],
|
||||
"latestVersion": now,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedMovieRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int32, 0, len(user.ViewedMovies))
|
||||
for id := range user.ViewedMovies {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"movieId": id,
|
||||
"latestViewedDatetime": user.ViewedMovies[id],
|
||||
"latestVersion": now,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedOmikujiRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int32, 0, len(user.DrawnOmikuji))
|
||||
for id := range user.DrawnOmikuji {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"omikujiId": id,
|
||||
"latestDrawDatetime": user.DrawnOmikuji[id],
|
||||
"latestVersion": now,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedDokanRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int32, 0, len(user.DokanConfirmed))
|
||||
for id := range user.DokanConfirmed {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
||||
now := gametime.NowMillis()
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"dokanId": id,
|
||||
"displayDatetime": now,
|
||||
"latestVersion": now,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedShopItemRecords(user store.UserState) []map[string]any {
|
||||
ids := make([]int, 0, len(user.ShopItems))
|
||||
for id := range user.ShopItems {
|
||||
ids = append(ids, int(id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
records := make([]map[string]any, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
row := user.ShopItems[int32(id)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"shopItemId": row.ShopItemId,
|
||||
"boughtCount": row.BoughtCount,
|
||||
"latestBoughtCountChangedDatetime": row.LatestBoughtCountChangedDatetime,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedShopReplaceableLineupRecords(user store.UserState) []map[string]any {
|
||||
slots := make([]int, 0, len(user.ShopReplaceableLineup))
|
||||
for slot := range user.ShopReplaceableLineup {
|
||||
slots = append(slots, int(slot))
|
||||
}
|
||||
sort.Ints(slots)
|
||||
records := make([]map[string]any, 0, len(slots))
|
||||
for _, slot := range slots {
|
||||
row := user.ShopReplaceableLineup[int32(slot)]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"slotNumber": row.SlotNumber,
|
||||
"shopItemId": row.ShopItemId,
|
||||
"latestVersion": row.LatestVersion,
|
||||
})
|
||||
}
|
||||
return records
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
type Projector func(user store.UserState) string
|
||||
|
||||
var projectors = make(map[string]Projector)
|
||||
|
||||
func register(tableName string, fn Projector) {
|
||||
projectors[tableName] = fn
|
||||
}
|
||||
|
||||
func registerStatic(tableNames ...string) {
|
||||
for _, name := range tableNames {
|
||||
projectors[name] = func(_ store.UserState) string { return "[]" }
|
||||
}
|
||||
}
|
||||
|
||||
func projectTable(tableName string, user store.UserState) string {
|
||||
fn, ok := projectors[tableName]
|
||||
if !ok {
|
||||
return "[]"
|
||||
}
|
||||
s := fn(user)
|
||||
if s == "" {
|
||||
return "[]"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func sortedStringKeys[T any](rows map[string]T) []string {
|
||||
keys := make([]string, 0, len(rows))
|
||||
for key := range rows {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func compareGimmickKey(a, b store.GimmickKey) int {
|
||||
if a.GimmickSequenceScheduleId != b.GimmickSequenceScheduleId {
|
||||
if a.GimmickSequenceScheduleId < b.GimmickSequenceScheduleId {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if a.GimmickSequenceId != b.GimmickSequenceId {
|
||||
if a.GimmickSequenceId < b.GimmickSequenceId {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if a.GimmickId < b.GimmickId {
|
||||
return -1
|
||||
}
|
||||
if a.GimmickId > b.GimmickId {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func compareGimmickOrnamentKey(a, b store.GimmickOrnamentKey) int {
|
||||
if cmp := compareGimmickKey(
|
||||
store.GimmickKey{
|
||||
GimmickSequenceScheduleId: a.GimmickSequenceScheduleId,
|
||||
GimmickSequenceId: a.GimmickSequenceId,
|
||||
GimmickId: a.GimmickId,
|
||||
},
|
||||
store.GimmickKey{
|
||||
GimmickSequenceScheduleId: b.GimmickSequenceScheduleId,
|
||||
GimmickSequenceId: b.GimmickSequenceId,
|
||||
GimmickId: b.GimmickId,
|
||||
},
|
||||
); cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
if a.GimmickOrnamentIndex < b.GimmickOrnamentIndex {
|
||||
return -1
|
||||
}
|
||||
if a.GimmickOrnamentIndex > b.GimmickOrnamentIndex {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package userdata
|
||||
|
||||
import (
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func FullClientTableMap(user store.UserState) map[string]string {
|
||||
return map[string]string{
|
||||
"IUser": projectTable("IUser", user),
|
||||
"IUserSetting": projectTable("IUserSetting", user),
|
||||
"IUserStatus": projectTable("IUserStatus", user),
|
||||
"IUserGem": projectTable("IUserGem", user),
|
||||
"IUserProfile": projectTable("IUserProfile", user),
|
||||
"IUserCharacter": projectTable("IUserCharacter", user),
|
||||
"IUserCostume": projectTable("IUserCostume", user),
|
||||
"IUserWeapon": projectTable("IUserWeapon", user),
|
||||
"IUserWeaponStory": projectTable("IUserWeaponStory", user),
|
||||
"IUserCompanion": projectTable("IUserCompanion", user),
|
||||
"IUserThought": projectTable("IUserThought", user),
|
||||
"IUserDeckCharacter": projectTable("IUserDeckCharacter", user),
|
||||
"IUserDeck": projectTable("IUserDeck", user),
|
||||
"IUserLogin": projectTable("IUserLogin", user),
|
||||
"IUserLoginBonus": projectTable("IUserLoginBonus", user),
|
||||
"IUserMission": projectTable("IUserMission", user),
|
||||
"IUserMainQuestFlowStatus": projectTable("IUserMainQuestFlowStatus", user),
|
||||
"IUserMainQuestMainFlowStatus": projectTable("IUserMainQuestMainFlowStatus", user),
|
||||
"IUserMainQuestProgressStatus": projectTable("IUserMainQuestProgressStatus", user),
|
||||
"IUserMainQuestSeasonRoute": projectTable("IUserMainQuestSeasonRoute", user),
|
||||
"IUserQuest": projectTable("IUserQuest", user),
|
||||
"IUserQuestMission": projectTable("IUserQuestMission", user),
|
||||
"IUserTutorialProgress": projectTable("IUserTutorialProgress", user),
|
||||
"IUserGimmick": projectTable("IUserGimmick", user),
|
||||
"IUserGimmickOrnamentProgress": projectTable("IUserGimmickOrnamentProgress", user),
|
||||
"IUserGimmickSequence": projectTable("IUserGimmickSequence", user),
|
||||
"IUserGimmickUnlock": projectTable("IUserGimmickUnlock", user),
|
||||
"IUserMaterial": projectTable("IUserMaterial", user),
|
||||
"IUserConsumableItem": projectTable("IUserConsumableItem", user),
|
||||
"IUserParts": projectTable("IUserParts", user),
|
||||
"IUserImportantItem": projectTable("IUserImportantItem", user),
|
||||
"IUserPremiumItem": projectTable("IUserPremiumItem", user),
|
||||
"IUserDeckPartsGroup": projectTable("IUserDeckPartsGroup", user),
|
||||
"IUserDeckSubWeaponGroup": projectTable("IUserDeckSubWeaponGroup", user),
|
||||
"IUserDeckCharacterDressupCostume": projectTable("IUserDeckCharacterDressupCostume", user),
|
||||
"IUserDeckTypeNote": projectTable("IUserDeckTypeNote", user),
|
||||
"IUserDeckLimitContentRestricted": projectTable("IUserDeckLimitContentRestricted", user),
|
||||
"IUserCostumeActiveSkill": projectTable("IUserCostumeActiveSkill", user),
|
||||
"IUserCostumeAwakenStatusUp": projectTable("IUserCostumeAwakenStatusUp", user),
|
||||
"IUserCostumeLevelBonusReleaseStatus": projectTable("IUserCostumeLevelBonusReleaseStatus", user),
|
||||
"IUserCostumeLotteryEffect": projectTable("IUserCostumeLotteryEffect", user),
|
||||
"IUserCostumeLotteryEffectAbility": projectTable("IUserCostumeLotteryEffectAbility", user),
|
||||
"IUserCostumeLotteryEffectStatusUp": projectTable("IUserCostumeLotteryEffectStatusUp", user),
|
||||
"IUserCostumeLotteryEffectPending": projectTable("IUserCostumeLotteryEffectPending", user),
|
||||
"IUserWeaponNote": projectTable("IUserWeaponNote", user),
|
||||
"IUserWeaponAbility": projectTable("IUserWeaponAbility", user),
|
||||
"IUserWeaponSkill": projectTable("IUserWeaponSkill", user),
|
||||
"IUserWeaponAwaken": projectTable("IUserWeaponAwaken", user),
|
||||
"IUserPartsGroupNote": projectTable("IUserPartsGroupNote", user),
|
||||
"IUserPartsPreset": projectTable("IUserPartsPreset", user),
|
||||
"IUserPartsPresetTag": projectTable("IUserPartsPresetTag", user),
|
||||
"IUserPartsStatusSub": projectTable("IUserPartsStatusSub", user),
|
||||
"IUserNaviCutIn": projectTable("IUserNaviCutIn", user),
|
||||
"IUserMovie": projectTable("IUserMovie", user),
|
||||
"IUserContentsStory": projectTable("IUserContentsStory", user),
|
||||
"IUserOmikuji": projectTable("IUserOmikuji", user),
|
||||
"IUserDokan": projectTable("IUserDokan", user),
|
||||
"IUserPortalCageStatus": projectTable("IUserPortalCageStatus", user),
|
||||
"IUserEventQuestGuerrillaFreeOpen": projectTable("IUserEventQuestGuerrillaFreeOpen", user),
|
||||
"IUserEventQuestProgressStatus": projectTable("IUserEventQuestProgressStatus", user),
|
||||
"IUserExtraQuestProgressStatus": projectTable("IUserExtraQuestProgressStatus", user),
|
||||
"IUserEventQuestDailyGroupCompleteReward": projectTable("IUserEventQuestDailyGroupCompleteReward", user),
|
||||
"IUserEventQuestLabyrinthSeason": projectTable("IUserEventQuestLabyrinthSeason", user),
|
||||
"IUserEventQuestLabyrinthStage": projectTable("IUserEventQuestLabyrinthStage", user),
|
||||
"IUserEventQuestTowerAccumulationReward": projectTable("IUserEventQuestTowerAccumulationReward", user),
|
||||
"IUserMainQuestReplayFlowStatus": projectTable("IUserMainQuestReplayFlowStatus", user),
|
||||
"IUserSideStoryQuest": projectTable("IUserSideStoryQuest", user),
|
||||
"IUserSideStoryQuestSceneProgressStatus": projectTable("IUserSideStoryQuestSceneProgressStatus", user),
|
||||
"IUserQuestLimitContentStatus": projectTable("IUserQuestLimitContentStatus", user),
|
||||
"IUserQuestReplayFlowRewardGroup": projectTable("IUserQuestReplayFlowRewardGroup", user),
|
||||
"IUserQuestAutoOrbit": projectTable("IUserQuestAutoOrbit", user),
|
||||
"IUserQuestSceneChoice": projectTable("IUserQuestSceneChoice", user),
|
||||
"IUserQuestSceneChoiceHistory": projectTable("IUserQuestSceneChoiceHistory", user),
|
||||
"IUserShopItem": projectTable("IUserShopItem", user),
|
||||
"IUserShopReplaceable": projectTable("IUserShopReplaceable", user),
|
||||
"IUserShopReplaceableLineup": projectTable("IUserShopReplaceableLineup", user),
|
||||
"IUserExplore": projectTable("IUserExplore", user),
|
||||
"IUserExploreScore": projectTable("IUserExploreScore", user),
|
||||
"IUserCharacterBoard": projectTable("IUserCharacterBoard", user),
|
||||
"IUserCharacterBoardAbility": projectTable("IUserCharacterBoardAbility", user),
|
||||
"IUserCharacterBoardStatusUp": projectTable("IUserCharacterBoardStatusUp", user),
|
||||
"IUserCharacterBoardCompleteReward": projectTable("IUserCharacterBoardCompleteReward", user),
|
||||
"IUserAutoSaleSettingDetail": projectTable("IUserAutoSaleSettingDetail", user),
|
||||
"IUserCharacterRebirth": projectTable("IUserCharacterRebirth", user),
|
||||
"IUserCageOrnamentReward": projectTable("IUserCageOrnamentReward", user),
|
||||
"IUserBigHuntProgressStatus": projectTable("IUserBigHuntProgressStatus", user),
|
||||
"IUserBigHuntMaxScore": projectTable("IUserBigHuntMaxScore", user),
|
||||
"IUserBigHuntStatus": projectTable("IUserBigHuntStatus", user),
|
||||
"IUserBigHuntScheduleMaxScore": projectTable("IUserBigHuntScheduleMaxScore", user),
|
||||
"IUserBigHuntWeeklyMaxScore": projectTable("IUserBigHuntWeeklyMaxScore", user),
|
||||
"IUserBigHuntWeeklyStatus": projectTable("IUserBigHuntWeeklyStatus", user),
|
||||
}
|
||||
}
|
||||
|
||||
func FirstEntranceClientTableMap(user store.UserState) map[string]string {
|
||||
tables := FullClientTableMap(user)
|
||||
for _, table := range []string{
|
||||
"IUserCharacter",
|
||||
"IUserCostume",
|
||||
"IUserWeapon",
|
||||
"IUserCompanion",
|
||||
"IUserDeckCharacter",
|
||||
"IUserDeck",
|
||||
"IUserTutorialProgress",
|
||||
"IUserParts",
|
||||
"IUserWeaponNote",
|
||||
"IUserWeaponStory",
|
||||
"IUserCostumeActiveSkill",
|
||||
"IUserDeckTypeNote",
|
||||
} {
|
||||
tables[table] = "[]"
|
||||
}
|
||||
return tables
|
||||
}
|
||||
|
||||
func SelectTables(all map[string]string, requested []string) map[string]string {
|
||||
selected := make(map[string]string, len(requested))
|
||||
for _, table := range requested {
|
||||
if payload, ok := all[table]; ok && payload != "" {
|
||||
selected[table] = payload
|
||||
continue
|
||||
}
|
||||
selected[table] = "[]"
|
||||
}
|
||||
return selected
|
||||
}
|
||||
|
||||
func BuildDiffFromTables(tables map[string]string) map[string]*pb.DiffData {
|
||||
diff := make(map[string]*pb.DiffData, len(tables))
|
||||
for table, payload := range tables {
|
||||
if payload == "" {
|
||||
payload = "[]"
|
||||
}
|
||||
diff[table] = &pb.DiffData{
|
||||
UpdateRecordsJson: payload,
|
||||
DeleteKeysJson: "[]",
|
||||
}
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
// BuildDiffFromTablesOrdered builds a diff map with tables in the given order.
|
||||
// Use when client applies tables in received order and order matters (e.g. IUserWeapon before IUserWeaponStory).
|
||||
// Protobuf map serialization order is implementation-defined; this at least ensures we only include
|
||||
// the requested tables in the specified sequence when building the map.
|
||||
func BuildDiffFromTablesOrdered(tables map[string]string, order []string) map[string]*pb.DiffData {
|
||||
diff := make(map[string]*pb.DiffData, len(order))
|
||||
for _, table := range order {
|
||||
payload, ok := tables[table]
|
||||
if !ok {
|
||||
payload = "[]"
|
||||
}
|
||||
if payload == "" {
|
||||
payload = "[]"
|
||||
}
|
||||
diff[table] = &pb.DiffData{
|
||||
UpdateRecordsJson: payload,
|
||||
DeleteKeysJson: "[]",
|
||||
}
|
||||
}
|
||||
return diff
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
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..2)].
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user