More code cleanup
This commit is contained in:
@@ -1,12 +1,7 @@
|
|||||||
environment: development
|
environment: development
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 3000
|
port: 3000
|
||||||
redis:
|
redis: redis://127.0.0.1:6379/0
|
||||||
host: 127.0.0.1
|
|
||||||
port: 6379
|
|
||||||
user:
|
|
||||||
password:
|
|
||||||
database: 0
|
|
||||||
routes:
|
routes:
|
||||||
face:
|
face:
|
||||||
default_overlay: true
|
default_overlay: true
|
||||||
|
|||||||
29
src/cache.go
29
src/cache.go
@@ -1,12 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultCacheKey struct {
|
type ResultCacheKey struct {
|
||||||
@@ -31,9 +28,7 @@ func GetResultCacheKey(uuid, renderType string, opts *QueryParams) (string, erro
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.Sum256(rawKeyData)
|
return fmt.Sprintf("result:%s", SHA256(rawKeyData)), nil
|
||||||
|
|
||||||
return fmt.Sprintf("result:%s", hex.EncodeToString(hash[:])), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCachedRenderResult returns the render result from Redis cache, or nil if it does not exist or cache is disabled.
|
// GetCachedRenderResult returns the render result from Redis cache, or nil if it does not exist or cache is disabled.
|
||||||
@@ -81,12 +76,26 @@ func GetCachedSkin(uuid string) (*image.NRGBA, bool, error) {
|
|||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Environment == "development" {
|
|
||||||
log.Printf("Retrieved player skin from cache (uuid=%s, slim=%v)\n", uuid, slim)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache, slim, nil
|
return cache, slim, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetCachedSkin(uuid string, value []byte, isSlim bool) error {
|
||||||
|
if err := r.Set(fmt.Sprintf("skin:%s", uuid), value, *config.Cache.SkinCacheDuration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSlim {
|
||||||
|
if err := r.Set(fmt.Sprintf("slim:%s", uuid), "true", *config.Cache.SkinCacheDuration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := r.Delete(fmt.Sprintf("slim:%s", uuid)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,14 +13,8 @@ var (
|
|||||||
Environment: "development",
|
Environment: "development",
|
||||||
Host: "127.0.0.1",
|
Host: "127.0.0.1",
|
||||||
Port: 3001,
|
Port: 3001,
|
||||||
Redis: RedisConfig{
|
Redis: "redis://127.0.0.1:6379/0",
|
||||||
Host: "127.0.0.1",
|
Routes: Routes{
|
||||||
Port: 6379,
|
|
||||||
User: "",
|
|
||||||
Password: "",
|
|
||||||
Database: 0,
|
|
||||||
},
|
|
||||||
Routes: RoutesConfig{
|
|
||||||
Face: RouteConfig{
|
Face: RouteConfig{
|
||||||
DefaultOverlay: true,
|
DefaultOverlay: true,
|
||||||
DefaultDownload: false,
|
DefaultDownload: false,
|
||||||
@@ -84,16 +78,16 @@ var (
|
|||||||
|
|
||||||
// Config is the root configuration object for the application.
|
// Config is the root configuration object for the application.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Environment string `yaml:"environment"`
|
Environment string `yaml:"environment"`
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port uint16 `yaml:"port"`
|
Port uint16 `yaml:"port"`
|
||||||
Redis RedisConfig `yaml:"redis"`
|
Redis string `yaml:"redis"`
|
||||||
Routes RoutesConfig `yaml:"routes"`
|
Routes Routes `yaml:"routes"`
|
||||||
Cache CacheConfig `yaml:"cache"`
|
Cache CacheConfig `yaml:"cache"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoutesConfig is the configuration data of all API routes.
|
// Routes is the configuration data of all API routes.
|
||||||
type RoutesConfig struct {
|
type Routes struct {
|
||||||
Face RouteConfig `yaml:"face"`
|
Face RouteConfig `yaml:"face"`
|
||||||
Head RouteConfig `yaml:"head"`
|
Head RouteConfig `yaml:"head"`
|
||||||
FullBody RouteConfig `yaml:"full_body"`
|
FullBody RouteConfig `yaml:"full_body"`
|
||||||
@@ -113,15 +107,6 @@ type RouteConfig struct {
|
|||||||
MaxScale int `yaml:"max_scale"`
|
MaxScale int `yaml:"max_scale"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RedisConfig is the configuration data used to connect to Redis.
|
|
||||||
type RedisConfig struct {
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Port uint16 `yaml:"port"`
|
|
||||||
User string `yaml:"user"`
|
|
||||||
Password string `yaml:"password"`
|
|
||||||
Database int `yaml:"database"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CacheConfig is the configuration data used to set TTL values for Redis keys.
|
// CacheConfig is the configuration data used to set TTL values for Redis keys.
|
||||||
type CacheConfig struct {
|
type CacheConfig struct {
|
||||||
SkinCacheDuration *time.Duration `yaml:"skin_cache_duration"`
|
SkinCacheDuration *time.Duration `yaml:"skin_cache_duration"`
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ type MinecraftProfile struct {
|
|||||||
} `json:"properties"`
|
} `json:"properties"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MinecraftDecodedTextures is the decoded object of the base64-encoded values property in a MinecraftProfile properties value.
|
// DecodedTextures is the decoded object of the base64-encoded values property in a MinecraftProfile properties value.
|
||||||
type MinecraftDecodedTextures struct {
|
type DecodedTextures struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -47,7 +47,7 @@ func GetMinecraftProfile(uuid string) (*MinecraftProfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("User-Agent", "mineatar.io Skin Render API")
|
req.Header.Set("User-Agent", "mineatar.io")
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ func GetMinecraftProfile(uuid string) (*MinecraftProfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := MinecraftProfile{}
|
var response MinecraftProfile
|
||||||
|
|
||||||
if err = json.Unmarshal(body, &response); err != nil {
|
if err = json.Unmarshal(body, &response); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -80,15 +80,15 @@ func GetMinecraftProfile(uuid string) (*MinecraftProfile, error) {
|
|||||||
return &response, nil
|
return &response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDecodedTexturesValue decodes the values from a MinecraftProfileTextures texture value.
|
// DecodeTexturesValue decodes the value from a MinecraftProfile texture property.
|
||||||
func GetDecodedTexturesValue(value string) (*MinecraftDecodedTextures, error) {
|
func DecodeTexturesValue(value string) (*DecodedTextures, error) {
|
||||||
rawResult, err := base64.StdEncoding.DecodeString(value)
|
rawResult, err := base64.StdEncoding.DecodeString(value)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := MinecraftDecodedTextures{}
|
var result DecodedTextures
|
||||||
|
|
||||||
if err = json.Unmarshal(rawResult, &result); err != nil {
|
if err = json.Unmarshal(rawResult, &result); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
16
src/redis.go
16
src/redis.go
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
"time"
|
"time"
|
||||||
@@ -24,17 +23,18 @@ type Redis struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect connects to the Redis server using the configuration values provided.
|
// Connect connects to the Redis server using the configuration values provided.
|
||||||
func (r *Redis) Connect(conf RedisConfig) error {
|
func (r *Redis) Connect(url string) error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
|
||||||
|
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
r.Client = redis.NewClient(&redis.Options{
|
opts, err := redis.ParseURL(url)
|
||||||
Addr: fmt.Sprintf("%s:%d", conf.Host, conf.Port),
|
|
||||||
Username: conf.User,
|
if err != nil {
|
||||||
Password: conf.Password,
|
return err
|
||||||
DB: conf.Database,
|
}
|
||||||
})
|
|
||||||
|
r.Client = redis.NewClient(opts)
|
||||||
|
|
||||||
if err := r.Client.Ping(ctx).Err(); err != nil {
|
if err := r.Client.Ping(ctx).Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
123
src/renderer.go
Normal file
123
src/renderer.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/mineatar-io/skin-render"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
RenderTypeFullBody = "fullbody"
|
||||||
|
RenderTypeFrontBody = "frontbody"
|
||||||
|
RenderTypeBackBody = "backbody"
|
||||||
|
RenderTypeLeftBody = "leftbody"
|
||||||
|
RenderTypeRightBody = "rightbody"
|
||||||
|
RenderTypeFace = "face"
|
||||||
|
RenderTypeHead = "head"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Render will render the image using the specified details and return the result.
|
||||||
|
func Render(renderType, uuid string, rawSkin *image.NRGBA, isSlim bool, opts *QueryParams) ([]byte, bool, error) {
|
||||||
|
if config.Cache.EnableLocks {
|
||||||
|
mutex := r.NewMutex(fmt.Sprintf("render-lock:%s-%d-%t-%s", renderType, opts.Scale, opts.Overlay, uuid))
|
||||||
|
mutex.Lock()
|
||||||
|
|
||||||
|
defer mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the existing render from cache if it exists
|
||||||
|
{
|
||||||
|
cache, err := GetCachedRenderResult(renderType, uuid, opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cache != nil {
|
||||||
|
return cache, true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
result *image.NRGBA
|
||||||
|
renderOpts skin.Options = skin.Options{
|
||||||
|
Overlay: opts.Overlay,
|
||||||
|
Slim: isSlim,
|
||||||
|
Scale: opts.Scale,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Render the image based on the type provided
|
||||||
|
{
|
||||||
|
switch renderType {
|
||||||
|
case RenderTypeFullBody:
|
||||||
|
{
|
||||||
|
result = skin.RenderBody(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeFrontBody:
|
||||||
|
{
|
||||||
|
result = skin.RenderFrontBody(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeBackBody:
|
||||||
|
{
|
||||||
|
result = skin.RenderBackBody(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeLeftBody:
|
||||||
|
{
|
||||||
|
result = skin.RenderLeftBody(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeRightBody:
|
||||||
|
{
|
||||||
|
result = skin.RenderRightBody(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeHead:
|
||||||
|
{
|
||||||
|
result = skin.RenderHead(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case RenderTypeFace:
|
||||||
|
{
|
||||||
|
result = skin.RenderFace(rawSkin, renderOpts)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown render type: %s", renderType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encode the image into a PNG in byte-array format
|
||||||
|
{
|
||||||
|
data, err = EncodePNG(result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the result into the cache for later use
|
||||||
|
{
|
||||||
|
if err = SetCachedRenderResult(renderType, uuid, opts, data); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, false, nil
|
||||||
|
}
|
||||||
145
src/util.go
145
src/util.go
@@ -2,7 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
@@ -20,14 +22,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
//go:embed favicon.ico
|
//go:embed favicon.ico
|
||||||
favicon []byte
|
favicon []byte
|
||||||
|
|
||||||
RenderTypeFullBody = "fullbody"
|
|
||||||
RenderTypeFrontBody = "frontbody"
|
|
||||||
RenderTypeBackBody = "backbody"
|
|
||||||
RenderTypeLeftBody = "leftbody"
|
|
||||||
RenderTypeRightBody = "rightbody"
|
|
||||||
RenderTypeFace = "face"
|
|
||||||
RenderTypeHead = "head"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryParams is used by most all API routes as options for how the image should be rendered, or how errors should be handled.
|
// QueryParams is used by most all API routes as options for how the image should be rendered, or how errors should be handled.
|
||||||
@@ -56,102 +50,6 @@ func Clamp[T int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render will render the image using the specified details and return the result.
|
|
||||||
func Render(renderType, uuid string, rawSkin *image.NRGBA, isSlim bool, opts *QueryParams) ([]byte, bool, error) {
|
|
||||||
if config.Cache.EnableLocks {
|
|
||||||
mutex := r.NewMutex(fmt.Sprintf("render-lock:%s-%d-%t-%s", renderType, opts.Scale, opts.Overlay, uuid))
|
|
||||||
mutex.Lock()
|
|
||||||
|
|
||||||
defer mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
cache, err := GetCachedRenderResult(renderType, uuid, opts)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cache != nil {
|
|
||||||
if config.Environment == "development" {
|
|
||||||
log.Printf("Retrieved render from cache (type=%s, uuid=%s, slim=%v, scale=%d)\n", renderType, uuid, isSlim, opts.Scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
result *image.NRGBA
|
|
||||||
renderOpts skin.Options = skin.Options{
|
|
||||||
Overlay: opts.Overlay,
|
|
||||||
Slim: isSlim,
|
|
||||||
Scale: opts.Scale,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
switch renderType {
|
|
||||||
case RenderTypeFullBody:
|
|
||||||
{
|
|
||||||
result = skin.RenderBody(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeFrontBody:
|
|
||||||
{
|
|
||||||
result = skin.RenderFrontBody(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeBackBody:
|
|
||||||
{
|
|
||||||
result = skin.RenderBackBody(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeLeftBody:
|
|
||||||
{
|
|
||||||
result = skin.RenderLeftBody(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeRightBody:
|
|
||||||
{
|
|
||||||
result = skin.RenderRightBody(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeHead:
|
|
||||||
{
|
|
||||||
result = skin.RenderHead(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RenderTypeFace:
|
|
||||||
{
|
|
||||||
result = skin.RenderFace(rawSkin, renderOpts)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unknown render type: %s", renderType))
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := EncodePNG(result)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = SetCachedRenderResult(renderType, uuid, opts, data); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Environment == "development" {
|
|
||||||
log.Printf("Rendered image (type=%s, uuid=%s, slim=%v, scale=%d)\n", renderType, uuid, isSlim, opts.Scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractUUID returns the UUID from the route param, allowing values such as "<uuid>.png" to be returned as "<uuid>".
|
// ExtractUUID returns the UUID from the route param, allowing values such as "<uuid>.png" to be returned as "<uuid>".
|
||||||
func ExtractUUID(ctx *fiber.Ctx) string {
|
func ExtractUUID(ctx *fiber.Ctx) string {
|
||||||
return strings.Split(ctx.Params("uuid"), ".")[0]
|
return strings.Split(ctx.Params("uuid"), ".")[0]
|
||||||
@@ -218,13 +116,13 @@ func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
err error = nil
|
err error = nil
|
||||||
skinImage *image.NRGBA = nil
|
skinImage *image.NRGBA = nil
|
||||||
rawSkin []byte = nil
|
rawSkin []byte = nil
|
||||||
isSlim bool = skin.IsSlimFromUUID(uuid)
|
isSlim bool = skin.IsSlimFromUUID(uuid)
|
||||||
profile *MinecraftProfile = nil
|
profile *MinecraftProfile = nil
|
||||||
rawTextures string = ""
|
rawTextures string = ""
|
||||||
texturesProperty *MinecraftDecodedTextures = nil
|
texturesProperty *DecodedTextures = nil
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get the textures metadata from Mojang about the Minecraft player
|
// Get the textures metadata from Mojang about the Minecraft player
|
||||||
@@ -259,7 +157,7 @@ func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) {
|
|||||||
|
|
||||||
// Decode the raw textures value returned from the player's properties
|
// Decode the raw textures value returned from the player's properties
|
||||||
{
|
{
|
||||||
if texturesProperty, err = GetDecodedTexturesValue(rawTextures); err != nil {
|
if texturesProperty, err = DecodeTexturesValue(rawTextures); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,23 +181,9 @@ func GetPlayerSkin(uuid string) (*image.NRGBA, bool, error) {
|
|||||||
|
|
||||||
// Put the skin into cache so it can be used for future requests
|
// Put the skin into cache so it can be used for future requests
|
||||||
if config.Cache.SkinCacheDuration != nil {
|
if config.Cache.SkinCacheDuration != nil {
|
||||||
if err = r.Set(fmt.Sprintf("skin:%s", uuid), rawSkin, *config.Cache.SkinCacheDuration); err != nil {
|
if err = SetCachedSkin(uuid, rawSkin, isSlim); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSlim {
|
|
||||||
if err = r.Set(fmt.Sprintf("slim:%s", uuid), "true", *config.Cache.SkinCacheDuration); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = r.Delete(fmt.Sprintf("slim:%s", uuid)); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Environment == "development" {
|
|
||||||
log.Printf("Fetched player skin from Mojang (uuid=%s, slim=%v)\n", uuid, isSlim)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return skinImage, isSlim, nil
|
return skinImage, isSlim, nil
|
||||||
@@ -357,3 +241,10 @@ func GetInstanceID() (uint16, error) {
|
|||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SHA256 computes the SHA-256 hash of the input byte-array.
|
||||||
|
func SHA256(value []byte) string {
|
||||||
|
hash := sha256.Sum256(value)
|
||||||
|
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user