mirror of
				https://github.com/elyby/chrly.git
				synced 2025-05-31 14:11:51 +05:30 
			
		
		
		
	
		
			
				
	
	
		
			116 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mojangtextures
 | 
						|
 | 
						|
import (
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/elyby/chrly/api/mojang"
 | 
						|
 | 
						|
	"github.com/tevino/abool"
 | 
						|
)
 | 
						|
 | 
						|
var now = time.Now
 | 
						|
 | 
						|
type inMemoryItem struct {
 | 
						|
	textures  *mojang.SignedTexturesResponse
 | 
						|
	timestamp int64
 | 
						|
}
 | 
						|
 | 
						|
type InMemoryTexturesStorage struct {
 | 
						|
	GCPeriod time.Duration
 | 
						|
	Duration time.Duration
 | 
						|
 | 
						|
	lock    sync.RWMutex
 | 
						|
	data    map[string]*inMemoryItem
 | 
						|
	working *abool.AtomicBool
 | 
						|
}
 | 
						|
 | 
						|
func NewInMemoryTexturesStorage() *InMemoryTexturesStorage {
 | 
						|
	storage := &InMemoryTexturesStorage{
 | 
						|
		GCPeriod: 10 * time.Second,
 | 
						|
		Duration: time.Minute + 10*time.Second,
 | 
						|
		data:     make(map[string]*inMemoryItem),
 | 
						|
	}
 | 
						|
 | 
						|
	return storage
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) Start() {
 | 
						|
	if s.working == nil {
 | 
						|
		s.working = abool.New()
 | 
						|
	}
 | 
						|
 | 
						|
	if !s.working.IsSet() {
 | 
						|
		go func() {
 | 
						|
			time.Sleep(s.GCPeriod)
 | 
						|
			// 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()
 | 
						|
				time.Sleep(s.GCPeriod - time.Since(start))
 | 
						|
			}
 | 
						|
		}()
 | 
						|
	}
 | 
						|
 | 
						|
	s.working.Set()
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) Stop() {
 | 
						|
	s.working.UnSet()
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) GetTextures(uuid string) (*mojang.SignedTexturesResponse, error) {
 | 
						|
	s.lock.RLock()
 | 
						|
	defer s.lock.RUnlock()
 | 
						|
 | 
						|
	item, exists := s.data[uuid]
 | 
						|
	validRange := s.getMinimalNotExpiredTimestamp()
 | 
						|
	if !exists || validRange > item.timestamp {
 | 
						|
		return nil, &ValueNotFound{}
 | 
						|
	}
 | 
						|
 | 
						|
	return item.textures, nil
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) StoreTextures(uuid string, textures *mojang.SignedTexturesResponse) {
 | 
						|
	var timestamp int64
 | 
						|
	if textures != nil {
 | 
						|
		decoded := textures.DecodeTextures()
 | 
						|
		if decoded == nil {
 | 
						|
			panic("unable to decode textures")
 | 
						|
		}
 | 
						|
 | 
						|
		timestamp = decoded.Timestamp
 | 
						|
	} else {
 | 
						|
		timestamp = unixNanoToUnixMicro(now().UnixNano())
 | 
						|
	}
 | 
						|
 | 
						|
	s.lock.Lock()
 | 
						|
	defer s.lock.Unlock()
 | 
						|
 | 
						|
	s.data[uuid] = &inMemoryItem{
 | 
						|
		textures:  textures,
 | 
						|
		timestamp: timestamp,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) gc() {
 | 
						|
	s.lock.Lock()
 | 
						|
	defer s.lock.Unlock()
 | 
						|
 | 
						|
	maxTime := s.getMinimalNotExpiredTimestamp()
 | 
						|
	for uuid, value := range s.data {
 | 
						|
		if maxTime > value.timestamp {
 | 
						|
			delete(s.data, uuid)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (s *InMemoryTexturesStorage) getMinimalNotExpiredTimestamp() int64 {
 | 
						|
	return unixNanoToUnixMicro(now().Add(s.Duration * time.Duration(-1)).UnixNano())
 | 
						|
}
 | 
						|
 | 
						|
func unixNanoToUnixMicro(unixNano int64) int64 {
 | 
						|
	return unixNano / 10e5
 | 
						|
}
 |