QuizZip / queue.go
NathMen12's picture
Update queue.go
cffc937 verified
Raw
History Blame Contribute Delete
3.44 kB
// queue.go
package main
import (
"encoding/json"
"log"
"sync"
"time"
"os"
)
type WaitingGame struct {
Pin string `json:"pin"`
HostID string `json:"host_id"`
Required int `json:"required"`
CreatedAt int64 `json:"created_at"`
}
type SlotManager struct {
mu sync.Mutex
capacity int
available int
waiting []WaitingGame
active map[string]int
savePath string
}
func NewSlotManager(cap int, savePath string) *SlotManager {
return &SlotManager{capacity: cap, available: cap, waiting: make([]WaitingGame, 0), active: make(map[string]int), savePath: savePath}
}
func (sm *SlotManager) Load() {
data, err := os.ReadFile(sm.savePath)
if err != nil { return }
var state struct {
Available int `json:"available"`
Waiting []WaitingGame `json:"waiting"`
Active map[string]int `json:"active"`
}
if json.Unmarshal(data, &state) == nil {
sm.mu.Lock()
sm.available = state.Available
sm.waiting = state.Waiting
sm.active = state.Active
sm.mu.Unlock()
Info("Queue State Restored: Avail=%d, Waiting=%d, Active=%d", sm.available, len(sm.waiting), len(sm.active))
}
}
func (sm *SlotManager) Save() {
sm.mu.Lock()
defer sm.mu.Unlock()
state := struct {
Available int `json:"available"`
Waiting []WaitingGame `json:"waiting"`
Active map[string]int `json:"active"`
}{sm.available, sm.waiting, sm.active}
data, _ := json.Marshal(state)
os.WriteFile(sm.savePath, data, 0644)
}
func (sm *SlotManager) PersistLoop(interval time.Duration) {
ticker := time.NewTicker(interval)
for range ticker.C { sm.Save() }
}
// RequestSlots : appelé par Host (HTTP) quand il clique "Start"
func (sm *SlotManager) RequestSlots(pin, hostID string, required int) (bool, chan struct{}) {
sm.mu.Lock()
defer sm.mu.Unlock()
if sm.available >= required {
sm.available -= required
sm.active[pin] = required
sm.Save()
return true, nil
}
ch := make(chan struct{}, 1)
wg := WaitingGame{Pin: pin, HostID: hostID, Required: required, CreatedAt: time.Now().Unix()}
sm.waiting = append(sm.waiting, wg)
sm.Save()
return false, ch
}
// ReleaseSlots : appelé quand partie finie (Game Engine)
func (sm *SlotManager) ReleaseSlots(pin string, count int) {
sm.mu.Lock()
defer sm.mu.Unlock()
reserved := sm.active[pin]
if reserved == 0 { return }
releaseCount := min(count, reserved)
sm.available += releaseCount
if releaseCount >= reserved {
delete(sm.active, pin)
} else {
sm.active[pin] = reserved - releaseCount
}
// Traiter file d'attente
newWaiting := make([]WaitingGame, 0, len(sm.waiting))
for _, wg := range sm.waiting {
if sm.available >= wg.Required {
sm.available -= wg.Required
sm.active[wg.Pin] = wg.Required
// NOTIFIER LE GAME ENGINE (via fonction globale)
go notifyQueueReady(wg.Pin) // Goroutine pour ne pas bloquer le lock
} else {
newWaiting = append(newWaiting, wg)
}
}
sm.waiting = newWaiting
sm.Save()
}
func (sm *SlotManager) GetQueuePosition(pin string) int {
sm.mu.Lock()
defer sm.mu.Unlock()
for i, wg := range sm.waiting {
if wg.Pin == pin { return i + 1 }
}
return -1
}
// notifyQueueReady est appelé par Queue Manager quand des slots se libèrent
// Elle doit déclencher le démarrage de la partie dans game.go
func notifyQueueReady(pin string) {
Info("Queue Slot Granted -> Starting Game: %s", pin)
// Appelle la fonction de game.go (package main)
StartGameLoop(hub, store, pin)
}