Skip to content

Commit d50470a

Browse files
committed
working
1 parent d45c198 commit d50470a

File tree

15 files changed

+335
-150
lines changed

15 files changed

+335
-150
lines changed
Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,47 @@
11
package com.rakeshv.tictactoe.config;
22

3+
import com.rakeshv.tictactoe.models.Response;
4+
import com.rakeshv.tictactoe.services.GameStorage;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.beans.factory.annotation.Autowired;
37
import org.springframework.messaging.Message;
48
import org.springframework.messaging.MessageChannel;
59
import org.springframework.messaging.MessageHeaders;
10+
import org.springframework.messaging.simp.SimpMessagingTemplate;
11+
import org.springframework.messaging.simp.stomp.StompCommand;
612
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
713
import org.springframework.messaging.support.ChannelInterceptor;
814
import org.springframework.util.MultiValueMap;
915

1016
import java.util.Map;
1117
import java.util.Set;
1218

19+
@Slf4j
1320
public class WebsocketChannelInterceptor implements ChannelInterceptor {
21+
1422
@Override
1523
public Message<?> preSend(Message<?> message, MessageChannel channel) {
1624
MessageHeaders headers = message.getHeaders();
1725
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
18-
// MultiValueMap<String, String> multiValueMap = headers.get(StompHeaderAccessor.NATIVE_HEADERS, MultiValueMap.class);
19-
// Set<String> keys = headers.keySet();
20-
// keys.forEach(k -> System.out.println("eky is " + k));
21-
// headers.values().forEach(x -> System.out.println("value is " + x));
22-
// multiValueMap.values().forEach(System.out::println);
26+
Set<String> keys = headers.keySet();
2327

24-
Set<Map.Entry<String, Object>> entrySet = headers.entrySet();
25-
// for (Map.Entry e : entrySet) {
26-
// System.out.println("key is " + e.getKey() + " and value is " + e.getValue());
27-
// }
28+
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
29+
MultiValueMap<String, String> multiValueMap = headers.get(StompHeaderAccessor.NATIVE_HEADERS, MultiValueMap.class);
30+
if (multiValueMap != null) {
31+
String username = multiValueMap.toSingleValueMap().get("username");
32+
Object sessionId = headers.get("simpSessionId");
33+
log.info("Session id is {}, username is {}", sessionId, username);
34+
}
35+
}
2836
return message;
2937
}
3038

3139
@Override
3240
public Message<?> postReceive(Message<?> message, MessageChannel channel) {
33-
MessageHeaders headers = message.getHeaders();
34-
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
35-
36-
Set<Map.Entry<String, Object>> entrySet = headers.entrySet();
37-
for (Map.Entry e : entrySet) {
38-
// System.out.println("key is " + e.getKey() + " and value is " + e.getValue());
39-
}
4041
return null;
4142
}
4243

4344
@Override
4445
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
45-
// MessageHeaders headers = message.getHeaders();
46-
// StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
47-
//
48-
// Set<Map.Entry<String, Object>> entrySet = headers.entrySet();
49-
// for (Map.Entry e : entrySet) {
50-
// System.out.println("key is " + e.getKey() + " and value is " + e.getValue());
51-
// }
5246
}
5347
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/config/WebsocketConfiguration.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.rakeshv.tictactoe.models.Game;
44
import com.rakeshv.tictactoe.models.GameStatus;
5+
import com.rakeshv.tictactoe.models.Response;
56
import com.rakeshv.tictactoe.services.GameStorage;
67
import lombok.extern.slf4j.Slf4j;
78
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,6 +25,7 @@
2425

2526
import javax.servlet.http.HttpSession;
2627
import java.util.Map;
28+
import java.util.Optional;
2729
import java.util.Set;
2830

