mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Persist Subjugation Quests triple-deck presets
This commit is contained in:
@@ -166,6 +166,10 @@ func (h *QuestHandler) applyExpRewards(user *store.UserState, questId int32, now
|
||||
return
|
||||
}
|
||||
|
||||
if questDef.CharacterExp == 0 && questDef.CostumeExp == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
deckCostumeUuids, deckCharacterIds := h.resolveDeckUnits(user, questId)
|
||||
if deckCostumeUuids == nil {
|
||||
log.Printf("[applyExpRewards] questId=%d skipping character/costume exp (deck not resolved)", questId)
|
||||
|
||||
@@ -191,11 +191,45 @@ func (s *DeckServiceServer) ReplaceTripleDeck(ctx context.Context, req *pb.Repla
|
||||
}
|
||||
store.ApplyDeckReplacement(user, model.DeckType(detail.DeckType), detail.UserDeckNumber, deckSlotsFromProto(detail.Deck), nowMillis)
|
||||
}
|
||||
|
||||
key := store.DeckKey{DeckType: model.DeckType(req.DeckType), UserDeckNumber: req.UserDeckNumber}
|
||||
td := user.TripleDecks[key]
|
||||
td.DeckType = model.DeckType(req.DeckType)
|
||||
td.UserDeckNumber = req.UserDeckNumber
|
||||
td.DeckNumber01 = innerDeckNumber(req.DeckDetail01)
|
||||
td.DeckNumber02 = innerDeckNumber(req.DeckDetail02)
|
||||
td.DeckNumber03 = innerDeckNumber(req.DeckDetail03)
|
||||
td.LatestVersion = nowMillis
|
||||
user.TripleDecks[key] = td
|
||||
})
|
||||
|
||||
return &pb.ReplaceTripleDeckResponse{}, nil
|
||||
}
|
||||
|
||||
func innerDeckNumber(d *pb.DeckDetail) int32 {
|
||||
if d == nil {
|
||||
return 0
|
||||
}
|
||||
return d.UserDeckNumber
|
||||
}
|
||||
|
||||
func (s *DeckServiceServer) UpdateTripleDeckName(ctx context.Context, req *pb.UpdateTripleDeckNameRequest) (*pb.UpdateTripleDeckNameResponse, error) {
|
||||
log.Printf("[DeckService] UpdateTripleDeckName: deckType=%d deckNumber=%d name=%q", req.DeckType, req.UserDeckNumber, req.Name)
|
||||
userId := CurrentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
key := store.DeckKey{DeckType: model.DeckType(req.DeckType), UserDeckNumber: req.UserDeckNumber}
|
||||
td := user.TripleDecks[key]
|
||||
td.DeckType = model.DeckType(req.DeckType)
|
||||
td.UserDeckNumber = req.UserDeckNumber
|
||||
td.Name = req.Name
|
||||
td.LatestVersion = gametime.NowMillis()
|
||||
user.TripleDecks[key] = td
|
||||
})
|
||||
|
||||
return &pb.UpdateTripleDeckNameResponse{}, 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)
|
||||
|
||||
@@ -14,6 +14,7 @@ func CloneUserState(u UserState) UserState {
|
||||
out.DeckSubWeapons = cloneSliceMap(u.DeckSubWeapons)
|
||||
out.DeckParts = cloneSliceMap(u.DeckParts)
|
||||
out.Decks = maps.Clone(u.Decks)
|
||||
out.TripleDecks = maps.Clone(u.TripleDecks)
|
||||
out.Quests = maps.Clone(u.Quests)
|
||||
out.QuestMissions = maps.Clone(u.QuestMissions)
|
||||
out.WeaponStories = maps.Clone(u.WeaponStories)
|
||||
|
||||
@@ -97,6 +97,7 @@ func SeedUserState(userId int64, uuid string, nowMillis int64, platform model.Cl
|
||||
Companions: make(map[string]CompanionState),
|
||||
DeckCharacters: make(map[string]DeckCharacterState),
|
||||
Decks: make(map[DeckKey]DeckState),
|
||||
TripleDecks: make(map[DeckKey]TripleDeckState),
|
||||
DeckSubWeapons: make(map[string][]string),
|
||||
DeckParts: make(map[string][]string),
|
||||
Quests: make(map[int32]UserQuestState),
|
||||
|
||||
@@ -43,6 +43,7 @@ func initMaps(u *store.UserState) {
|
||||
u.Thoughts = make(map[string]store.ThoughtState)
|
||||
u.DeckCharacters = make(map[string]store.DeckCharacterState)
|
||||
u.Decks = make(map[store.DeckKey]store.DeckState)
|
||||
u.TripleDecks = make(map[store.DeckKey]store.TripleDeckState)
|
||||
u.DeckSubWeapons = make(map[string][]string)
|
||||
u.DeckParts = make(map[string][]string)
|
||||
u.Quests = make(map[int32]store.UserQuestState)
|
||||
@@ -299,6 +300,16 @@ func loadMapTables(db *sql.DB, uid int64, u *store.UserState) {
|
||||
u.Decks[store.DeckKey{DeckType: v.DeckType, UserDeckNumber: v.UserDeckNumber}] = v
|
||||
})
|
||||
|
||||
queryRows(db, `SELECT deck_type, user_deck_number, name, deck_number01, deck_number02, deck_number03, latest_version
|
||||
FROM user_triple_decks WHERE user_id=?`, uid,
|
||||
func(rows *sql.Rows) {
|
||||
var v store.TripleDeckState
|
||||
var dt int32
|
||||
rows.Scan(&dt, &v.UserDeckNumber, &v.Name, &v.DeckNumber01, &v.DeckNumber02, &v.DeckNumber03, &v.LatestVersion)
|
||||
v.DeckType = model.DeckType(dt)
|
||||
u.TripleDecks[store.DeckKey{DeckType: v.DeckType, UserDeckNumber: v.UserDeckNumber}] = v
|
||||
})
|
||||
|
||||
queryRows(db, `SELECT user_deck_character_uuid, ordinal, user_weapon_uuid
|
||||
FROM user_deck_sub_weapons WHERE user_id=? ORDER BY user_deck_character_uuid, ordinal`, uid,
|
||||
func(rows *sql.Rows) {
|
||||
|
||||
@@ -171,6 +171,12 @@ func writeUserState(tx *sql.Tx, uid int64, u *store.UserState) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for k, v := range u.TripleDecks {
|
||||
if err := exec(`INSERT INTO user_triple_decks (user_id, deck_type, user_deck_number, name, deck_number01, deck_number02, deck_number03, latest_version) VALUES (?,?,?,?,?,?,?,?)`,
|
||||
uid, int32(k.DeckType), k.UserDeckNumber, v.Name, v.DeckNumber01, v.DeckNumber02, v.DeckNumber03, v.LatestVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for key, uuids := range u.DeckSubWeapons {
|
||||
for i, uuid := range uuids {
|
||||
if err := exec(`INSERT INTO user_deck_sub_weapons (user_id, user_deck_character_uuid, ordinal, user_weapon_uuid) VALUES (?,?,?,?)`,
|
||||
@@ -713,6 +719,18 @@ func diffAndSave(tx *sql.Tx, uid int64, before, after *store.UserState) error {
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range after.TripleDecks {
|
||||
if old, ok := before.TripleDecks[k]; !ok || old != v {
|
||||
exec(`INSERT OR REPLACE INTO user_triple_decks (user_id, deck_type, user_deck_number, name, deck_number01, deck_number02, deck_number03, latest_version) VALUES (?,?,?,?,?,?,?,?)`,
|
||||
uid, int32(k.DeckType), k.UserDeckNumber, v.Name, v.DeckNumber01, v.DeckNumber02, v.DeckNumber03, v.LatestVersion)
|
||||
}
|
||||
}
|
||||
for k := range before.TripleDecks {
|
||||
if _, ok := after.TripleDecks[k]; !ok {
|
||||
exec(`DELETE FROM user_triple_decks WHERE user_id=? AND deck_type=? AND user_deck_number=?`, uid, int32(k.DeckType), k.UserDeckNumber)
|
||||
}
|
||||
}
|
||||
|
||||
replaceSliceTable(tx, uid, "user_deck_sub_weapons", after.DeckSubWeapons, func(key string, uuids []string) {
|
||||
for i, uuid := range uuids {
|
||||
exec(`INSERT INTO user_deck_sub_weapons (user_id, user_deck_character_uuid, ordinal, user_weapon_uuid) VALUES (?,?,?,?)`, uid, key, i, uuid)
|
||||
|
||||
@@ -70,6 +70,7 @@ type UserState struct {
|
||||
Thoughts map[string]ThoughtState
|
||||
DeckCharacters map[string]DeckCharacterState
|
||||
Decks map[DeckKey]DeckState
|
||||
TripleDecks map[DeckKey]TripleDeckState
|
||||
Quests map[int32]UserQuestState
|
||||
QuestMissions map[QuestMissionKey]UserQuestMissionState
|
||||
Missions map[int32]UserMissionState
|
||||
@@ -142,6 +143,9 @@ func (u *UserState) EnsureMaps() {
|
||||
if u.Decks == nil {
|
||||
u.Decks = make(map[DeckKey]DeckState)
|
||||
}
|
||||
if u.TripleDecks == nil {
|
||||
u.TripleDecks = make(map[DeckKey]TripleDeckState)
|
||||
}
|
||||
if u.DeckSubWeapons == nil {
|
||||
u.DeckSubWeapons = make(map[string][]string)
|
||||
}
|
||||
@@ -455,6 +459,16 @@ type DeckState struct {
|
||||
LatestVersion int64
|
||||
}
|
||||
|
||||
type TripleDeckState struct {
|
||||
DeckType model.DeckType
|
||||
UserDeckNumber int32
|
||||
Name string
|
||||
DeckNumber01 int32
|
||||
DeckNumber02 int32
|
||||
DeckNumber03 int32
|
||||
LatestVersion int64
|
||||
}
|
||||
|
||||
type DeckCharacterInput struct {
|
||||
UserCostumeUuid string
|
||||
MainUserWeaponUuid string
|
||||
|
||||
@@ -188,6 +188,9 @@ func ChangedTables(before, after *store.UserState) []string {
|
||||
if !mapsEqualStruct(before.Decks, after.Decks) {
|
||||
add("IUserDeck")
|
||||
}
|
||||
if !mapsEqualStruct(before.TripleDecks, after.TripleDecks) {
|
||||
add("IUserTripleDeck")
|
||||
}
|
||||
if !mapsEqualSliceValues(before.DeckSubWeapons, after.DeckSubWeapons) {
|
||||
add("IUserDeckSubWeaponGroup")
|
||||
}
|
||||
@@ -358,6 +361,8 @@ func keyFieldsForTable(table string) []string {
|
||||
return []string{"userId", "userDeckCharacterUuid"}
|
||||
case "IUserDeck":
|
||||
return []string{"userId", "deckType", "userDeckNumber"}
|
||||
case "IUserTripleDeck":
|
||||
return []string{"userId", "deckType", "userDeckNumber"}
|
||||
case "IUserDeckSubWeaponGroup":
|
||||
return []string{"userId", "userDeckCharacterUuid", "sortOrder"}
|
||||
case "IUserDeckPartsGroup":
|
||||
|
||||
@@ -13,6 +13,10 @@ func init() {
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserTripleDeck", func(user store.UserState) string {
|
||||
s, _ := utils.EncodeJSONMaps(sortedTripleDeckRecords(user)...)
|
||||
return s
|
||||
})
|
||||
register("IUserDeckCharacter", func(user store.UserState) string {
|
||||
s, _ := utils.EncodeJSONMaps(sortedDeckCharacterRecords(user)...)
|
||||
return s
|
||||
@@ -68,6 +72,35 @@ func sortedDeckRecords(user store.UserState) []map[string]any {
|
||||
return records
|
||||
}
|
||||
|
||||
func sortedTripleDeckRecords(user store.UserState) []map[string]any {
|
||||
keys := make([]store.DeckKey, 0, len(user.TripleDecks))
|
||||
for key := range user.TripleDecks {
|
||||
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.TripleDecks[key]
|
||||
records = append(records, map[string]any{
|
||||
"userId": user.UserId,
|
||||
"deckType": int32(row.DeckType),
|
||||
"userDeckNumber": row.UserDeckNumber,
|
||||
"name": row.Name,
|
||||
"deckNumber01": row.DeckNumber01,
|
||||
"deckNumber02": row.DeckNumber02,
|
||||
"deckNumber03": row.DeckNumber03,
|
||||
"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))
|
||||
|
||||
@@ -21,6 +21,7 @@ func FullClientTableMap(user store.UserState) map[string]string {
|
||||
"IUserThought": projectTable("IUserThought", user),
|
||||
"IUserDeckCharacter": projectTable("IUserDeckCharacter", user),
|
||||
"IUserDeck": projectTable("IUserDeck", user),
|
||||
"IUserTripleDeck": projectTable("IUserTripleDeck", user),
|
||||
"IUserLogin": projectTable("IUserLogin", user),
|
||||
"IUserLoginBonus": projectTable("IUserLoginBonus", user),
|
||||
"IUserMission": projectTable("IUserMission", user),
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE user_triple_decks (
|
||||
user_id INTEGER NOT NULL REFERENCES users(user_id),
|
||||
deck_type INTEGER NOT NULL,
|
||||
user_deck_number INTEGER NOT NULL,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
deck_number01 INTEGER NOT NULL DEFAULT 0,
|
||||
deck_number02 INTEGER NOT NULL DEFAULT 0,
|
||||
deck_number03 INTEGER NOT NULL DEFAULT 0,
|
||||
latest_version INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (user_id, deck_type, user_deck_number)
|
||||
);
|
||||
|
||||
-- Legacy BigHunt wave-decks have no preset wrapper rows and aren't reachable
|
||||
-- via the new IUserTripleDeck projection. Drop them so users start clean and
|
||||
-- repopulate via ReplaceTripleDeck from the client.
|
||||
DELETE FROM user_decks WHERE deck_type = 5;
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS user_triple_decks;
|
||||
Reference in New Issue
Block a user