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,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
pb "lunar-tear/server/gen/proto"
|
||||
"lunar-tear/server/internal/gacha"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/service"
|
||||
"lunar-tear/server/internal/store/memory"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type loggingListener struct {
|
||||
net.Listener
|
||||
}
|
||||
|
||||
func (l loggingListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("[gRPC] Accept error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("[gRPC] New connection from %v", conn.RemoteAddr())
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func startGRPC(
|
||||
host string,
|
||||
octoURL string,
|
||||
userStore *memory.MemoryStore,
|
||||
questEngine *questflow.QuestHandler,
|
||||
gachaHandler *gacha.GachaHandler,
|
||||
cageOrnamentCatalog *masterdata.CageOrnamentCatalog,
|
||||
loginBonusCatalog *masterdata.LoginBonusCatalog,
|
||||
characterViewerCatalog *masterdata.CharacterViewerCatalog,
|
||||
shopCatalog *masterdata.ShopCatalog,
|
||||
costumeCatalog *masterdata.CostumeCatalog,
|
||||
omikujiCatalog *masterdata.OmikujiCatalog,
|
||||
weaponCatalog *masterdata.WeaponCatalog,
|
||||
exploreCatalog *masterdata.ExploreCatalog,
|
||||
gimmickCatalog *masterdata.GimmickCatalog,
|
||||
characterBoardCatalog *masterdata.CharacterBoardCatalog,
|
||||
partsCatalog *masterdata.PartsCatalog,
|
||||
characterRebirthCatalog *masterdata.CharacterRebirthCatalog,
|
||||
companionCatalog *masterdata.CompanionCatalog,
|
||||
materialCatalog *masterdata.MaterialCatalog,
|
||||
gameConfig *masterdata.GameConfig,
|
||||
sideStoryCatalog *masterdata.SideStoryCatalog,
|
||||
bigHuntCatalog *masterdata.BigHuntCatalog,
|
||||
) {
|
||||
lis, err := net.Listen("tcp", ":443")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen on :443: %v", err)
|
||||
}
|
||||
lis = loggingListener{Listener: lis}
|
||||
|
||||
grpcServer := grpc.NewServer(
|
||||
grpc.ChainUnaryInterceptor(loggingInterceptor, timeSyncInterceptor),
|
||||
grpc.UnknownServiceHandler(loggingUnknownService),
|
||||
)
|
||||
|
||||
registerServices(grpcServer,
|
||||
host,
|
||||
octoURL,
|
||||
userStore,
|
||||
questEngine,
|
||||
gachaHandler,
|
||||
cageOrnamentCatalog,
|
||||
loginBonusCatalog,
|
||||
characterViewerCatalog,
|
||||
shopCatalog,
|
||||
costumeCatalog,
|
||||
omikujiCatalog,
|
||||
weaponCatalog,
|
||||
exploreCatalog,
|
||||
gimmickCatalog,
|
||||
characterBoardCatalog,
|
||||
partsCatalog,
|
||||
characterRebirthCatalog,
|
||||
companionCatalog,
|
||||
materialCatalog,
|
||||
gameConfig,
|
||||
sideStoryCatalog,
|
||||
bigHuntCatalog,
|
||||
)
|
||||
|
||||
reflection.Register(grpcServer)
|
||||
|
||||
log.Printf("gRPC server listening on :443")
|
||||
log.Printf("client host address: %s:443", host)
|
||||
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func registerServices(
|
||||
srv *grpc.Server,
|
||||
host string,
|
||||
octoURL string,
|
||||
userStore *memory.MemoryStore,
|
||||
questEngine *questflow.QuestHandler,
|
||||
gachaHandler *gacha.GachaHandler,
|
||||
cageOrnamentCatalog *masterdata.CageOrnamentCatalog,
|
||||
loginBonusCatalog *masterdata.LoginBonusCatalog,
|
||||
characterViewerCatalog *masterdata.CharacterViewerCatalog,
|
||||
shopCatalog *masterdata.ShopCatalog,
|
||||
costumeCatalog *masterdata.CostumeCatalog,
|
||||
omikujiCatalog *masterdata.OmikujiCatalog,
|
||||
weaponCatalog *masterdata.WeaponCatalog,
|
||||
exploreCatalog *masterdata.ExploreCatalog,
|
||||
gimmickCatalog *masterdata.GimmickCatalog,
|
||||
characterBoardCatalog *masterdata.CharacterBoardCatalog,
|
||||
partsCatalog *masterdata.PartsCatalog,
|
||||
characterRebirthCatalog *masterdata.CharacterRebirthCatalog,
|
||||
companionCatalog *masterdata.CompanionCatalog,
|
||||
materialCatalog *masterdata.MaterialCatalog,
|
||||
gameConfig *masterdata.GameConfig,
|
||||
sideStoryCatalog *masterdata.SideStoryCatalog,
|
||||
bigHuntCatalog *masterdata.BigHuntCatalog,
|
||||
) {
|
||||
pb.RegisterBannerServiceServer(srv, service.NewBannerServiceServer(userStore))
|
||||
pb.RegisterUserServiceServer(srv, service.NewUserServiceServer(userStore, userStore))
|
||||
pb.RegisterBattleServiceServer(srv, service.NewBattleServiceServer(userStore, userStore))
|
||||
pb.RegisterConfigServiceServer(srv, service.NewConfigServiceServer(host, int32(443), octoURL))
|
||||
pb.RegisterDataServiceServer(srv, service.NewDataServiceServer(userStore, userStore))
|
||||
pb.RegisterTutorialServiceServer(srv, service.NewTutorialServiceServer(userStore, userStore, questEngine))
|
||||
pb.RegisterGachaServiceServer(srv, service.NewGachaServiceServer(userStore, userStore, userStore, gachaHandler))
|
||||
pb.RegisterGiftServiceServer(srv, service.NewGiftServiceServer(userStore, userStore))
|
||||
pb.RegisterGamePlayServiceServer(srv, service.NewGameplayServiceServer())
|
||||
pb.RegisterGimmickServiceServer(srv, service.NewGimmickServiceServer(userStore, userStore, gimmickCatalog))
|
||||
pb.RegisterQuestServiceServer(srv, service.NewQuestServiceServer(userStore, userStore, questEngine))
|
||||
pb.RegisterNotificationServiceServer(srv, service.NewNotificationServiceServer(userStore, userStore))
|
||||
pb.RegisterCageOrnamentServiceServer(srv, service.NewCageOrnamentServiceServer(userStore, userStore, cageOrnamentCatalog, questEngine.Granter))
|
||||
pb.RegisterDeckServiceServer(srv, service.NewDeckServiceServer(userStore, userStore))
|
||||
pb.RegisterFriendServiceServer(srv, service.NewFriendServiceServer(userStore, userStore))
|
||||
pb.RegisterLoginBonusServiceServer(srv, service.NewLoginBonusServiceServer(userStore, userStore, loginBonusCatalog))
|
||||
pb.RegisterNaviCutInServiceServer(srv, service.NewNaviCutInServiceServer(userStore, userStore))
|
||||
pb.RegisterContentsStoryServiceServer(srv, service.NewContentsStoryServiceServer(userStore, userStore))
|
||||
pb.RegisterDokanServiceServer(srv, service.NewDokanServiceServer(userStore, userStore))
|
||||
pb.RegisterPortalCageServiceServer(srv, service.NewPortalCageServiceServer(userStore, userStore))
|
||||
pb.RegisterCharacterViewerServiceServer(srv, service.NewCharacterViewerServiceServer(userStore, userStore, characterViewerCatalog))
|
||||
pb.RegisterMissionServiceServer(srv, service.NewMissionServiceServer(userStore, userStore))
|
||||
pb.RegisterShopServiceServer(srv, service.NewShopServiceServer(userStore, userStore, shopCatalog, questEngine.Granter))
|
||||
pb.RegisterCostumeServiceServer(srv, service.NewCostumeServiceServer(userStore, userStore, costumeCatalog, gameConfig))
|
||||
pb.RegisterMovieServiceServer(srv, service.NewMovieServiceServer(userStore, userStore))
|
||||
pb.RegisterOmikujiServiceServer(srv, service.NewOmikujiServiceServer(userStore, userStore, omikujiCatalog))
|
||||
pb.RegisterWeaponServiceServer(srv, service.NewWeaponServiceServer(userStore, userStore, weaponCatalog, gameConfig))
|
||||
pb.RegisterExploreServiceServer(srv, service.NewExploreServiceServer(userStore, userStore, exploreCatalog))
|
||||
pb.RegisterCharacterBoardServiceServer(srv, service.NewCharacterBoardServiceServer(userStore, userStore, characterBoardCatalog))
|
||||
pb.RegisterPartsServiceServer(srv, service.NewPartsServiceServer(userStore, userStore, partsCatalog, gameConfig))
|
||||
pb.RegisterCharacterServiceServer(srv, service.NewCharacterServiceServer(userStore, userStore, characterRebirthCatalog, gameConfig))
|
||||
pb.RegisterCompanionServiceServer(srv, service.NewCompanionServiceServer(userStore, userStore, companionCatalog, gameConfig))
|
||||
pb.RegisterMaterialServiceServer(srv, service.NewMaterialServiceServer(userStore, userStore, materialCatalog, gameConfig))
|
||||
pb.RegisterSideStoryQuestServiceServer(srv, service.NewSideStoryQuestServiceServer(userStore, userStore, sideStoryCatalog))
|
||||
pb.RegisterBigHuntServiceServer(srv, service.NewBigHuntServiceServer(userStore, userStore, bigHuntCatalog, questEngine))
|
||||
pb.RegisterRewardServiceServer(srv, service.NewRewardServiceServer(userStore, userStore, bigHuntCatalog, questEngine.Granter))
|
||||
}
|
||||
|
||||
func loggingInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
log.Printf(">>> %s", info.FullMethod)
|
||||
resp, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
log.Printf("<<< %s ERROR: %v", info.FullMethod, err)
|
||||
} else {
|
||||
log.Printf("<<< %s OK", info.FullMethod)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func timeSyncInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
resp, err := handler(ctx, req)
|
||||
switch info.FullMethod {
|
||||
case "/apb.api.user.UserService/Auth",
|
||||
"/apb.api.user.UserService/RegisterUser",
|
||||
"/apb.api.user.UserService/TransferUser":
|
||||
default:
|
||||
grpc.SetTrailer(ctx, metadata.Pairs(
|
||||
"x-apb-response-datetime", fmt.Sprintf("%d", gametime.NowMillis()),
|
||||
))
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func loggingUnknownService(_ any, stream grpc.ServerStream) error {
|
||||
fullMethod, ok := grpc.MethodFromServerStream(stream)
|
||||
if !ok {
|
||||
fullMethod = "<unknown>"
|
||||
}
|
||||
log.Printf(">>> %s", fullMethod)
|
||||
err := status.Errorf(codes.Unimplemented, "unknown service or method %s", fullMethod)
|
||||
log.Printf("<<< %s ERROR: %v", fullMethod, err)
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"lunar-tear/server/internal/service"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
func startHTTP(port int, resourcesBaseURL string) {
|
||||
octoServer := service.NewOctoHTTPServer(resourcesBaseURL)
|
||||
h2s := &http2.Server{}
|
||||
octoHandler := h2c.NewHandler(octoServer.Handler(), h2s)
|
||||
log.Printf("Octo HTTP server listening on :%d (HTTP/1.1 + h2c)", port)
|
||||
srv := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: octoHandler}
|
||||
http2.ConfigureServer(srv, h2s)
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
log.Fatalf("HTTP server on %d failed: %v", port, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"lunar-tear/server/internal/gacha"
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/questflow"
|
||||
"lunar-tear/server/internal/store/memory"
|
||||
)
|
||||
|
||||
func main() {
|
||||
httpPort := flag.Int("http-port", 8080, "HTTP server port (Octo API)")
|
||||
host := flag.String("host", "127.0.0.1", "hostname the client will connect to")
|
||||
scene := flag.Int("scene", 0, "Bootstrap to scene N (0 = fresh start)")
|
||||
starterItems := flag.Bool("starter-items", false, "Grant starter items to new users")
|
||||
flag.Parse()
|
||||
|
||||
octoURL := "http://" + *host + ":" + strconv.Itoa(*httpPort)
|
||||
prefix := octoURL + "/"
|
||||
padLen := 43 - len(prefix)
|
||||
resourcesBaseURL := ""
|
||||
if padLen < 1 {
|
||||
log.Printf("[config] host:port too long for 43-char resource URL; list.bin will be served unchanged")
|
||||
} else {
|
||||
resourcesBaseURL = prefix + strings.Repeat("r", padLen)
|
||||
}
|
||||
|
||||
go startHTTP(*httpPort, resourcesBaseURL)
|
||||
|
||||
snapshotDir := "snapshots"
|
||||
if err := os.MkdirAll(snapshotDir, 0755); err != nil {
|
||||
log.Fatalf("create snapshot dir: %v", err)
|
||||
}
|
||||
|
||||
gameConfig, err := masterdata.LoadGameConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("load game config: %v", err)
|
||||
}
|
||||
log.Printf("game config loaded (goldId=%d, skipTicketId=%d, rebirthGold=%d)",
|
||||
gameConfig.ConsumableItemIdForGold, gameConfig.ConsumableItemIdForQuestSkipTicket, gameConfig.CharacterRebirthConsumeGold)
|
||||
|
||||
partsCatalog, err := masterdata.LoadPartsCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load parts catalog: %v", err)
|
||||
}
|
||||
log.Printf("parts catalog loaded: %d parts, %d rarities", len(partsCatalog.PartsById), len(partsCatalog.RarityByRarityType))
|
||||
|
||||
questCatalog, err := masterdata.LoadQuestCatalog(partsCatalog)
|
||||
if err != nil {
|
||||
log.Fatalf("load quest catalog: %v", err)
|
||||
}
|
||||
questHandler := questflow.NewQuestHandler(questCatalog, gameConfig)
|
||||
userStore := memory.New(gametime.Now,
|
||||
memory.WithSnapshotDir(snapshotDir),
|
||||
memory.WithSceneId(int32(*scene)),
|
||||
memory.WithStarterItems(*starterItems),
|
||||
)
|
||||
if *scene != 0 {
|
||||
log.Printf("bootstrap scene: %d (from snapshot)", *scene)
|
||||
}
|
||||
|
||||
gachaEntries, medalInfo, err := masterdata.LoadGachaCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load gacha catalog: %v", err)
|
||||
}
|
||||
log.Printf("gacha catalog loaded: %d entries", len(gachaEntries))
|
||||
|
||||
gachaPool, err := masterdata.LoadGachaPool()
|
||||
if err != nil {
|
||||
log.Fatalf("load gacha pool: %v", err)
|
||||
}
|
||||
log.Printf("gacha pool loaded: costumes=%d rarities, weapons=%d rarities, materials=%d",
|
||||
len(gachaPool.CostumesByRarity), len(gachaPool.WeaponsByRarity), len(gachaPool.Materials))
|
||||
|
||||
shopCatalog, err := masterdata.LoadShopCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load shop catalog: %v", err)
|
||||
}
|
||||
log.Printf("shop catalog loaded: %d items, %d content groups, %d exchange shops",
|
||||
len(shopCatalog.Items), len(shopCatalog.Contents), len(shopCatalog.ExchangeShopCells))
|
||||
|
||||
gachaPool.BuildShopFeatured(shopCatalog)
|
||||
gachaPool.PruneUnpairedCostumes()
|
||||
gachaPool.BuildFeaturedMapping(gachaEntries)
|
||||
gachaPool.BuildBannerPools(gachaEntries)
|
||||
masterdata.EnrichCatalogPromotions(gachaEntries, gachaPool)
|
||||
userStore.ReplaceCatalog(gachaEntries)
|
||||
|
||||
dupExchange, err := masterdata.LoadDupExchange()
|
||||
if err != nil {
|
||||
log.Fatalf("load dup exchange: %v", err)
|
||||
}
|
||||
dupAdded, err := masterdata.EnrichDupExchange(dupExchange, gachaPool)
|
||||
if err != nil {
|
||||
log.Fatalf("enrich dup exchange: %v", err)
|
||||
}
|
||||
log.Printf("dup exchange loaded: %d entries (%d derived from limit-break materials)", len(dupExchange), dupAdded)
|
||||
|
||||
gachaHandler := gacha.NewGachaHandler(gachaPool, gameConfig, questHandler.Granter, medalInfo, dupExchange)
|
||||
|
||||
conditionResolver, err := masterdata.LoadConditionResolver()
|
||||
if err != nil {
|
||||
log.Fatalf("load condition resolver: %v", err)
|
||||
}
|
||||
|
||||
cageOrnamentCatalog := masterdata.LoadCageOrnamentCatalog()
|
||||
loginBonusCatalog := masterdata.LoadLoginBonusCatalog()
|
||||
characterViewerCatalog := masterdata.LoadCharacterViewerCatalog(conditionResolver)
|
||||
omikujiCatalog := masterdata.LoadOmikujiCatalog()
|
||||
|
||||
materialCatalog, err := masterdata.LoadMaterialCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load material catalog: %v", err)
|
||||
}
|
||||
log.Printf("material catalog loaded: %d materials", len(materialCatalog.All))
|
||||
|
||||
costumeCatalog, err := masterdata.LoadCostumeCatalog(materialCatalog)
|
||||
if err != nil {
|
||||
log.Fatalf("load costume catalog: %v", err)
|
||||
}
|
||||
log.Printf("costume catalog loaded: %d costumes, %d materials, %d rarity curves", len(costumeCatalog.Costumes), len(costumeCatalog.Materials), len(costumeCatalog.ExpByRarity))
|
||||
|
||||
weaponCatalog, err := masterdata.LoadWeaponCatalog(materialCatalog)
|
||||
if err != nil {
|
||||
log.Fatalf("load weapon catalog: %v", err)
|
||||
}
|
||||
log.Printf("weapon catalog loaded: %d weapons, %d materials, %d enhance configs", len(weaponCatalog.Weapons), len(weaponCatalog.Materials), len(weaponCatalog.ExpByEnhanceId))
|
||||
|
||||
exploreCatalog, err := masterdata.LoadExploreCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load explore catalog: %v", err)
|
||||
}
|
||||
log.Printf("explore catalog loaded: %d explores, %d grade assets", len(exploreCatalog.Explores), len(exploreCatalog.GradeAssets))
|
||||
|
||||
gimmickCatalog, err := masterdata.LoadGimmickCatalog(conditionResolver)
|
||||
if err != nil {
|
||||
log.Fatalf("load gimmick catalog: %v", err)
|
||||
}
|
||||
|
||||
characterBoardCatalog, err := masterdata.LoadCharacterBoardCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load character board catalog: %v", err)
|
||||
}
|
||||
log.Printf("character board catalog loaded: %d panels, %d boards", len(characterBoardCatalog.PanelById), len(characterBoardCatalog.BoardById))
|
||||
|
||||
characterRebirthCatalog, err := masterdata.LoadCharacterRebirthCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load character rebirth catalog: %v", err)
|
||||
}
|
||||
log.Printf("character rebirth catalog loaded: %d characters", len(characterRebirthCatalog.StepGroupByCharacterId))
|
||||
|
||||
companionCatalog, err := masterdata.LoadCompanionCatalog()
|
||||
if err != nil {
|
||||
log.Fatalf("load companion catalog: %v", err)
|
||||
}
|
||||
log.Printf("companion catalog loaded: %d companions, %d categories", len(companionCatalog.CompanionById), len(companionCatalog.GoldCostByCategory))
|
||||
|
||||
sideStoryCatalog := masterdata.LoadSideStoryCatalog()
|
||||
bigHuntCatalog := masterdata.LoadBigHuntCatalog()
|
||||
|
||||
startGRPC(
|
||||
*host,
|
||||
octoURL,
|
||||
userStore,
|
||||
questHandler,
|
||||
gachaHandler,
|
||||
cageOrnamentCatalog,
|
||||
loginBonusCatalog,
|
||||
characterViewerCatalog,
|
||||
shopCatalog,
|
||||
costumeCatalog,
|
||||
omikujiCatalog,
|
||||
weaponCatalog,
|
||||
exploreCatalog,
|
||||
gimmickCatalog,
|
||||
characterBoardCatalog,
|
||||
partsCatalog,
|
||||
characterRebirthCatalog,
|
||||
companionCatalog,
|
||||
materialCatalog,
|
||||
gameConfig,
|
||||
sideStoryCatalog,
|
||||
bigHuntCatalog,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user