mirror of
https://github.com/unmojang/drasl.git
synced 2025-09-08 22:56:49 -04:00
Look up fallback player ID using POST /profiles/minecraft
authlib-injector specifies POST /profiles/minecraft as the only available route for player name -> UUID, so we have to use it if we want to support authlib-injector-compatible fallback API servers.
This commit is contained in:
parent
7d2f20ca92
commit
b0eb50238b
41
account.go
41
account.go
@ -59,7 +59,7 @@ func (fallbackAPIServer *FallbackAPIServer) PlayerNamesToIDs(remainingLowerNames
|
|||||||
return responses
|
return responses
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fallbackAPIServer *FallbackAPIServer) PlayerNamesToIDsWorker() {
|
func (app *App) PlayerNamesToIDsWorker(fallbackAPIServer *FallbackAPIServer) {
|
||||||
// All communication with the POST /profiles/minecraft (a.k.a. POST
|
// All communication with the POST /profiles/minecraft (a.k.a. POST
|
||||||
// /minecraft/profile/lookup/bulk/byname) route on a fallback API server is
|
// /minecraft/profile/lookup/bulk/byname) route on a fallback API server is
|
||||||
// done by a single goroutine running this function. It buffers a queue of
|
// done by a single goroutine running this function. It buffers a queue of
|
||||||
@ -93,6 +93,16 @@ func (fallbackAPIServer *FallbackAPIServer) PlayerNamesToIDsWorker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double-check for validity, invalid player names will spoil
|
||||||
|
// the entire batch. We will assume that if a player name is
|
||||||
|
// valid to Drasl, it is valid on all fallback API servers (if
|
||||||
|
// this becomes a problem in the future, we may need a
|
||||||
|
// FallbackAPIServer.ValidPlayerNameRegex.
|
||||||
|
if app.ValidatePlayerName(job.LowerName) != nil {
|
||||||
|
job.ReturnCh <- mo.None[PlayerNameToIDResponse]()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := lowerNameToResponseChs[job.LowerName]; !ok {
|
if _, ok := lowerNameToResponseChs[job.LowerName]; !ok {
|
||||||
lowerNameQueue = append(lowerNameQueue, &job.LowerName)
|
lowerNameQueue = append(lowerNameQueue, &job.LowerName)
|
||||||
}
|
}
|
||||||
@ -197,6 +207,17 @@ func AccountPlayerNameToID(app *App) func(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lowerName := strings.ToLower(playerName)
|
lowerName := strings.ToLower(playerName)
|
||||||
|
if app.ValidatePlayerName(lowerName) != nil {
|
||||||
|
// This error message is consistent with POST
|
||||||
|
// https://api.mojang.com/users/profiles/minecraft/:playerName as
|
||||||
|
// of 2025-04-03
|
||||||
|
errorMessage := fmt.Sprintf("getProfileName.name: Invalid profile name")
|
||||||
|
return &YggdrasilError{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Error_: mo.Some("CONSTRAINT_VIOLATION"),
|
||||||
|
ErrorMessage: mo.Some(errorMessage),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var player Player
|
var player Player
|
||||||
result := app.DB.First(&player, "name = ?", lowerName)
|
result := app.DB.First(&player, "name = ?", lowerName)
|
||||||
@ -246,7 +267,8 @@ func AccountPlayerNamesToIDs(app *App) func(c echo.Context) error {
|
|||||||
Error_: mo.Some("CONSTRAINT_VIOLATION"),
|
Error_: mo.Some("CONSTRAINT_VIOLATION"),
|
||||||
ErrorMessage: mo.Some(errorMessage),
|
ErrorMessage: mo.Some(errorMessage),
|
||||||
}
|
}
|
||||||
} else if len(playerNames) > MAX_PLAYER_NAMES_TO_IDS {
|
}
|
||||||
|
if len(playerNames) > MAX_PLAYER_NAMES_TO_IDS {
|
||||||
// This error message is consistent with POST
|
// This error message is consistent with POST
|
||||||
// https://api.mojang.com/profiles/minecraft as of 2025-04-02
|
// https://api.mojang.com/profiles/minecraft as of 2025-04-02
|
||||||
errorMessage := fmt.Sprintf("getProfileName.profileNames: size must be between 0 and %d", MAX_PLAYER_NAMES_TO_IDS)
|
errorMessage := fmt.Sprintf("getProfileName.profileNames: size must be between 0 and %d", MAX_PLAYER_NAMES_TO_IDS)
|
||||||
@ -271,7 +293,20 @@ func AccountPlayerNamesToIDs(app *App) func(c echo.Context) error {
|
|||||||
ErrorMessage: mo.Some(errorMessage),
|
ErrorMessage: mo.Some(errorMessage),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remainingLowerNames.Add(strings.ToLower(playerName))
|
|
||||||
|
lowerName := strings.ToLower(playerName)
|
||||||
|
if app.ValidatePlayerName(lowerName) != nil {
|
||||||
|
// This error message is consistent with POST
|
||||||
|
// https://api.mojang.com/profiles/minecraft as of 2025-04-03
|
||||||
|
errorMessage := fmt.Sprintf("getProfileName.profileNames[%d].<list element>: Invalid profile name", i)
|
||||||
|
return &YggdrasilError{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Error_: mo.Some("CONSTRAINT_VIOLATION"),
|
||||||
|
ErrorMessage: mo.Some(errorMessage),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingLowerNames.Add(lowerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, lowerName := range remainingLowerNames.ToSlice() {
|
for _, lowerName := range remainingLowerNames.ToSlice() {
|
||||||
|
50
common.go
50
common.go
@ -9,6 +9,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
"github.com/dgraph-io/ristretto"
|
"github.com/dgraph-io/ristretto"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -687,7 +688,7 @@ func (app *App) GetFallbackSkinTexturesProperty(player *Player) (*SessionProfile
|
|||||||
fallbackPlayer = player.FallbackPlayer
|
fallbackPlayer = player.FallbackPlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fallbackAPIServer := range app.Config.FallbackAPIServers {
|
for _, fallbackAPIServer := range app.FallbackAPIServers {
|
||||||
var id string
|
var id string
|
||||||
if fallbackPlayerIsUUID {
|
if fallbackPlayerIsUUID {
|
||||||
// If we have the UUID already, use it
|
// If we have the UUID already, use it
|
||||||
@ -695,38 +696,17 @@ func (app *App) GetFallbackSkinTexturesProperty(player *Player) (*SessionProfile
|
|||||||
} else {
|
} else {
|
||||||
// Otherwise, we only know the player name. Query the fallback API
|
// Otherwise, we only know the player name. Query the fallback API
|
||||||
// server to get the fallback player's UUID
|
// server to get the fallback player's UUID
|
||||||
// TODO this should POST /profiles/minecraft instead to be authlib-injector-compatible
|
lowerName := strings.ToLower(fallbackPlayer)
|
||||||
reqURL, err := url.JoinPath(fallbackAPIServer.AccountURL, "/users/profiles/minecraft/", fallbackPlayer)
|
fallbackResponses := fallbackAPIServer.PlayerNamesToIDs(mapset.NewSet(lowerName))
|
||||||
if err != nil {
|
if len(fallbackResponses) == 1 && strings.EqualFold(lowerName, fallbackResponses[0].Name) {
|
||||||
log.Println(err)
|
id = fallbackResponses[0].ID
|
||||||
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res, err := app.CachedGet(reqURL, fallbackAPIServer.CacheTTLSeconds)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Couldn't access fallback API server at %s: %s\n", reqURL, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
// Be silent, 404s will be common here
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var playerResponse PlayerNameToIDResponse
|
|
||||||
err = json.Unmarshal(res.BodyBytes, &playerResponse)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Received invalid response from fallback API server at %s\n", reqURL)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id = playerResponse.ID
|
|
||||||
}
|
|
||||||
reqURL, err := url.JoinPath(fallbackAPIServer.SessionURL, "session/minecraft/profile", id)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := app.CachedGet(reqURL+"?unsigned=false", fallbackAPIServer.CacheTTLSeconds)
|
reqURL := fallbackAPIServer.Config.SessionURL + "/session/minecraft/profile/" + url.PathEscape(id)
|
||||||
|
res, err := app.CachedGet(reqURL+"?unsigned=false", fallbackAPIServer.Config.CacheTTLSeconds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Couldn't access fallback API server at %s: %s\n", reqURL, err)
|
log.Printf("Couldn't access fallback API server at %s: %s\n", reqURL, err)
|
||||||
continue
|
continue
|
||||||
@ -806,11 +786,7 @@ func (app *App) GetDefaultSkinTexture(player *Player) *texture {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultSkinURL, err := url.JoinPath(app.FrontEndURL, "web/texture/default-skin/"+filename)
|
defaultSkinURL := app.FrontEndURL + "/web/texture/default-skin/" + url.PathEscape(filename)
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error generating default skin URL for file %s\n", *defaultSkinPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
skinModel := SkinModelClassic
|
skinModel := SkinModelClassic
|
||||||
if slimSkinRegex.MatchString(*defaultSkinPath) {
|
if slimSkinRegex.MatchString(*defaultSkinPath) {
|
||||||
@ -844,11 +820,7 @@ func (app *App) GetDefaultCapeTexture(player *Player) *texture {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCapeURL, err := url.JoinPath(app.FrontEndURL, "web/texture/default-cape/"+filename)
|
defaultCapeURL := app.FrontEndURL + "/web/texture/default-cape/" + url.PathEscape(filename)
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error generating default cape URL for file %s\n", *defaultCapePath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &texture{
|
return &texture{
|
||||||
URL: defaultCapeURL,
|
URL: defaultCapeURL,
|
||||||
|
2
main.go
2
main.go
@ -612,7 +612,7 @@ func setup(config *Config) *App {
|
|||||||
|
|
||||||
func (app *App) Run() {
|
func (app *App) Run() {
|
||||||
for _, fallbackAPIServer := range PtrSlice(app.FallbackAPIServers) {
|
for _, fallbackAPIServer := range PtrSlice(app.FallbackAPIServers) {
|
||||||
go (*fallbackAPIServer).PlayerNamesToIDsWorker()
|
go app.PlayerNamesToIDsWorker(fallbackAPIServer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user