mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
210 lines
7.0 KiB
Go
210 lines
7.0 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
// LauncherAPIHost is the official JP launcher API host observed from the PC launcher.
|
|
const LauncherAPIHost = "api-launcher-jp.yo-star.com"
|
|
|
|
func (s *Server) handleLauncherBootstrap(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
|
return
|
|
}
|
|
sum := s.releaseSummary()
|
|
status := http.StatusOK
|
|
if !sum.Ready || sum.Snapshot == nil {
|
|
status = http.StatusServiceUnavailable
|
|
}
|
|
writeNoStoreJSON(w, status, s.launcherBootstrapBody(sum))
|
|
}
|
|
|
|
func (s *Server) handleLauncherGameConfig(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
|
return
|
|
}
|
|
sum := s.releaseSummary()
|
|
if !sum.Ready || sum.Snapshot == nil {
|
|
writeLauncherEnvelope(w, http.StatusServiceUnavailable, 503, "release not ready", LauncherGameConfigJSONData{
|
|
ResourceBootstrapURL: s.launcherBootstrapURL(),
|
|
Scope: "resource_bootstrap_only",
|
|
})
|
|
return
|
|
}
|
|
snap := sum.Snapshot
|
|
data := LauncherGameConfigData{
|
|
GameLatestVersion: defaultString(s.launcherLatestVersion(snap), snap.AppVersion),
|
|
GameLatestFilePath: s.launcherLatestFilePath(snap),
|
|
GameStartExeName: s.launcherStartExeName(snap),
|
|
GameStartParams: s.launcherStartParams(snap),
|
|
ResourceBootstrapURL: s.launcherBootstrapURL(),
|
|
ServerInfoURL: s.serverInfoURL(),
|
|
ClientPatchBaseURL: s.clientPatchBaseURL(),
|
|
Scope: "resource_bootstrap_only",
|
|
}
|
|
if snap.LauncherMetadata != nil && snap.LauncherMetadata.GameLowestVersion != "" {
|
|
data.GameLowestVersion = snap.LauncherMetadata.GameLowestVersion
|
|
}
|
|
writeLauncherEnvelope(w, http.StatusOK, 200, "ok", data)
|
|
}
|
|
|
|
func (s *Server) handleLauncherGameConfigJSON(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
|
return
|
|
}
|
|
sum := s.releaseSummary()
|
|
if !sum.Ready || sum.Snapshot == nil {
|
|
writeLauncherEnvelope(w, http.StatusServiceUnavailable, 503, "release not ready", LauncherGameConfigJSONData{
|
|
URL: s.launcherBootstrapURL(),
|
|
ResourceBootstrapURL: s.launcherBootstrapURL(),
|
|
Scope: "resource_bootstrap_only",
|
|
})
|
|
return
|
|
}
|
|
writeLauncherEnvelope(w, http.StatusOK, 200, "ok", LauncherGameConfigJSONData{
|
|
URL: s.launcherBootstrapJSONURL(r.URL.Query()),
|
|
ResourceBootstrapURL: s.launcherBootstrapURL(),
|
|
PackageUpdateManifest: false,
|
|
Scope: "resource_bootstrap_only",
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleLauncherCdnConfig(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeErrorJSON(w, http.StatusMethodNotAllowed, "method_not_allowed", "method not allowed")
|
|
return
|
|
}
|
|
writeLauncherEnvelope(w, http.StatusOK, 200, "ok", LauncherCdnConfigData{
|
|
PrimaryCDN: strings.TrimRight(s.cfg.PublicBaseURL, "/"),
|
|
BackupCDN: strings.TrimRight(s.cfg.PublicBaseURL, "/"),
|
|
ResourceBootstrapURL: s.launcherBootstrapURL(),
|
|
PackageUpdateManifest: false,
|
|
Scope: "resource_bootstrap_only",
|
|
})
|
|
}
|
|
|
|
func (s *Server) launcherBootstrapBody(sum ReleaseSummary) LauncherBootstrapResponse {
|
|
publicBase := strings.TrimRight(s.cfg.PublicBaseURL, "/")
|
|
body := LauncherBootstrapResponse{
|
|
Service: "bat-api",
|
|
Ready: sum.Ready,
|
|
LauncherAPI: LauncherAPIInfo{
|
|
Host: LauncherAPIHost,
|
|
ObservedResourceRelevantEndpoints: []string{
|
|
"/api/launcher/game/config",
|
|
"/api/launcher/game/config/json",
|
|
"/api/launcher/advanced/game/download/cdn",
|
|
},
|
|
CompatibilityScope: "resource_bootstrap_only",
|
|
},
|
|
Policy: LauncherPolicy{
|
|
Source: "rust_bat_snapshot",
|
|
DownloadsLauncherPackage: false,
|
|
EmulatesLoginOrGateway: false,
|
|
PackageUpdateManifest: false,
|
|
},
|
|
Resource: LauncherResource{
|
|
Release: sum.Snapshot,
|
|
Distribution: sum.Distribution,
|
|
ServerInfoURL: s.serverInfoURL(),
|
|
ClientPatchBaseURL: s.clientPatchBaseURL(),
|
|
},
|
|
Endpoints: LauncherEndpointSet{
|
|
Bootstrap: publicBase + "/v1/bootstrap",
|
|
LauncherBootstrap: publicBase + "/v1/launcher/bootstrap",
|
|
ServerInfo: s.serverInfoURL(),
|
|
ClientPatchBase: s.clientPatchBaseURL(),
|
|
LauncherGameConfig: publicBase + "/" + LauncherAPIHost + "/api/launcher/game/config",
|
|
LauncherGameConfigJSON: publicBase + "/" + LauncherAPIHost + "/api/launcher/game/config/json",
|
|
LauncherCdnConfig: publicBase + "/" + LauncherAPIHost + "/api/launcher/advanced/game/download/cdn",
|
|
},
|
|
}
|
|
if sum.Snapshot != nil {
|
|
body.GameMainConfig = sum.Snapshot.GameMainConfig
|
|
body.LauncherMetadata = sum.Snapshot.LauncherMetadata
|
|
if rewritten, ok := rewriteAddressablesRoot(sum.Snapshot.AddressablesRoot, publicBase); ok {
|
|
body.AddressablesCatalogURLRoot = rewritten
|
|
}
|
|
}
|
|
return body
|
|
}
|
|
|
|
func (s *Server) releaseSummary() ReleaseSummary {
|
|
if idx := s.index(); idx != nil {
|
|
return idx.Summary()
|
|
}
|
|
return ReleaseSummary{}
|
|
}
|
|
|
|
func (s *Server) serverInfoURL() string {
|
|
return strings.TrimRight(s.cfg.PublicBaseURL, "/") + "/" + ServerInfoHost + "/server-info.json"
|
|
}
|
|
|
|
func (s *Server) clientPatchBaseURL() string {
|
|
return strings.TrimRight(s.cfg.PublicBaseURL, "/") + "/" + ClientPatchHost
|
|
}
|
|
|
|
func (s *Server) launcherBootstrapURL() string {
|
|
return strings.TrimRight(s.cfg.PublicBaseURL, "/") + "/v1/launcher/bootstrap"
|
|
}
|
|
|
|
func (s *Server) launcherBootstrapJSONURL(query url.Values) string {
|
|
base := strings.TrimRight(s.cfg.PublicBaseURL, "/") + "/" + LauncherAPIHost + "/api/launcher/resource/bootstrap.json"
|
|
values := url.Values{}
|
|
for _, key := range []string{"version", "file_path"} {
|
|
if value := query.Get(key); value != "" {
|
|
values.Set(key, value)
|
|
}
|
|
}
|
|
if encoded := values.Encode(); encoded != "" {
|
|
return base + "?" + encoded
|
|
}
|
|
return base
|
|
}
|
|
|
|
func (s *Server) launcherLatestVersion(snap *SnapshotSummary) string {
|
|
if snap.LauncherMetadata != nil && snap.LauncherMetadata.GameLatestVersion != "" {
|
|
return snap.LauncherMetadata.GameLatestVersion
|
|
}
|
|
return snap.AppVersion
|
|
}
|
|
|
|
func (s *Server) launcherLatestFilePath(snap *SnapshotSummary) string {
|
|
if snap.LauncherMetadata != nil {
|
|
return snap.LauncherMetadata.GameLatestFilePath
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (s *Server) launcherStartExeName(snap *SnapshotSummary) string {
|
|
if snap.LauncherMetadata != nil {
|
|
return snap.LauncherMetadata.GameStartExeName
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (s *Server) launcherStartParams(snap *SnapshotSummary) []string {
|
|
if snap.LauncherMetadata != nil && len(snap.LauncherMetadata.GameStartParams) > 0 {
|
|
return append([]string(nil), snap.LauncherMetadata.GameStartParams...)
|
|
}
|
|
return []string{}
|
|
}
|
|
|
|
func writeLauncherEnvelope[T any](w http.ResponseWriter, status int, code int, message string, data T) {
|
|
writeNoStoreJSON(w, status, LauncherEnvelope[T]{
|
|
Code: code,
|
|
Message: message,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
func launcherHostPath(path string) string {
|
|
return fmt.Sprintf("/%s%s", LauncherAPIHost, path)
|
|
}
|