Index: webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java (revision 1297533) +++ webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java (revision ) @@ -101,6 +101,7 @@ CharBuffer response = CharBuffer.wrap(message); connection.getWsOutbound().writeTextMessage(response); } catch (IOException ignore) { + // Ignore } } } @@ -196,13 +197,13 @@ @Override protected void onTextMessage(CharBuffer charBuffer) throws IOException { String message = charBuffer.toString(); - if ("left".equals(message)) { + if ("west".equals(message)) { snake.setDirection(Direction.WEST); - } else if ("up".equals(message)) { + } else if ("north".equals(message)) { snake.setDirection(Direction.NORTH); - } else if ("right".equals(message)) { + } else if ("east".equals(message)) { snake.setDirection(Direction.EAST); - } else if ("down".equals(message)) { + } else if ("south".equals(message)) { snake.setDirection(Direction.SOUTH); } } Index: webapps/examples/websocket/snake.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- webapps/examples/websocket/snake.html (revision 1297533) +++ webapps/examples/websocket/snake.html (revision ) @@ -67,6 +67,7 @@ Game.socket = null; Game.nextFrame = null; Game.interval = null; + Game.direction = 'none'; Game.gridSize = 10; function Snake() { @@ -94,24 +95,29 @@ if (code > 36 && code < 41) { switch (code) { case 37: - Game.socket.send('left'); + if (Game.direction != 'east') Game.setDirection('west'); break; case 38: - Game.socket.send('up'); + if (Game.direction != 'south') Game.setDirection('north'); break; case 39: - Game.socket.send('right'); + if (Game.direction != 'west') Game.setDirection('east'); break; case 40: - Game.socket.send('down'); + if (Game.direction != 'north') Game.setDirection('south'); break; } - Console.log('Sent: keyCode ' + code); } }, false); Game.connect('ws://' + window.location.host + '/examples/websocket/snake'); }; + Game.setDirection = function(direction) { + Game.direction = direction; + Game.socket.send(direction); + Console.log('Sent: Direction ' + direction); + }; + Game.startGameLoop = function() { if (window.webkitRequestAnimationFrame) { Game.nextFrame = function () { @@ -156,7 +162,8 @@ Game.removeSnake = function(id) { Game.entities[id] = null; - delete Game.entities[id]; // force GC + // Force GC. + delete Game.entities[id]; }; Game.run = (function() { @@ -184,12 +191,13 @@ } Game.socket.onopen = function () { - // Socket initialised.. start the game loop + // Socket open.. start the game loop. Console.log('Info: WebSocket connection opened.'); Console.log('Info: Press an arrow key to begin.'); Game.startGameLoop(); setInterval(function() { - Game.socket.send('ping'); // prevent server read timeout + // Prevent server read timeout. + Game.socket.send('ping'); }, 5000); }; @@ -199,7 +207,8 @@ }; Game.socket.onmessage = function (message) { - var packet = eval('(' + message.data + ')'); // Consider using json lib to parse data. + // _Potential_ security hole, consider using json lib to parse data in production. + var packet = eval('(' + message.data + ')'); switch (packet.type) { case 'update': for (var i = 0; i < packet.data.length; i++) { @@ -216,6 +225,7 @@ break; case 'dead': Console.log('Info: Your snake is dead, bad luck!'); + Game.direction = 'none'; break; case 'kill': Console.log('Info: Head shot!'); Index: webapps/examples/WEB-INF/classes/websocket/snake/Snake.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- webapps/examples/WEB-INF/classes/websocket/snake/Snake.java (revision 1297533) +++ webapps/examples/WEB-INF/classes/websocket/snake/Snake.java (revision ) @@ -16,24 +16,25 @@ */ package websocket.snake; +import org.apache.catalina.websocket.WsOutbound; + import java.io.IOException; import java.nio.CharBuffer; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; -import java.util.Iterator; -import org.apache.catalina.websocket.WsOutbound; - public class Snake { - private static final int DEFAULT_LENGTH = 6; + private static final int DEFAULT_LENGTH = 5; private final int id; private final WsOutbound outbound; private Direction direction; - private Deque locations = new ArrayDeque(); + private int length = DEFAULT_LENGTH; + private Location head; + private Deque tail = new ArrayDeque(); private String hexColor; public Snake(int id, WsOutbound outbound) { @@ -45,14 +46,12 @@ private void resetState() { this.direction = Direction.NONE; - this.locations.clear(); - Location startLocation = SnakeWebSocketServlet.getRandomLocation(); - for (int i = 0; i < DEFAULT_LENGTH; i++) { - locations.add(startLocation); + this.head = SnakeWebSocketServlet.getRandomLocation(); + this.tail.clear(); + this.length = DEFAULT_LENGTH; - } + } - } - private void kill() { + private synchronized void kill() { resetState(); try { CharBuffer response = CharBuffer.wrap("{'type': 'dead'}"); @@ -62,8 +61,8 @@ } } - private void reward() { - grow(); + private synchronized void reward() { + length++; try { CharBuffer response = CharBuffer.wrap("{'type': 'kill'}"); outbound.writeTextMessage(response); @@ -73,8 +72,7 @@ } public synchronized void update(Collection snakes) { - Location firstLocation = locations.getFirst(); - Location nextLocation = firstLocation.getAdjacentLocation(direction); + Location nextLocation = head.getAdjacentLocation(direction); if (nextLocation.x >= SnakeWebSocketServlet.PLAYFIELD_WIDTH) { nextLocation.x = 0; } @@ -87,43 +85,41 @@ if (nextLocation.y < 0) { nextLocation.y = SnakeWebSocketServlet.PLAYFIELD_HEIGHT; } - locations.addFirst(nextLocation); - locations.removeLast(); + if (direction != Direction.NONE) { + tail.addFirst(head); + if (tail.size() > length) { + tail.removeLast(); + } + head = nextLocation; + } for (Snake snake : snakes) { - if (snake.getId() != getId() && - colliding(snake.getHeadLocation())) { - snake.kill(); - reward(); + if (snake.getTail().contains(head)) { + kill(); + if (id != snake.id) { + snake.reward(); - } - } - } + } + } + } - - private void grow() { - Location lastLocation = locations.getLast(); - Location newLocation = new Location(lastLocation.x, lastLocation.y); - locations.add(newLocation); } - private boolean colliding(Location location) { - return direction != Direction.NONE && locations.contains(location); + public synchronized Collection getTail() { + return tail; } - public void setDirection(Direction direction) { + public synchronized void setDirection(Direction direction) { this.direction = direction; } public synchronized String getLocationsJson() { StringBuilder sb = new StringBuilder(); - for (Iterator iterator = locations.iterator(); - iterator.hasNext();) { - Location location = iterator.next(); - sb.append(String.format("{x: %d, y: %d}", + sb.append(String.format("{x: %d, y: %d}", - Integer.valueOf(location.x), Integer.valueOf(location.y))); - if (iterator.hasNext()) { + Integer.valueOf(head.x), Integer.valueOf(head.y))); + for (Location location : tail) { - sb.append(','); + sb.append(','); + sb.append(String.format("{x: %d, y: %d}", + Integer.valueOf(location.x), Integer.valueOf(location.y))); - } + } - } return String.format("{'id':%d,'body':[%s]}", Integer.valueOf(id), sb.toString()); } @@ -134,9 +130,5 @@ public String getHexColor() { return hexColor; - } - - public synchronized Location getHeadLocation() { - return locations.getFirst(); } }