From 7828de031c7e82576c860bb1fd120f9a3d2d175b Mon Sep 17 00:00:00 2001 From: Ilya Groshev Date: Wed, 22 Apr 2026 08:59:10 +0300 Subject: [PATCH] Fx equipment duplication and add CopyDeck/RemoveDeck --- server/cmd/dev/main.go | 2 + server/internal/service/deck.go | 40 +++++++++++++++ server/internal/store/helpers.go | 85 ++++++++++++++++++++++++++------ 3 files changed, 111 insertions(+), 16 deletions(-) diff --git a/server/cmd/dev/main.go b/server/cmd/dev/main.go index 707d5b4..8168230 100644 --- a/server/cmd/dev/main.go +++ b/server/cmd/dev/main.go @@ -40,6 +40,7 @@ func main() { // lunar-tear (grpc) flags grpcListen := flag.String("grpc.listen", "0.0.0.0:8003", "lunar-tear gRPC listen address (host:port)") grpcPublicAddr := flag.String("grpc.public-addr", "10.0.2.2:8003", "lunar-tear externally-reachable address") + grpcDB := flag.String("grpc.db", "db/game.db", "lunar-tear SQLite database path") grpcOctoURL := flag.String("grpc.octo-url", "", "Octo CDN base URL passed to lunar-tear (default: derived from cdn.public-addr)") grpcAuthURL := flag.String("grpc.auth-url", "", "auth server base URL passed to lunar-tear (default: derived from auth.listen)") @@ -87,6 +88,7 @@ func main() { cmd: exec.CommandContext(ctx, "go", "run", "./cmd/lunar-tear", "--listen", *grpcListen, "--public-addr", *grpcPublicAddr, + "--db", *grpcDB, "--octo-url", *grpcOctoURL, "--auth-url", *grpcAuthURL, ), diff --git a/server/internal/service/deck.go b/server/internal/service/deck.go index 3ff3dc2..8187019 100644 --- a/server/internal/service/deck.go +++ b/server/internal/service/deck.go @@ -213,3 +213,43 @@ func (s *DeckServiceServer) ReplaceMultiDeck(ctx context.Context, req *pb.Replac return &pb.ReplaceMultiDeckResponse{}, nil } + +func (s *DeckServiceServer) RemoveDeck(ctx context.Context, req *pb.RemoveDeckRequest) (*pb.RemoveDeckResponse, error) { + log.Printf("[DeckService] RemoveDeck: deckType=%d deckNumber=%d", req.DeckType, req.UserDeckNumber) + userId := CurrentUserId(ctx, s.users, s.sessions) + + s.users.UpdateUser(userId, func(user *store.UserState) { + store.RemoveDeckData(user, model.DeckType(req.DeckType), req.UserDeckNumber) + }) + + return &pb.RemoveDeckResponse{}, nil +} + +func (s *DeckServiceServer) CopyDeck(ctx context.Context, req *pb.CopyDeckRequest) (*pb.CopyDeckResponse, error) { + log.Printf("[DeckService] CopyDeck: from deckType=%d deckNumber=%d -> to deckType=%d deckNumber=%d", + req.FromDeckType, req.FromUserDeckNumber, req.ToDeckType, req.ToUserDeckNumber) + userId := CurrentUserId(ctx, s.users, s.sessions) + + var resultType int32 + s.users.UpdateUser(userId, func(user *store.UserState) { + slots := store.ReadDeckSlots(user, model.DeckType(req.FromDeckType), req.FromUserDeckNumber) + if slots == nil { + return + } + + nowMillis := gametime.NowMillis() + fromKey := store.DeckKey{DeckType: model.DeckType(req.FromDeckType), UserDeckNumber: req.FromUserDeckNumber} + srcName := user.Decks[fromKey].Name + + store.ApplyDeckReplacement(user, model.DeckType(req.ToDeckType), req.ToUserDeckNumber, slots, nowMillis) + + toKey := store.DeckKey{DeckType: model.DeckType(req.ToDeckType), UserDeckNumber: req.ToUserDeckNumber} + deck := user.Decks[toKey] + deck.Name = srcName + user.Decks[toKey] = deck + + resultType = 1 + }) + + return &pb.CopyDeckResponse{ResultType: resultType}, nil +} diff --git a/server/internal/store/helpers.go b/server/internal/store/helpers.go index 1b12ef2..eb49abf 100644 --- a/server/internal/store/helpers.go +++ b/server/internal/store/helpers.go @@ -330,31 +330,84 @@ func ApplyDeckReplacement(user *UserState, deckType model.DeckType, userDeckNumb deck.Power = 100 } - uuidPtrs := []*string{&deck.UserDeckCharacterUuid01, &deck.UserDeckCharacterUuid02, &deck.UserDeckCharacterUuid03} - for i, uuidPtr := range uuidPtrs { + for _, oldUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} { + if oldUuid == "" { + continue + } + delete(user.DeckCharacters, oldUuid) + delete(user.DeckSubWeapons, oldUuid) + delete(user.DeckParts, oldUuid) + } + + var newUuids [3]string + for i := range 3 { if i >= len(slots) || slots[i].UserCostumeUuid == "" { - *uuidPtr = "" continue } slot := slots[i] - dcUuid := *uuidPtr - if dcUuid == "" { - dcUuid = uuid.New().String() + dcUuid := uuid.New().String() + user.DeckCharacters[dcUuid] = DeckCharacterState{ + UserDeckCharacterUuid: dcUuid, + UserCostumeUuid: slot.UserCostumeUuid, + MainUserWeaponUuid: slot.MainUserWeaponUuid, + UserCompanionUuid: slot.UserCompanionUuid, + UserThoughtUuid: slot.UserThoughtUuid, + DressupCostumeId: slot.DressupCostumeId, + LatestVersion: nowMillis, } - dc := user.DeckCharacters[dcUuid] - dc.UserDeckCharacterUuid = dcUuid - dc.UserCostumeUuid = slot.UserCostumeUuid - dc.MainUserWeaponUuid = slot.MainUserWeaponUuid - dc.UserCompanionUuid = slot.UserCompanionUuid - dc.UserThoughtUuid = slot.UserThoughtUuid - dc.DressupCostumeId = slot.DressupCostumeId - dc.LatestVersion = nowMillis - user.DeckCharacters[dcUuid] = dc user.DeckSubWeapons[dcUuid] = slot.SubWeaponUuids user.DeckParts[dcUuid] = slot.PartsUuids - *uuidPtr = dcUuid + newUuids[i] = dcUuid } + deck.UserDeckCharacterUuid01 = newUuids[0] + deck.UserDeckCharacterUuid02 = newUuids[1] + deck.UserDeckCharacterUuid03 = newUuids[2] deck.LatestVersion = nowMillis user.Decks[deckKey] = deck } + +func RemoveDeckData(user *UserState, deckType model.DeckType, userDeckNumber int32) { + deckKey := DeckKey{DeckType: deckType, UserDeckNumber: userDeckNumber} + deck, ok := user.Decks[deckKey] + if !ok { + return + } + for _, dcUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} { + if dcUuid == "" { + continue + } + delete(user.DeckCharacters, dcUuid) + delete(user.DeckSubWeapons, dcUuid) + delete(user.DeckParts, dcUuid) + } + delete(user.Decks, deckKey) +} + +func ReadDeckSlots(user *UserState, deckType model.DeckType, userDeckNumber int32) []DeckCharacterInput { + deckKey := DeckKey{DeckType: deckType, UserDeckNumber: userDeckNumber} + deck, ok := user.Decks[deckKey] + if !ok { + return nil + } + slots := make([]DeckCharacterInput, 3) + for i, dcUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} { + if dcUuid == "" { + continue + } + dc, ok := user.DeckCharacters[dcUuid] + if !ok { + continue + } + slots[i] = DeckCharacterInput{ + UserCostumeUuid: dc.UserCostumeUuid, + MainUserWeaponUuid: dc.MainUserWeaponUuid, + SubWeaponUuids: user.DeckSubWeapons[dcUuid], + PartsUuids: user.DeckParts[dcUuid], + UserCompanionUuid: dc.UserCompanionUuid, + UserThoughtUuid: dc.UserThoughtUuid, + DressupCostumeId: dc.DressupCostumeId, + } + } + return slots +}