mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
refactor(channelserver): extract QuestCache from Server struct
The quest cache fields (lock, data map, expiry map) were spread across Server with manual lock management. The old read path also missed the RLock entirely, creating a data race. Encapsulating in a dedicated type fixes the race and reduces Server's field count by 2.
This commit is contained in:
@@ -205,9 +205,8 @@ func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadQuestFile(s *Session, questId int) []byte {
|
func loadQuestFile(s *Session, questId int) []byte {
|
||||||
data, exists := s.server.questCacheData[questId]
|
if cached, ok := s.server.questCache.Get(questId); ok {
|
||||||
if exists && s.server.questCacheTime[questId].Add(time.Duration(s.server.erupeConfig.QuestCacheExpiry)*time.Second).After(time.Now()) {
|
return cached
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%05dd0.bin", questId)))
|
file, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%05dd0.bin", questId)))
|
||||||
@@ -260,11 +259,9 @@ func loadQuestFile(s *Session, questId int) []byte {
|
|||||||
}
|
}
|
||||||
questBody.WriteBytes(newStrings.Data())
|
questBody.WriteBytes(newStrings.Data())
|
||||||
|
|
||||||
s.server.questCacheLock.Lock()
|
result := questBody.Data()
|
||||||
s.server.questCacheData[questId] = questBody.Data()
|
s.server.questCache.Put(questId, result)
|
||||||
s.server.questCacheTime[questId] = time.Now()
|
return result
|
||||||
s.server.questCacheLock.Unlock()
|
|
||||||
return questBody.Data()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
|
func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
|
||||||
|
|||||||
49
server/channelserver/quest_cache.go
Normal file
49
server/channelserver/quest_cache.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QuestCache is a thread-safe, expiring cache for parsed quest file data.
|
||||||
|
type QuestCache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
data map[int][]byte
|
||||||
|
expiry map[int]time.Time
|
||||||
|
ttl time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQuestCache creates a QuestCache with the given TTL in seconds.
|
||||||
|
// A TTL of 0 disables caching (Get always misses).
|
||||||
|
func NewQuestCache(ttlSeconds int) *QuestCache {
|
||||||
|
return &QuestCache{
|
||||||
|
data: make(map[int][]byte),
|
||||||
|
expiry: make(map[int]time.Time),
|
||||||
|
ttl: time.Duration(ttlSeconds) * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns cached quest data if it exists and has not expired.
|
||||||
|
func (c *QuestCache) Get(questID int) ([]byte, bool) {
|
||||||
|
if c.ttl <= 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
c.mu.RLock()
|
||||||
|
defer c.mu.RUnlock()
|
||||||
|
b, ok := c.data[questID]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if time.Now().After(c.expiry[questID]) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return b, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put stores quest data in the cache with the configured TTL.
|
||||||
|
func (c *QuestCache) Put(questID int, b []byte) {
|
||||||
|
c.mu.Lock()
|
||||||
|
c.data[questID] = b
|
||||||
|
c.expiry[questID] = time.Now().Add(c.ttl)
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
@@ -101,9 +101,7 @@ type Server struct {
|
|||||||
|
|
||||||
raviente *Raviente
|
raviente *Raviente
|
||||||
|
|
||||||
questCacheLock sync.RWMutex
|
questCache *QuestCache
|
||||||
questCacheData map[int][]byte
|
|
||||||
questCacheTime map[int]time.Time
|
|
||||||
|
|
||||||
handlerTable map[network.PacketID]handlerFunc
|
handlerTable map[network.PacketID]handlerFunc
|
||||||
}
|
}
|
||||||
@@ -132,8 +130,7 @@ func NewServer(config *Config) *Server {
|
|||||||
state: make([]uint32, 30),
|
state: make([]uint32, 30),
|
||||||
support: make([]uint32, 30),
|
support: make([]uint32, 30),
|
||||||
},
|
},
|
||||||
questCacheData: make(map[int][]byte),
|
questCache: NewQuestCache(config.ErupeConfig.QuestCacheExpiry),
|
||||||
questCacheTime: make(map[int]time.Time),
|
|
||||||
handlerTable: buildHandlerTable(),
|
handlerTable: buildHandlerTable(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ func createTestServer() *Server {
|
|||||||
sessions: make(map[net.Conn]*Session),
|
sessions: make(map[net.Conn]*Session),
|
||||||
stages: make(map[string]*Stage),
|
stages: make(map[string]*Stage),
|
||||||
semaphore: make(map[string]*Semaphore),
|
semaphore: make(map[string]*Semaphore),
|
||||||
questCacheData: make(map[int][]byte),
|
questCache: NewQuestCache(0),
|
||||||
questCacheTime: make(map[int]time.Time),
|
|
||||||
erupeConfig: &cfg.Config{
|
erupeConfig: &cfg.Config{
|
||||||
DebugOptions: cfg.DebugOptions{
|
DebugOptions: cfg.DebugOptions{
|
||||||
LogOutboundMessages: false,
|
LogOutboundMessages: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user