2931
@Configuration
@@ -65,13 +67,21 @@ public void registerStompEndpoints(StompEndpointRegistry registry) {
6567

6668
@EventListener
6769
public void onDisconnectEvent(SessionDisconnectEvent event) {
68-
String message = "Game terminated";
69-
log.error("session disconnet {}", event.getSessionId());
70-
String gameId = GameStorage.getInstance().getGameBySessionId(event.getSessionId());
71-
Game game = GameStorage.getInstance().getGameById(gameId);
72-
game.setGameStatus(GameStatus.TERMINATED);
73-
this.messagingTemplate.convertAndSend("/topic/terminate-game/" + game.getPlayer1().getLogin(), message);
74-
this.messagingTemplate.convertAndSend("/topic/terminate-game/" + game.getPlayer2().getLogin(), message);
70+
String sessionId = event.getSessionId();
71+
String user = GameStorage.getInstance().getUserName(sessionId);
72+
String message = "Game terminated by " + user;
73+
Optional<Game> optionalGame = GameStorage.getInstance().getGameById(sessionId);
74+
if (optionalGame.isPresent()) {
75+
Game game = optionalGame.get();
76+
game.setGameStatus(GameStatus.TERMINATED);
77+
GameStorage.getInstance().removeGame(sessionId);
78+
GameStorage.getInstance().removePlayer(sessionId);
79+
80+
Response response = Response.builder().message(message).build();
81+
this.messagingTemplate.convertAndSend("/topic/terminate-game/" + game.getPlayer1().getLogin(), response);
82+
if (game.getPlayer2() != null)
83+
this.messagingTemplate.convertAndSend("/topic/terminate-game/" + game.getPlayer2().getLogin(), response);
84+
}
7585
}
7686

7787
@Override
Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
package com.rakeshv.tictactoe.controllers;
22

3-
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.ObjectMapper;
53
import com.rakeshv.tictactoe.exceptions.GameNotFoundException;
64
import com.rakeshv.tictactoe.exceptions.InvalidGameException;
75
import com.rakeshv.tictactoe.models.Game;
86
import com.rakeshv.tictactoe.models.GamePlay;
97
import com.rakeshv.tictactoe.models.Player;
8+
import com.rakeshv.tictactoe.models.Response;
109
import com.rakeshv.tictactoe.services.GameService;
10+
import com.rakeshv.tictactoe.services.GameStorage;
1111
import lombok.extern.slf4j.Slf4j;
1212
import org.springframework.beans.factory.annotation.Autowired;
1313
import org.springframework.messaging.Message;
1414
import org.springframework.messaging.MessageHeaders;
1515
import org.springframework.messaging.handler.annotation.MessageMapping;
1616
import org.springframework.messaging.handler.annotation.Payload;
17-
import org.springframework.messaging.handler.annotation.SendTo;
1817
import org.springframework.messaging.simp.SimpMessagingTemplate;
1918
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
2019
import org.springframework.stereotype.Controller;
2120

22-
import java.util.Map;
23-
import java.util.Set;
21+
import java.util.Optional;
2422

2523
@Controller
2624
@Slf4j
@@ -35,33 +33,93 @@ public void startGame(@Payload Player player, Message<?> message) {
3533
MessageHeaders headers = message.getHeaders();
3634
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
3735
Object sessionId = headers.get("simpSessionId");
38-
log.info("Player {} started game with session id {}", player.getLogin(), sessionId);
39-
log.error("session id is {}", sessionId);
40-
Game game = gameService.createGame(player, sessionId);
41-
this.messagingTemplate.convertAndSend("/topic/start-game/" + player.getLogin(), game);
36+
if (checkIfUserExists(player.getLogin())) {
37+
sendNotification("/topic/invalid-username/" + player.getLogin(),
38+
Response.builder().message("Username already exists. Select different username")
39+
.sessionid(sessionId).build());
40+
} else {
41+
log.info("Player {} started game with session id {}", player.getLogin(), sessionId);
42+
GameStorage.getInstance().addPlayer(sessionId, player.getLogin());
43+
Game game = gameService.createGame(player, sessionId);
44+
this.messagingTemplate.convertAndSend("/topic/start-game/" + player.getLogin(), game);
45+
}
4246
}
4347

4448
@MessageMapping("/play")
4549
public void playGame(@Payload GamePlay request) throws InvalidGameException, GameNotFoundException {
4650
log.info("playing the game {}", request);
4751
Game game = gameService.playGame(request);
48-
this.messagingTemplate.convertAndSend("/topic/game-progress/" + game.getGameId(), game);
52+
if (game.getWinner() != null) {
53+
String player1 = game.getPlayer1().getLogin();
54+
String player2 = game.getPlayer2().getLogin();
55+
this.messagingTemplate.convertAndSend("/topic/game-over/" + player1,
56+
Response.builder().message("Winner is " + game.getWinner()).build());
57+
this.messagingTemplate.convertAndSend("/topic/game-over/" + player2,
58+
Response.builder().message("Winner is " + game.getWinner()).build());
59+
} else {
60+
this.messagingTemplate.convertAndSend("/topic/game-progress/" + game.getPlayer1().getLogin(), game);
61+
this.messagingTemplate.convertAndSend("/topic/game-progress/" + game.getPlayer2().getLogin(), game);
62+
}
4963
}
5064

5165
@MessageMapping("/connect/random")
5266
public void connectRandomGame(@Payload Player player, Message<?> message) throws GameNotFoundException {
53-
log.info("Connecting player {} to random game", player.getLogin());
5467
MessageHeaders headers = message.getHeaders();
5568
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
5669
Object sessionId = headers.get("simpSessionId");
57-
log.info("Player {} started game with session id {}", player.getLogin(), sessionId);
58-
log.error("session id is {}", sessionId);
59-
this.messagingTemplate.convertAndSend("/topic/connect-random/" + player.getLogin(),
60-
gameService.connectToRandomGame(player, sessionId));
70+
if (checkIfUserExists(player.getLogin())) {
71+
sendNotification("/topic/invalid-username/" + player.getLogin(),
72+
Response.builder().message("Username already exists. Select different username").build());
73+
} else {
74+
log.info("connecting Player {} to random game", player.getLogin());
75+
Optional<Game> optionalGame = gameService.connectToRandomGame(player, sessionId);
76+
if (optionalGame.isPresent()) {
77+
sendGameNotification(player, sessionId.toString(), optionalGame.get());
78+
} else {
79+
sendNotification("/topic/game-not-found/" + player.getLogin(),
80+
Response.builder().message("No game found. Please create new game").build());
81+
}
82+
}
83+
}
84+
85+
@MessageMapping("/connect/gameid")
86+
public void connectToGameId(@Payload Player player, Message<?> message) {
87+
MessageHeaders headers = message.getHeaders();
88+
Object sessionId = headers.get("simpSessionId");
89+
if (checkIfUserExists(player.getLogin())) {
90+
sendNotification("/topic/invalid-username/" + player.getLogin(),
91+
Response.builder().message("Username already exists. Select different username").build());
92+
} else {
93+
log.info("User {} is trying to connect to game id {}", player.getLogin(), player.getGameId());
94+
Optional<Game> optionalGame = gameService.connectToGame(player, sessionId.toString());
95+
if (optionalGame.isPresent()) {
96+
log.info("Found the game with id {}", player.getGameId());
97+
sendGameNotification(player, sessionId.toString(), optionalGame.get());
98+
} else {
99+
sendNotification("/topic/game-not-found/" + player.getLogin(),
100+
Response.builder().message("No game found. Please create new game").build());
101+
}
102+
}
61103
}
62104

63105
@MessageMapping("/disconnect")
64106
public void disconnectPlayer(@Payload Player player) {
65107
log.error("Player {} disconnected", player);
108+
GameStorage.getInstance().removeGame(player.getLogin());
109+
GameStorage.getInstance().removePlayer(player.getLogin());
110+
}
111+
112+
private boolean checkIfUserExists(String username) {
113+
return GameStorage.getInstance().checkIfUserExists(username);
114+
}
115+
116+
private void sendNotification(String prefix, Object response) {
117+
this.messagingTemplate.convertAndSend(prefix, response);
118+
}
119+
120+
private void sendGameNotification(Player player, String sessionId, Game game) {
121+
GameStorage.getInstance().addPlayer(sessionId, player.getLogin());
122+
sendNotification("/topic/connect-random/player2/" + player.getLogin(), game);
123+
sendNotification("/topic/connect-random/player1/" + game.getPlayer1().getLogin(), game);
66124
}
67125
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/models/Game.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ public class Game {
99
private String gameId;
1010
Player player1;
1111
Player player2;
12-
String player1SessionId;
13-
String player2SessionId;
12+
Object player1SessionId;
13+
Object player2SessionId;
1414
private GameStatus gameStatus;
1515
private int[][] board;
1616
private int[] row;
1717
private int[] column;
18-
private Tick winner;
18+
private String winner;
19+
boolean isFirstPlayer;
20+
String currentTick;
1921
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/models/GamePlay.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ public class GamePlay {
88
private int x;
99
private int y;
1010
private String gameId;
11+
int turn;
1112
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/models/Player.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
public class Player {
1313

1414
private String login;
15+
private String gameId;
1516
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.rakeshv.tictactoe.models;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@NoArgsConstructor
11+
@Builder
12+
public class Response {
13+
private String message;
14+
private Object sessionid;
15+
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/models/Tick.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
@Getter
66
public enum Tick {
77
X(1),
8-
O(2)
8+
O(4)
99
;
1010

1111
private final int value;

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/services/GameEngine.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,28 @@ public Game checkWinner(Game game) {
1414
Tick winner = null;
1515

1616
for (int i = 0; i < 3; i++) {
17-
winner = row[i] == 3 ? Tick.X : row[i] == 6 ? Tick.O : null;
17+
winner = row[i] == 3 ? Tick.X : row[i] == 12 ? Tick.O : null;
1818
if (winner == null) {
19-
winner = column[i] == 3 ? Tick.X : column[i] == 6 ? Tick.O : null;
19+
winner = (column[i] == 3) ? Tick.X : (column[i] == 12 ? Tick.O : null);
20+
if (winner != null)
21+
break;
22+
} else {
23+
break;
2024
}
2125
}
2226
int[][] board = game.getBoard();
2327
int diagonal = board[0][0] + board[1][1] + board[2][2];
2428
if (winner == null) {
25-
winner = diagonal == 3 ? Tick.X : diagonal == 6 ? Tick.O : null;
29+
winner = diagonal == 3 ? Tick.X : diagonal == 12 ? Tick.O : null;
2630
}
2731
int antiDiagonal = board[0][2] + board[1][1] + board[2][0];
2832
if (winner == null) {
29-
winner = antiDiagonal == 3 ? Tick.X : antiDiagonal == 6 ? Tick.O : null;
33+
winner = antiDiagonal == 3 ? Tick.X : antiDiagonal == 12 ? Tick.O : null;
34+
}
35+
36+
if (winner != null) {
37+
game.setWinner(winner == Tick.X ? game.getPlayer1().getLogin() : game.getPlayer2().getLogin());
3038
}
31-
game.setWinner(winner);
3239
return game;
3340
}
3441
}

spring-boot-websockets/tictactoe/src/main/java/com/rakeshv/tictactoe/services/GameService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import com.rakeshv.tictactoe.models.GamePlay;
77
import com.rakeshv.tictactoe.models.Player;
88

9+
import java.util.Optional;
10+
911
public interface GameService {
1012
Game createGame(Player player, Object sessionId);
1113

12-
Game connectToGame(Player player2, String gameId) throws GameNotFoundException, InvalidGameException;
14+
Optional<Game> connectToGame(Player player2, String sessionId);
1315

14-
Game connectToRandomGame(Player player, Object sessionId) throws GameNotFoundException;
16+
Optional<Game> connectToRandomGame(Player player, Object sessionId) throws GameNotFoundException;
1517

1618
Game playGame(GamePlay gamePlay) throws GameNotFoundException, InvalidGameException;
1719
}

0 commit comments

Comments
 (0)