package mcfetch import ( "encoding/json" "errors" "net/http" "time" ) // AsyncPlayerFetcher is responsible for fetching Minecraft player data asynchronously type AsyncPlayerFetcher struct { playerName string retries int retryDelay time.Duration timeout time.Duration cache ICache } // NewAsyncPlayerFetcher creates a new AsyncPlayerFetcher with an abstract cache (ICache) func NewAsyncPlayerFetcher(playerName string, cache ICache, retries int, retryDelay time.Duration, timeout time.Duration) *AsyncPlayerFetcher { cache.Init() return &AsyncPlayerFetcher{ playerName: playerName, retries: retries, retryDelay: retryDelay, timeout: timeout, cache: cache, } } // FetchPlayerData fetches the player data asynchronously using channels func (pf *AsyncPlayerFetcher) FetchPlayerData(resultChan chan *FetchedPlayerResult, errorChan chan error) { go func() { cachedData, found := pf.cache.Get(pf.playerName) if found { resultChan <- (*FetchedPlayerResult)(cachedData) return } // If not in cache, make request to Mojang API var player FetchedPlayerResult for i := 0; i < pf.retries; i++ { resp, err := pf.makeRequest(pf.playerName) if err == nil { defer resp.Body.Close() // Decode the response into FetchedPlayerResult if err := json.NewDecoder(resp.Body).Decode(&player); err == nil { // Store the result in the cache and return the data pf.cache.Set(pf.playerName, (*CacheResult)(&player)) pf.cache.Sync() resultChan <- &player return } } time.Sleep(pf.retryDelay) } errorChan <- errors.New("Failed to fetch player data after retries") }() } // makeRequest performs the HTTP request to Mojang API func (pf *AsyncPlayerFetcher) makeRequest(playerName string) (*http.Response, error) { client := http.Client{Timeout: pf.timeout} url := "https://api.mojang.com/users/profiles/minecraft/" + playerName resp, err := client.Get(url) if err != nil || resp.StatusCode != http.StatusOK { return nil, errors.New("Request failed") } return resp, nil }