diff --git a/src/main/java/com/alterdekim/game/WebSocketConfig.java b/src/main/java/com/alterdekim/game/WebSocketConfig.java index ea29151..5ea80f9 100644 --- a/src/main/java/com/alterdekim/game/WebSocketConfig.java +++ b/src/main/java/com/alterdekim/game/WebSocketConfig.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.*; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; @Configuration @EnableWebSocket @@ -19,6 +20,7 @@ public class WebSocketConfig implements WebSocketConfigurer { public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(gamePool), "/websocket") .setAllowedOriginPatterns("*") + .addInterceptors(new HttpSessionHandshakeInterceptor()) .withSockJS(); } } \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/component/LongPoll.java b/src/main/java/com/alterdekim/game/component/LongPoll.java index 0148c28..f5516b1 100644 --- a/src/main/java/com/alterdekim/game/component/LongPoll.java +++ b/src/main/java/com/alterdekim/game/component/LongPoll.java @@ -1,5 +1,6 @@ package com.alterdekim.game.component; +import com.alterdekim.game.component.game.GamePool; import com.alterdekim.game.component.processors.ChatProcessor; import com.alterdekim.game.component.processors.FriendProcessor; import com.alterdekim.game.component.processors.Processor; @@ -52,6 +53,9 @@ public class LongPoll { @Autowired private FriendServiceImpl friendService; + @Autowired + private GamePool gamePool; + public LongPoll() { processors.addAll(Arrays.asList(new ChatProcessor(this), new FriendProcessor(this), new RoomProcessor(this))); } @@ -94,6 +98,7 @@ public class LongPoll { } private LongPollResult process(Long userId, LongPollConfig config) { + if( gamePool.containsPlayer(userId) ) config.getGameRedirect().add(new GameRedirect(gamePool.getGameIdByPlayerId(userId).get())); List result = new ArrayList<>(); result.add(new LongPollResultSingle<>(LongPollResultType.OnlineUsers, Arrays.asList(map.size()))); result.add(new LongPollResultSingle<>(LongPollResultType.Redirect, config.getGameRedirect())); diff --git a/src/main/java/com/alterdekim/game/component/game/BoardGUI.java b/src/main/java/com/alterdekim/game/component/game/BoardGUI.java new file mode 100644 index 0000000..a432229 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/BoardGUI.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class BoardGUI { + private List top; + private List right; + private List bottom; + private List left; + private List corners; +} diff --git a/src/main/java/com/alterdekim/game/component/game/BoardTile.java b/src/main/java/com/alterdekim/game/component/game/BoardTile.java new file mode 100644 index 0000000..abba437 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/BoardTile.java @@ -0,0 +1,20 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class BoardTile { + private String uid; + private Integer id; + private Integer cost; + private String stars; + private String img; + private String color; + private String ownerColor; +} diff --git a/src/main/java/com/alterdekim/game/component/game/CornerTile.java b/src/main/java/com/alterdekim/game/component/game/CornerTile.java new file mode 100644 index 0000000..a01dc00 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/CornerTile.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CornerTile { + private String img; +} diff --git a/src/main/java/com/alterdekim/game/component/game/GamePlayer.java b/src/main/java/com/alterdekim/game/component/game/GamePlayer.java new file mode 100644 index 0000000..9249fc7 --- /dev/null +++ b/src/main/java/com/alterdekim/game/component/game/GamePlayer.java @@ -0,0 +1,16 @@ +package com.alterdekim.game.component.game; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class GamePlayer { + private Long userId; + private String displayName; + private Integer money; +} diff --git a/src/main/java/com/alterdekim/game/component/game/GamePool.java b/src/main/java/com/alterdekim/game/component/game/GamePool.java index 9f5f557..379fc85 100644 --- a/src/main/java/com/alterdekim/game/component/game/GamePool.java +++ b/src/main/java/com/alterdekim/game/component/game/GamePool.java @@ -17,17 +17,16 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketSession; +import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.List; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @Slf4j @Component public class GamePool { - @Autowired - private LongPoll longPoll; - @Autowired private UserServiceImpl userService; @@ -47,8 +46,7 @@ public class GamePool { List players = roomPlayerService.findByRoomId(r.getId()); roomPlayerService.removeByRoomId(r.getId()); roomService.removeRoom(r.getId()); - games.put(r.getId(), new GameRoom(players)); - longPoll.notifyPlayers(r.getId(), players); + games.put(r.getId(), new GameRoom(players, userService)); }); } @@ -58,13 +56,38 @@ public class GamePool { BasicMessage pm = new ObjectMapper().readValue(message, BasicMessage.class); User u = userService.findById(pm.getUid()); if (u == null || !games.containsKey(pm.getRoomId())) return; - if (!Hash.sha256((u.getId() + u.getUsername() + u.getPassword() + pm.getRoomId()).getBytes()).equals(pm.getAccessToken())) + if (!Hash.sha256((u.getId() + u.getUsername() + u.getPassword() + pm.getRoomId()).getBytes()).equals(pm.getAccessToken())) { + session.close(); return; + } games.get(pm.getRoomId()).receiveMessage(pm, session); - } catch (JsonProcessingException | NoSuchAlgorithmException e) { + } catch (NoSuchAlgorithmException | IOException e) { log.error(e.getMessage(), e); } catch (StringIndexOutOfBoundsException e) { //log.error(e.getMessage(), e); } } + + public Boolean containsPlayer(Long userId) { + return games.keySet() + .stream() + .anyMatch(k -> games.get(k) + .getPlayers() + .stream() + .anyMatch(p -> p.getUserId().longValue() == userId.longValue() + ) + ); + } + + public Optional getGameIdByPlayerId(Long userId) { + return games.keySet() + .stream() + .filter(k -> games.get(k) + .getPlayers() + .stream() + .anyMatch(p -> p.getUserId().longValue() == userId.longValue() + ) + ) + .findFirst(); + } } diff --git a/src/main/java/com/alterdekim/game/component/game/GameRoom.java b/src/main/java/com/alterdekim/game/component/game/GameRoom.java index 42ee0de..4a83222 100644 --- a/src/main/java/com/alterdekim/game/component/game/GameRoom.java +++ b/src/main/java/com/alterdekim/game/component/game/GameRoom.java @@ -1,40 +1,108 @@ package com.alterdekim.game.component.game; import com.alterdekim.game.entities.RoomPlayer; +import com.alterdekim.game.service.UserServiceImpl; import com.alterdekim.game.websocket.message.BasicMessage; +import com.alterdekim.game.websocket.message.ResponseMessage; +import com.alterdekim.game.websocket.message.WebSocketMessageType; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Slf4j public class GameRoom { @Getter - private List players; + private List players; private ConcurrentHashMap socks; - public GameRoom(List players) { - this.players = players; + private UserServiceImpl userService; + + public GameRoom(List players, UserServiceImpl userService) { + this.userService = userService; + this.players = players.stream().map(p -> new GamePlayer(p.getUserId(), userService.findById(p.getUserId()).getDisplayName(), 0)).collect(Collectors.toList()); this.socks = new ConcurrentHashMap<>(); } public void receiveMessage(BasicMessage message, WebSocketSession session) { if(players.stream().noneMatch(p -> p.getUserId().longValue() == message.getUid().longValue())) return; socks.put(message.getUid(), session); - log.info("GOT MESSAGE " + message.getUid() + " " + message.getAccessToken() + " " + message.getBody()); - this.sendMessage(message.getUid()); + log.info("receiveMessage " + message.getType()); + parseMessage(message); } - private void sendMessage(Long userId) { + private void parseMessage(BasicMessage message) { + switch (message.getType()) { + case InfoRequest: + sendAllInfoRequest(message); + break; + } + } + + private void sendAllInfoRequest(BasicMessage message) { + try { + ObjectMapper om = new ObjectMapper(); + List top = new ArrayList<>(); + List right = new ArrayList<>(); + List bottom = new ArrayList<>(); + List left = new ArrayList<>(); + for( int i = 0; i < 9; i++ ) { + top.add(new BoardTile(UUID.randomUUID().toString(), 2 + i, 1000, "", "/static/images/7up.png", "ffffff", "f5f5f5")); + } + + for( int i = 0; i < 9; i++ ) { + right.add(new BoardTile(UUID.randomUUID().toString(), i, 1400, "", "/static/images/fanta.png", "bbbbbb", "f5f5f5")); + } + + for( int i = 0; i < 9; i++ ) { + bottom.add(new BoardTile(UUID.randomUUID().toString(), 2 + i, 1800, "", "/static/images/cola.png", "eeeeee", "f5f5f5")); + } + + for( int i = 10; i >= 2; i-- ) { + left.add(new BoardTile(UUID.randomUUID().toString(), i, 2200, "", "/static/images/beeline.png", "000000", "f5f5f5")); + } + + List corners = new ArrayList<>(); + corners.add(new CornerTile("/static/images/start.png")); + corners.add(new CornerTile("/static/images/injail.png")); + corners.add(new CornerTile("/static/images/parking.png")); + corners.add(new CornerTile("/static/images/gotojail.png")); + + BoardGUI boardGUI = new BoardGUI(top, right, bottom, left, corners); + sendMessage(message.getUid(), WebSocketMessageType.PlayersList, om.writeValueAsString(players)); + sendMessage(message.getUid(), WebSocketMessageType.BoardGUI, om.writeValueAsString(boardGUI)); + left.get(2).setCost(12345); + left.get(2).setImg("/static/images/fanta.png"); + left.get(2).setColor("bcbcbc"); + left.get(2).setStars("★★★"); + left.get(2).setOwnerColor("fffbbb"); + sendMessage(message.getUid(), WebSocketMessageType.ChangeBoardTileState, om.writeValueAsString(left.get(2))); + } catch (JsonProcessingException e) { + log.error(e.getMessage(), e); + } + } + + private void sendMessage(Long userId, WebSocketMessageType type, String message) { try { if (socks.get(userId).isOpen()) - socks.get(userId).sendMessage(new TextMessage("HEY!")); + socks.get(userId).sendMessage( + new TextMessage( + new ObjectMapper().writeValueAsString( + new ResponseMessage(type, message) + ) + ) + ); } catch (IOException e) { log.error(e.getMessage(), e); } diff --git a/src/main/java/com/alterdekim/game/controller/GameController.java b/src/main/java/com/alterdekim/game/controller/GameController.java index 68ee476..610282a 100644 --- a/src/main/java/com/alterdekim/game/controller/GameController.java +++ b/src/main/java/com/alterdekim/game/controller/GameController.java @@ -7,6 +7,7 @@ import com.alterdekim.game.util.AuthenticationUtil; import com.alterdekim.game.util.Hash; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/com/alterdekim/game/websocket/WebSocketHandler.java b/src/main/java/com/alterdekim/game/websocket/WebSocketHandler.java index d092e20..ff9597e 100644 --- a/src/main/java/com/alterdekim/game/websocket/WebSocketHandler.java +++ b/src/main/java/com/alterdekim/game/websocket/WebSocketHandler.java @@ -10,6 +10,7 @@ import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; + @Slf4j @AllArgsConstructor public class WebSocketHandler extends TextWebSocketHandler { @@ -21,10 +22,12 @@ public class WebSocketHandler extends TextWebSocketHandler { String receivedMessage = (String) message.getPayload(); gamePool.receiveMessage(receivedMessage, session); } + @Override public void afterConnectionEstablished(WebSocketSession session) { // Perform actions when a new WebSocket connection is established } + @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { // Perform actions when a WebSocket connection is closed diff --git a/src/main/java/com/alterdekim/game/websocket/message/BasicMessage.java b/src/main/java/com/alterdekim/game/websocket/message/BasicMessage.java index 4f46dbd..164ab1f 100644 --- a/src/main/java/com/alterdekim/game/websocket/message/BasicMessage.java +++ b/src/main/java/com/alterdekim/game/websocket/message/BasicMessage.java @@ -14,4 +14,5 @@ public class BasicMessage { private String accessToken; private Long uid; private String body; -} + private WebSocketMessageType type; +} \ No newline at end of file diff --git a/src/main/java/com/alterdekim/game/websocket/message/IWebSocketMessage.java b/src/main/java/com/alterdekim/game/websocket/message/IWebSocketMessage.java deleted file mode 100644 index 71e7ebb..0000000 --- a/src/main/java/com/alterdekim/game/websocket/message/IWebSocketMessage.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.alterdekim.game.websocket.message; - -public interface IWebSocketMessage { -} diff --git a/src/main/java/com/alterdekim/game/websocket/message/ResponseMessage.java b/src/main/java/com/alterdekim/game/websocket/message/ResponseMessage.java new file mode 100644 index 0000000..a48f3f3 --- /dev/null +++ b/src/main/java/com/alterdekim/game/websocket/message/ResponseMessage.java @@ -0,0 +1,14 @@ +package com.alterdekim.game.websocket.message; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class ResponseMessage { + private WebSocketMessageType type; + private String body; +} diff --git a/src/main/java/com/alterdekim/game/websocket/message/WebSocketMessageType.java b/src/main/java/com/alterdekim/game/websocket/message/WebSocketMessageType.java new file mode 100644 index 0000000..49f96b8 --- /dev/null +++ b/src/main/java/com/alterdekim/game/websocket/message/WebSocketMessageType.java @@ -0,0 +1,8 @@ +package com.alterdekim.game.websocket.message; + +public enum WebSocketMessageType { + PlayersList, + BoardGUI, + InfoRequest, + ChangeBoardTileState +} diff --git a/src/main/resources/static/css/game.css b/src/main/resources/static/css/game.css index 09f9198..9aa6ee1 100644 --- a/src/main/resources/static/css/game.css +++ b/src/main/resources/static/css/game.css @@ -1,6 +1,6 @@ body { font-size: 14px; - font-family: 'Open Sans',tahoma,arial,sans-serif; + font-family: 'Montserrat', sans-serif; } ._mbtn { diff --git a/src/main/resources/static/javascript/game.js b/src/main/resources/static/javascript/game.js index 3a7aeea..332fd0c 100644 --- a/src/main/resources/static/javascript/game.js +++ b/src/main/resources/static/javascript/game.js @@ -1,25 +1,109 @@ -const player_html = '

'; - -var stompClient = null; +var socket = null; function disconnect() { - if (stompClient !== null) { - stompClient.disconnect(); + if (socket !== null) { + socket.close(); } console.log("Disconnected"); } -function sendMessage(message) { - stompClient.send("/", {}, JSON.stringify({'body': JSON.stringify(message), 'accessToken': $("api-tag").attr("data-access-token"), 'uid': $("api-tag").attr("data-uid"), 'roomId': $("api-tag").attr("data-room-id")})); +function sendMessage(message, type) { + socket.send(JSON.stringify({ + 'type': type, + 'body': JSON.stringify(message), + 'accessToken': $("api-tag").attr("data-access-token"), + 'uid': $("api-tag").attr("data-uid"), + 'roomId': $("api-tag").attr("data-room-id") + })); } $(document).ready(function() { - var socket = new SockJS('/websocket'); - stompClient = Stomp.over(socket); - stompClient.connect({}, function (frame) { - console.log('Connected: ' + frame); - stompClient.subscribe('/', function (message) { - //showMessage(JSON.parse(message.body).content); - }); - }); -}); \ No newline at end of file + socket = new SockJS('/websocket'); + + socket.onopen = function() { + console.log('open'); + sendMessage({}, 'InfoRequest'); + }; + + socket.onmessage = function(e) { + console.log('message', e.data); + showMessage(JSON.parse(e.data)); + }; + + socket.onclose = function() { + console.log('close'); + window.location.assign("/games"); + }; +}); + +function showMessage(message) { + console.log('GOT IT'); + switch(message.type) { + case 'PlayersList': + parsePlayersList(JSON.parse(message.body)); + break; + case 'BoardGUI': + parseBoardGUI(JSON.parse(message.body)); + break; + case 'ChangeBoardTileState': + changeBoardState(JSON.parse(message.body)); + break; + } +} + +function changeBoardState(body) { + let board_tile = $(".board_field[data-fid='" + body.uid + "']"); + $(board_tile).find(".cost").css("background-color", "#"+body.color); + $(board_tile).find(".cost").html(body.cost); + $(board_tile).find(".lcost").css("background-color", "#"+body.color); + $(board_tile).find(".lcost").html(body.cost); + $(board_tile).find("._star").html(body.stars); + $(board_tile).find("img").attr("src", body.img); + $(board_tile).find(".fv").css("background-color", "#"+body.ownerColor); + $(board_tile).find(".fh").css("background-color", "#"+body.ownerColor); +} + +function parseBoardGUI(body) { + let t_html = ''; + for( let i = 0; i < body.top.length; i++ ) { + let board_tile = body.top[i]; + t_html += '
'+board_tile.cost+'
'+board_tile.stars+'
'; + } + $("#top_board_tiles").replaceWith(t_html); + + let r_html = ''; + for( let i = 0; i < body.right.length; i++ ) { + let board_tile = body.right[i]; + r_html += '
'+board_tile.cost+'
'+board_tile.stars+'
'; + } + $("#right_board_tiles").replaceWith(r_html); + + let b_html = ''; + for( let i = 0; i < body.bottom.length; i++ ) { + let board_tile = body.bottom[i]; + b_html += '
'+board_tile.stars+'
'+board_tile.cost+'
'; + } + $("#bottom_board_tiles").replaceWith(b_html); + + let l_html = ''; + for( let i = 0; i < body.left.length; i++ ) { + let board_tile = body.left[i]; + l_html += '
'+board_tile.cost+'
'+board_tile.stars+'
'; + } + $("#left_board_tiles").replaceWith(l_html); + + let corners = $(".corner"); + for(let i = 0; i < body.corners.length; i++ ) { + $(corners[i]).find("img").attr("src", body.corners[i].img); + } + resizeTable(); +} + +function parsePlayersList(body) { + let p_html = ''; + for( let i = 0; i < body.length; i++ ) { + let player = body[i]; + p_html += '

'+player.displayName+'

'+player.money+'

'; + } + $(".players").append(p_html); +} \ No newline at end of file diff --git a/src/main/resources/templates/game.html b/src/main/resources/templates/game.html index c69c8ce..522ea50 100644 --- a/src/main/resources/templates/game.html +++ b/src/main/resources/templates/game.html @@ -5,9 +5,10 @@ - + +