@@ -7,16 +7,24 @@ import (
7
7
"matching-service/processes"
8
8
"matching-service/utils"
9
9
"net/http"
10
+ "sync"
10
11
11
12
"github.com/gorilla/websocket"
12
13
)
13
14
14
- var upgrader = websocket.Upgrader {
15
- CheckOrigin : func (r * http.Request ) bool {
16
- // Allow all connections by skipping the origin check (set more restrictions in production)
17
- return true
18
- },
19
- }
15
+ var (
16
+ upgrader = websocket.Upgrader {
17
+ CheckOrigin : func (r * http.Request ) bool {
18
+ // Allow all connections by skipping the origin check (set more restrictions in production)
19
+ return true
20
+ },
21
+ }
22
+ // A map to hold active WebSocket connections per username
23
+ activeConnections = make (map [string ]* websocket.Conn )
24
+ // A map to hold user's match ctx cancel function
25
+ matchContexts = make (map [string ]context.CancelFunc )
26
+ mu sync.Mutex // Mutex for thread-safe access to activeConnections
27
+ )
20
28
21
29
// handleConnections manages WebSocket connections and matching logic.
22
30
func HandleWebSocketConnections (w http.ResponseWriter , r * http.Request ) {
@@ -42,10 +50,32 @@ func HandleWebSocketConnections(w http.ResponseWriter, r *http.Request) {
42
50
return
43
51
}
44
52
53
+ // Store WebSocket connection in the activeConnections map.
54
+ mu .Lock ()
55
+ // Checks if user is already an existing websocket connection
56
+ if _ , exists := activeConnections [matchRequest .Username ]; exists {
57
+ mu .Unlock ()
58
+ log .Printf ("User %s is already connected, rejecting new connection." , matchRequest .Username )
59
+ ws .WriteJSON (models.MatchRejected {
60
+ Type : "match_rejected" ,
61
+ Message : "You are already in a matchmaking queue. Please disconnect before reconnecting." ,
62
+ })
63
+ ws .Close ()
64
+ return
65
+ }
66
+ activeConnections [matchRequest .Username ] = ws
67
+ matchCtx , matchCancel := context .WithCancel (context .Background ())
68
+ matchContexts [matchRequest .Username ] = matchCancel
69
+ mu .Unlock ()
70
+
45
71
// Create a context for cancellation
46
72
ctx , cancel := context .WithCancel (context .Background ())
47
73
defer cancel () // Ensure cancel is called to release resources
48
74
75
+ processes .EnqueueUser (processes .GetRedisClient (), matchRequest .Username , ctx )
76
+ processes .AddUserToTopicSets (processes .GetRedisClient (), matchRequest , ctx )
77
+ processes .StoreUserDetails (processes .GetRedisClient (), matchRequest , ctx )
78
+
49
79
timeoutCtx , timeoutCancel , err := createTimeoutContext ()
50
80
if err != nil {
51
81
log .Printf ("Error creating timeout context: %v" , err )
@@ -60,7 +90,7 @@ func HandleWebSocketConnections(w http.ResponseWriter, r *http.Request) {
60
90
go processes .PerformMatching (matchRequest , ctx , matchFoundChan ) // Perform matching
61
91
62
92
// Wait for a match, timeout, or cancellation.
63
- waitForResult (ws , ctx , timeoutCtx , matchFoundChan )
93
+ waitForResult (ws , ctx , timeoutCtx , matchCtx , matchFoundChan , matchRequest . Username )
64
94
}
65
95
66
96
// readMatchRequest reads the initial match request from the WebSocket connection.
@@ -69,7 +99,6 @@ func readMatchRequest(ws *websocket.Conn) (models.MatchRequest, error) {
69
99
if err := ws .ReadJSON (& matchRequest ); err != nil {
70
100
return matchRequest , err
71
101
}
72
- matchRequest .Port = ws .RemoteAddr ().String ()
73
102
log .Printf ("Received match request: %v" , matchRequest )
74
103
return matchRequest , nil
75
104
}
@@ -85,25 +114,36 @@ func createTimeoutContext() (context.Context, context.CancelFunc, error) {
85
114
}
86
115
87
116
// waitForResult waits for a match result, timeout, or cancellation.
88
- func waitForResult (ws * websocket.Conn , ctx , timeoutCtx context.Context , matchFoundChan chan models.MatchFound ) {
117
+ func waitForResult (ws * websocket.Conn , ctx , timeoutCtx , matchCtx context.Context , matchFoundChan chan models.MatchFound , username string ) {
89
118
select {
90
119
case <- ctx .Done ():
91
120
log .Println ("Matching cancelled" )
121
+ // Cleanup Redis
122
+ processes .CleanUpUser (processes .GetRedisClient (), username , ctx )
92
123
return
93
124
case <- timeoutCtx .Done ():
94
125
log .Println ("Connection timed out" )
126
+ // Cleanup Redis
127
+ processes .CleanUpUser (processes .GetRedisClient (), username , ctx )
95
128
sendTimeoutResponse (ws )
96
129
return
130
+ case <- matchCtx .Done ():
131
+ log .Println ("Match found for user HEREEE: " + username )
132
+ return
97
133
case result , ok := <- matchFoundChan :
98
134
if ! ok {
99
135
// Channel closed without a match, possibly due to context cancellation
100
136
log .Println ("Match channel closed without finding a match" )
101
137
return
102
138
}
103
- log .Println ("Match found" )
104
- if err := ws .WriteJSON (result ); err != nil {
105
- log .Printf ("write error: %v" , err )
106
- }
139
+ log .Println ("Match found for user: " + result .User )
140
+
141
+ // Notify the users about the match
142
+ notifyMatch (result .User , result .MatchedUser , result )
143
+
144
+ // if err := ws.WriteJSON(result); err != nil {
145
+ // log.Printf("write error: %v", err)
146
+ // }
107
147
return
108
148
}
109
149
}
@@ -118,3 +158,37 @@ func sendTimeoutResponse(ws *websocket.Conn) {
118
158
log .Printf ("write error: %v" , err )
119
159
}
120
160
}
161
+
162
+ func notifyMatch (username , matchedUsername string , result models.MatchFound ) {
163
+ mu .Lock ()
164
+ defer mu .Unlock ()
165
+
166
+ // Send message to the first user
167
+ if userConn , userExists := activeConnections [username ]; userExists {
168
+ if err := userConn .WriteJSON (result ); err != nil {
169
+ log .Printf ("Error sending message to user %s: %v\n " , username , err )
170
+ }
171
+ }
172
+
173
+ // Send message to the matched user
174
+ if matchedUserConn , matchedUserExists := activeConnections [matchedUsername ]; matchedUserExists {
175
+ result .User , result .MatchedUser = result .MatchedUser , result .User // Swap User and MatchedUser values
176
+ if err := matchedUserConn .WriteJSON (result ); err != nil {
177
+ log .Printf ("Error sending message to user %s: %v\n " , username , err )
178
+ }
179
+ }
180
+
181
+ // Remove the match context for both users and cancel for matched user
182
+ if _ , exists := matchContexts [username ]; exists {
183
+ delete (matchContexts , username )
184
+ }
185
+
186
+ if cancelFunc , exists := matchContexts [matchedUsername ]; exists {
187
+ delete (matchContexts , matchedUsername )
188
+ cancelFunc ()
189
+ }
190
+
191
+ // Remove users from the activeConnections map
192
+ delete (activeConnections , username )
193
+ delete (activeConnections , matchedUsername )
194
+ }
0 commit comments