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)
}