File size: 3,441 Bytes
cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 cffc937 caa7df1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | // 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)
} |