2019-11-21 04:03:13 +05:30
|
|
|
package mojangtextures
|
2019-04-21 22:58:58 +05:30
|
|
|
|
|
|
|
import (
|
2020-04-29 23:45:13 +05:30
|
|
|
"fmt"
|
2019-04-21 22:58:58 +05:30
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2020-04-29 23:45:13 +05:30
|
|
|
"github.com/getsentry/raven-go"
|
|
|
|
|
2019-04-21 22:58:58 +05:30
|
|
|
"github.com/elyby/chrly/api/mojang"
|
|
|
|
|
|
|
|
"github.com/tevino/abool"
|
|
|
|
)
|
|
|
|
|
|
|
|
var now = time.Now
|
|
|
|
|
|
|
|
type inMemoryItem struct {
|
|
|
|
textures *mojang.SignedTexturesResponse
|
|
|
|
timestamp int64
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
type InMemoryTexturesStorage struct {
|
|
|
|
GCPeriod time.Duration
|
|
|
|
Duration time.Duration
|
|
|
|
|
2020-01-06 02:15:11 +05:30
|
|
|
lock sync.RWMutex
|
2019-04-21 22:58:58 +05:30
|
|
|
data map[string]*inMemoryItem
|
|
|
|
working *abool.AtomicBool
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func NewInMemoryTexturesStorage() *InMemoryTexturesStorage {
|
|
|
|
storage := &InMemoryTexturesStorage{
|
|
|
|
GCPeriod: 10 * time.Second,
|
|
|
|
Duration: time.Minute + 10*time.Second,
|
|
|
|
data: make(map[string]*inMemoryItem),
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
2019-06-19 02:04:16 +05:30
|
|
|
|
|
|
|
return storage
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) Start() {
|
2019-04-21 22:58:58 +05:30
|
|
|
if s.working == nil {
|
|
|
|
s.working = abool.New()
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.working.IsSet() {
|
|
|
|
go func() {
|
2019-11-21 04:03:13 +05:30
|
|
|
time.Sleep(s.GCPeriod)
|
2019-04-21 22:58:58 +05:30
|
|
|
// TODO: this can be reimplemented in future with channels, but right now I have no idea how to make it right
|
|
|
|
for s.working.IsSet() {
|
|
|
|
start := time.Now()
|
|
|
|
s.gc()
|
2019-11-21 04:03:13 +05:30
|
|
|
time.Sleep(s.GCPeriod - time.Since(start))
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
s.working.Set()
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) Stop() {
|
2019-04-21 22:58:58 +05:30
|
|
|
s.working.UnSet()
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) {
|
2020-01-06 02:15:11 +05:30
|
|
|
s.lock.RLock()
|
|
|
|
defer s.lock.RUnlock()
|
2019-04-21 22:58:58 +05:30
|
|
|
|
|
|
|
item, exists := s.data[uuid]
|
2019-11-21 04:03:13 +05:30
|
|
|
validRange := s.getMinimalNotExpiredTimestamp()
|
2019-05-06 01:36:29 +05:30
|
|
|
if !exists || validRange > item.timestamp {
|
2020-04-28 20:27:51 +05:30
|
|
|
return nil, nil
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return item.textures, nil
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
|
2019-05-06 01:36:29 +05:30
|
|
|
var timestamp int64
|
|
|
|
if textures != nil {
|
2020-04-29 23:45:13 +05:30
|
|
|
decoded, err := textures.DecodeTextures()
|
|
|
|
if err != nil {
|
|
|
|
tags := map[string]string{
|
|
|
|
"textures.id": textures.Id,
|
|
|
|
"textures.name": textures.Name,
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, prop := range textures.Props {
|
|
|
|
tags[fmt.Sprintf("textures.props[%d].name", i)] = prop.Name
|
|
|
|
tags[fmt.Sprintf("textures.props[%d].value", i)] = prop.Value
|
|
|
|
tags[fmt.Sprintf("textures.props[%d].signature", i)] = prop.Signature
|
|
|
|
}
|
|
|
|
|
|
|
|
raven.CaptureErrorAndWait(err, tags)
|
|
|
|
|
|
|
|
panic(err)
|
2019-05-06 01:36:29 +05:30
|
|
|
}
|
2019-04-21 22:58:58 +05:30
|
|
|
|
2019-05-06 01:36:29 +05:30
|
|
|
timestamp = decoded.Timestamp
|
|
|
|
} else {
|
|
|
|
timestamp = unixNanoToUnixMicro(now().UnixNano())
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
|
|
|
|
2019-05-06 01:36:29 +05:30
|
|
|
s.lock.Lock()
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
|
|
|
|
s.data[uuid] = &inMemoryItem{
|
2019-04-21 22:58:58 +05:30
|
|
|
textures: textures,
|
2019-05-06 01:36:29 +05:30
|
|
|
timestamp: timestamp,
|
2019-04-21 22:58:58 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) gc() {
|
2019-04-21 22:58:58 +05:30
|
|
|
s.lock.Lock()
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
maxTime := s.getMinimalNotExpiredTimestamp()
|
2019-04-21 22:58:58 +05:30
|
|
|
for uuid, value := range s.data {
|
|
|
|
if maxTime > value.timestamp {
|
|
|
|
delete(s.data, uuid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-06 01:36:29 +05:30
|
|
|
|
2019-11-21 04:03:13 +05:30
|
|
|
func (s *InMemoryTexturesStorage) getMinimalNotExpiredTimestamp() int64 {
|
|
|
|
return unixNanoToUnixMicro(now().Add(s.Duration * time.Duration(-1)).UnixNano())
|
2019-05-06 01:36:29 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
func unixNanoToUnixMicro(unixNano int64) int64 {
|
|
|
|
return unixNano / 10e5
|
|
|
|
}
|