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,196 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/store"
|
||||
"lunar-tear/server/internal/userdata"
|
||||
)
|
||||
|
||||
const partsMaxLevel = int32(15)
|
||||
|
||||
var partsDiffTables = []string{
|
||||
"IUserParts",
|
||||
"IUserConsumableItem",
|
||||
}
|
||||
|
||||
type PartsServiceServer struct {
|
||||
pb.UnimplementedPartsServiceServer
|
||||
users store.UserRepository
|
||||
sessions store.SessionRepository
|
||||
catalog *masterdata.PartsCatalog
|
||||
config *masterdata.GameConfig
|
||||
}
|
||||
|
||||
func NewPartsServiceServer(users store.UserRepository, sessions store.SessionRepository, catalog *masterdata.PartsCatalog, config *masterdata.GameConfig) *PartsServiceServer {
|
||||
return &PartsServiceServer{users: users, sessions: sessions, catalog: catalog, config: config}
|
||||
}
|
||||
|
||||
func (s *PartsServiceServer) Sell(ctx context.Context, req *pb.PartsSellRequest) (*pb.PartsSellResponse, error) {
|
||||
log.Printf("[PartsService] Sell: %d part(s)", len(req.UserPartsUuid))
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
|
||||
oldUser, _ := s.users.SnapshotUser(userId)
|
||||
tracker := userdata.NewDeleteTracker().
|
||||
Track("IUserParts", oldUser, userdata.SortedPartsRecords, []string{"userId", "userPartsUuid"})
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
totalGold := int32(0)
|
||||
for _, uuid := range req.UserPartsUuid {
|
||||
part, ok := user.Parts[uuid]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Sell: uuid=%s not found, skipping", uuid)
|
||||
continue
|
||||
}
|
||||
if part.IsProtected {
|
||||
log.Printf("[PartsService] Sell: uuid=%s is protected, skipping", uuid)
|
||||
continue
|
||||
}
|
||||
|
||||
partDef, ok := s.catalog.PartsById[part.PartsId]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Sell: partsId=%d not in catalog, skipping", part.PartsId)
|
||||
continue
|
||||
}
|
||||
|
||||
sellFunc, ok := s.catalog.SellPriceByRarity[partDef.RarityType]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Sell: no sell price func for rarity=%d, skipping", partDef.RarityType)
|
||||
continue
|
||||
}
|
||||
|
||||
gold := sellFunc.Evaluate(part.Level)
|
||||
totalGold += gold
|
||||
delete(user.Parts, uuid)
|
||||
log.Printf("[PartsService] Sell: uuid=%s partsId=%d level=%d -> %d gold", uuid, part.PartsId, part.Level, gold)
|
||||
}
|
||||
|
||||
if totalGold > 0 {
|
||||
user.ConsumableItems[s.config.ConsumableItemIdForGold] += totalGold
|
||||
log.Printf("[PartsService] Sell: total gold +%d", totalGold)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parts sell: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.SelectTables(userdata.FullClientTableMap(snapshot), partsDiffTables)
|
||||
diff := tracker.Apply(snapshot, tables)
|
||||
|
||||
return &pb.PartsSellResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *PartsServiceServer) Enhance(ctx context.Context, req *pb.PartsEnhanceRequest) (*pb.PartsEnhanceResponse, error) {
|
||||
log.Printf("[PartsService] Enhance: uuid=%s", req.UserPartsUuid)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
isSuccess := false
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
part, ok := user.Parts[req.UserPartsUuid]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Enhance: part uuid=%s not found", req.UserPartsUuid)
|
||||
return
|
||||
}
|
||||
|
||||
if part.Level >= partsMaxLevel {
|
||||
log.Printf("[PartsService] Enhance: part uuid=%s already at max level %d", req.UserPartsUuid, part.Level)
|
||||
return
|
||||
}
|
||||
|
||||
partDef, ok := s.catalog.PartsById[part.PartsId]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Enhance: part master id=%d not found", part.PartsId)
|
||||
return
|
||||
}
|
||||
|
||||
rarity, ok := s.catalog.RarityByRarityType[partDef.RarityType]
|
||||
if !ok {
|
||||
log.Printf("[PartsService] Enhance: rarity type=%d not found", partDef.RarityType)
|
||||
return
|
||||
}
|
||||
|
||||
goldCost := int32(0)
|
||||
if prices, ok := s.catalog.PriceByGroupAndLevel[rarity.PartsLevelUpPriceGroupId]; ok {
|
||||
goldCost = prices[part.Level]
|
||||
}
|
||||
|
||||
currentGold := user.ConsumableItems[s.config.ConsumableItemIdForGold]
|
||||
if currentGold < goldCost {
|
||||
log.Printf("[PartsService] Enhance: insufficient gold have=%d need=%d", currentGold, goldCost)
|
||||
return
|
||||
}
|
||||
|
||||
user.ConsumableItems[s.config.ConsumableItemIdForGold] -= goldCost
|
||||
|
||||
successRate := int32(1000)
|
||||
if rates, ok := s.catalog.RateByGroupAndLevel[rarity.PartsLevelUpRateGroupId]; ok {
|
||||
if r, ok := rates[part.Level]; ok {
|
||||
successRate = r
|
||||
}
|
||||
}
|
||||
|
||||
if rand.Intn(1000) < int(successRate) {
|
||||
part.Level++
|
||||
isSuccess = true
|
||||
log.Printf("[PartsService] Enhance: SUCCESS partsId=%d level %d -> %d (rate=%d‰, cost=%d gold)",
|
||||
part.PartsId, part.Level-1, part.Level, successRate, goldCost)
|
||||
} else {
|
||||
log.Printf("[PartsService] Enhance: FAIL partsId=%d stays level %d (rate=%d‰, cost=%d gold)",
|
||||
part.PartsId, part.Level, successRate, goldCost)
|
||||
}
|
||||
|
||||
part.LatestVersion = nowMillis
|
||||
user.Parts[req.UserPartsUuid] = part
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parts enhance: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.FullClientTableMap(snapshot)
|
||||
diff := userdata.BuildDiffFromTables(userdata.SelectTables(tables, partsDiffTables))
|
||||
|
||||
return &pb.PartsEnhanceResponse{
|
||||
IsSuccess: isSuccess,
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *PartsServiceServer) ReplacePreset(ctx context.Context, req *pb.PartsReplacePresetRequest) (*pb.PartsReplacePresetResponse, error) {
|
||||
log.Printf("[PartsService] ReplacePreset: preset=%d uuids=[%s, %s, %s]",
|
||||
req.UserPartsPresetNumber, req.UserPartsUuid01, req.UserPartsUuid02, req.UserPartsUuid03)
|
||||
|
||||
userId := currentUserId(ctx, s.users, s.sessions)
|
||||
nowMillis := gametime.NowMillis()
|
||||
|
||||
snapshot, err := s.users.UpdateUser(userId, func(user *store.UserState) {
|
||||
preset := user.PartsPresets[req.UserPartsPresetNumber]
|
||||
preset.UserPartsPresetNumber = req.UserPartsPresetNumber
|
||||
preset.UserPartsUuid01 = req.UserPartsUuid01
|
||||
preset.UserPartsUuid02 = req.UserPartsUuid02
|
||||
preset.UserPartsUuid03 = req.UserPartsUuid03
|
||||
preset.LatestVersion = nowMillis
|
||||
user.PartsPresets[req.UserPartsPresetNumber] = preset
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parts replace preset: %w", err)
|
||||
}
|
||||
|
||||
tables := userdata.FullClientTableMap(snapshot)
|
||||
diff := userdata.BuildDiffFromTables(userdata.SelectTables(tables, []string{"IUserPartsPreset"}))
|
||||
|
||||
return &pb.PartsReplacePresetResponse{
|
||||
DiffUserData: diff,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user