/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ch.rasc.wampspring.demo.various.snake; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import ch.rasc.wampspring.EventMessenger; import ch.rasc.wampspring.annotation.WampCallListener; import ch.rasc.wampspring.annotation.WampSubscribeListener; import ch.rasc.wampspring.annotation.WampUnsubscribeListener; /** * Sets up the timer for the multi-player snake game WebSocket example. */ @Service public class SnakeService { private final ConcurrentHashMap<Integer, Snake> snakes = new ConcurrentHashMap<>(); private final EventMessenger eventMessenger; private Timer gameTimer; private final SnakeId currentSnakeId; @Autowired public SnakeService(SnakeId currentSnakeId, EventMessenger eventMessenger) { this.eventMessenger = eventMessenger; this.currentSnakeId = currentSnakeId; } @WampSubscribeListener(value = "snake", replyTo = "snake") public synchronized SnakeMessage addSnake() { Snake newSnake = new Snake(this.currentSnakeId); if (this.snakes.isEmpty()) { startTimer(); } this.snakes.put(newSnake.getId(), newSnake); return SnakeMessage.createJoinMessage(createJoinData()); } @WampUnsubscribeListener(value = "snake", replyTo = "snake") public synchronized SnakeMessage removeSnake() { try { Integer snakeId = this.currentSnakeId.getId(); if (snakeId != null) { this.snakes.remove(snakeId); if (this.snakes.isEmpty()) { if (this.gameTimer != null) { this.gameTimer.cancel(); this.gameTimer = null; } } return SnakeMessage.createLeaveMessage(snakeId); } } catch (BeanCreationException e) { // A websocket session was destroyed from another example // The other example does not have the scope snakeId in it's session // so a call to currentSnakeId.getId() throws an error } return null; } public void startTimer() { this.gameTimer = new Timer(SnakeService.class.getSimpleName() + " Timer"); this.gameTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { tick(); } }, 100, 100); } public void tick() { Collection<Snake> allSnakes = getSnakes(); List<Map<String, Object>> updateData = new ArrayList<>(); for (Snake snake : allSnakes) { snake.update(allSnakes, this.eventMessenger); Map<String, Object> locationsData = snake.getLocationsData(); if (locationsData != null) { updateData.add(locationsData); } } if (!updateData.isEmpty()) { this.eventMessenger.sendToAll("snake", SnakeMessage.createUpdateMessage(updateData)); } } private Collection<Snake> getSnakes() { return Collections.unmodifiableCollection(this.snakes.values()); } public List<Map<String, Object>> createJoinData() { List<Map<String, Object>> result = new ArrayList<>(); for (Snake snake : getSnakes()) { Map<String, Object> es = new HashMap<>(); es.put("id", snake.getId()); es.put("color", snake.getHexColor()); result.add(es); } return result; } @WampCallListener public void changeDirection(String message) { Snake snake = this.snakes.get(this.currentSnakeId.getId()); if (snake != null) { if ("west".equals(message)) { snake.setDirection(Direction.WEST); } else if ("north".equals(message)) { snake.setDirection(Direction.NORTH); } else if ("east".equals(message)) { snake.setDirection(Direction.EAST); } else if ("south".equals(message)) { snake.setDirection(Direction.SOUTH); } } } }