From bc0ec2693f5faa09f6687b7804af665352602521 Mon Sep 17 00:00:00 2001 From: evanchooly Date: Mon, 28 Feb 2011 23:09:05 -0500 Subject: [PATCH] server side logic done --- .gitignore | 1 + core/pom.xml | 19 ++++ pom.xml | 92 ++++++++++++++++++ war/pom.xml | 37 +++++++ .../antwerkz/wsdemos/war/ActionHandler.java | 7 ++ .../java/com/antwerkz/wsdemos/war/Piece.java | 31 ++++++ .../java/com/antwerkz/wsdemos/war/Player.java | 74 ++++++++++++++ .../java/com/antwerkz/wsdemos/war/Result.java | 11 +++ .../java/com/antwerkz/wsdemos/war/Type.java | 51 ++++++++++ .../com/antwerkz/wsdemos/war/WarGame.java | 97 +++++++++++++++++++ .../com/antwerkz/wsdemos/war/WarGameTest.java | 82 ++++++++++++++++ 11 files changed, 502 insertions(+) create mode 100644 .gitignore create mode 100644 core/pom.xml create mode 100644 pom.xml create mode 100644 war/pom.xml create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/ActionHandler.java create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/Piece.java create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/Player.java create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/Result.java create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/Type.java create mode 100644 war/src/main/java/com/antwerkz/wsdemos/war/WarGame.java create mode 100644 war/src/test/java/com/antwerkz/wsdemos/war/WarGameTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9650ed --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +idea-files diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..47060d6 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + + com.antwerkz.wsdemos + parent + 1.0 + .. + + + com.antwerkz.wsdemos + core + 1.0 + WebSocket Demo core libraries + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b6d8c18 --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + com.antwerkz.wsdemos + parent + 1.0 + pom + + Websocket Demos Parent + + + core + war + + + + package + + + + maven-compiler-plugin + + 1.6 + 1.6 + + + + + org.apache.maven.plugins + maven-release-plugin + 2.1 + + + + + + + + glassfish + glassfish + http://download.java.net/maven/glassfish + + + repository.jboss.org + JBoss Maven Repository + http://repository.jboss.org/maven2 + + + antwerkz + http://antwerkz.com/ + + + maven.annotation.rg + http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo + + + jfrog + http://www.jfrog.org/artifactory/plugins-releases + + + + + + + com.sun.grizzly + grizzly-websockets + ${grizzly.version} + + + com.sun.grizzly + grizzly-http-servlet + ${grizzly.version} + + + javax.servlet + servlet-api + 2.5 + + + org.testng + testng + 5.14.2 + + + + + + 1.9.32 + + diff --git a/war/pom.xml b/war/pom.xml new file mode 100644 index 0000000..c92ff70 --- /dev/null +++ b/war/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + com.antwerkz.wsdemos + parent + 1.0 + + + com.antwerkz.wsdemos + war + 1.0 + + War! + + + + com.sun.grizzly + grizzly-websockets + + + com.sun.grizzly + grizzly-http-servlet + + + javax.servlet + servlet-api + + + org.testng + testng + + + \ No newline at end of file diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/ActionHandler.java b/war/src/main/java/com/antwerkz/wsdemos/war/ActionHandler.java new file mode 100644 index 0000000..f9b9691 --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/ActionHandler.java @@ -0,0 +1,7 @@ +package com.antwerkz.wsdemos.war; + +public class ActionHandler { + public Result strike() { + return Result.MISS; + } +} diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/Piece.java b/war/src/main/java/com/antwerkz/wsdemos/war/Piece.java new file mode 100644 index 0000000..8375c11 --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/Piece.java @@ -0,0 +1,31 @@ +package com.antwerkz.wsdemos.war; + +public class Piece { + private Type type; + private boolean[][] hits; + + public Piece(final Type type) { + this.type = type; + type.createHits(this); + } + + public void setHits(final boolean[][] values) { + hits = values; + } + + public boolean isDestroyed() { + boolean destroyed = true; + for (boolean[] hit : hits) { + for (boolean b : hit) { + destroyed &= b; + } + } + + return destroyed; + } + + public Result strike(final int x, final int y) { + hits[x][y] = true; + return Result.HIT; + } +} diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/Player.java b/war/src/main/java/com/antwerkz/wsdemos/war/Player.java new file mode 100644 index 0000000..79358b5 --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/Player.java @@ -0,0 +1,74 @@ +package com.antwerkz.wsdemos.war; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.grizzly.websockets.WebSocket; + +public class Player { + private ActionHandler[][] handlers; + private String name; + private boolean ready = false; + private List pieces = new ArrayList(); + private WebSocket socket; + + public Player(final WebSocket socket) { + this.socket = socket; + handlers = new ActionHandler[WarGame.DIMENSION][]; + for (int i = 0; i < handlers.length; i++) { + handlers[i] = new ActionHandler[WarGame.DIMENSION]; + for (int j = 0; j < handlers.length; j++) { + handlers[i][j] = new ActionHandler(); + } + } + } + + public WebSocket getSocket() { + return socket; + } + + public boolean isReady() { + return ready; + } + + public void setReady(final boolean ready) { + this.ready = ready; + } + + public boolean isDefeated() { + boolean defeated = true; + for (Piece piece : pieces) { + defeated &= piece.isDestroyed(); + } + return defeated; + } + + public Result place(Type type, final int mapX, final int mapY) { + final int[] dimensions = type.dimensions(); + final Piece piece = new Piece(type); + pieces.add(piece); + for (int xIndex = 0; xIndex < dimensions[0]; xIndex++) { + for (int yIndex = 0; yIndex < dimensions[1]; yIndex++) { + final int x = mapX + xIndex; + final int y = mapY + yIndex; + handlers[xIndex][yIndex] = new ActionHandler() { + @Override + public Result strike() { + piece.strike(x, y); + return isDefeated() ? Result.VICTORY : Result.HIT; + } + }; + } + } + ready = pieces.size() == Type.values().length; + return ready ? Result.READY : Result.PLACED; + } + + public Result strike(int x, int y) { + if (x < 0 || y < 0 || x >= WarGame.DIMENSION || y >= WarGame.DIMENSION) { + return Result.OFFMAP; + } + return handlers[x][y].strike(); + } + +} \ No newline at end of file diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/Result.java b/war/src/main/java/com/antwerkz/wsdemos/war/Result.java new file mode 100644 index 0000000..2f77447 --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/Result.java @@ -0,0 +1,11 @@ +package com.antwerkz.wsdemos.war; + +public enum Result { + HIT, + MISS, + OCCUPIED, + PLACED, + READY, + VICTORY, + OFFMAP,; +} diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/Type.java b/war/src/main/java/com/antwerkz/wsdemos/war/Type.java new file mode 100644 index 0000000..43b0b0e --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/Type.java @@ -0,0 +1,51 @@ +package com.antwerkz.wsdemos.war; + +public enum Type { + AA { + @Override + public int[] dimensions() { + return new int[] {1, 2}; + } + }, + TANK { + @Override + public int[] dimensions() { + return new int[] {2, 2}; + } + }, + PLATOON{ + @Override + public int[] dimensions() { + return new int[] {2, 4}; + } + + }, + HQ{ + @Override + public int[] dimensions() { + return new int[] {3, 3}; + } + + }; + + boolean[][] create(int width, int height) { + boolean spaces[][] = new boolean[width][]; + for (int i = 0; i < spaces.length; i++) { + spaces[i] = new boolean[height]; + } + + return spaces; + } + + public void createHits(final Piece piece) { + final int[] dimensions = dimensions(); + piece.setHits(create(dimensions[0], dimensions[1])); + } + + public abstract int[] dimensions(); + + @Override + public String toString() { + return name(); + } +} \ No newline at end of file diff --git a/war/src/main/java/com/antwerkz/wsdemos/war/WarGame.java b/war/src/main/java/com/antwerkz/wsdemos/war/WarGame.java new file mode 100644 index 0000000..769d76a --- /dev/null +++ b/war/src/main/java/com/antwerkz/wsdemos/war/WarGame.java @@ -0,0 +1,97 @@ +package com.antwerkz.wsdemos.war; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import com.sun.grizzly.tcp.Request; +import com.sun.grizzly.websockets.DataFrame; +import com.sun.grizzly.websockets.WebSocket; +import com.sun.grizzly.websockets.WebSocketApplication; + +public class WarGame extends WebSocketApplication { + public static final int DIMENSION = 64; + Map players = new ConcurrentHashMap(); + + public Result strike(WebSocket player, int x, int y) { + return players.get(player).strike(x, y); + } + + @Override + public void onConnect(final WebSocket socket) { + if (players.put(socket, new Player(socket)) == null) { + super.onConnect(socket); + } else { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage(), e); + } + } + } + + @Override + public void onMessage(final WebSocket socket, final DataFrame frame) throws IOException { + final String[] payload = frame.getTextPayload().split(":"); + int index = 0; + Result result; + final String operation = payload[index++]; + Type type = null; + final int x; + final int y; + if ("place".equals(operation)) { + type = Type.valueOf(payload[index++]); + x = Integer.parseInt(payload[index++]); + y = Integer.parseInt(payload[index++]); + result = getPlayer(socket) + .place(type, x, y); + } else /*if ("strike".equals(operation)) */ { + x = Integer.parseInt(payload[index++]); + y = Integer.parseInt(payload[index++]); + result = getOpponent(socket) + .strike(x, y); + } + switch (result) { + case PLACED: + final int[] dimensions = type.dimensions(); + socket.send(String.format("placed:%s:%s:%s:%s", x, y, dimensions[0], dimensions[1])); + break; + case VICTORY: + socket.send("you win"); + getOpponent(socket).getSocket().send("you lose"); + break; + case HIT: + socket.send(String.format("boom:%s:%s", x, y)); + getOpponent(socket).getSocket().send(String.format("ouch:%s:%s", x, y)); + break; + case MISS: + socket.send(String.format("whiff:%s:%s", x, y)); + getOpponent(socket).getSocket().send(String.format("whew:%s:%s", x, y)); + break; + } + } + + private Player getPlayer(final WebSocket socket) { + final Player player = players.get(socket); + if (player == null) { + throw new RuntimeException("Can't find the player. What's wrong with me?"); + } + return player; + } + + private Player getOpponent(final WebSocket socket) { + for (Entry webSocket : players.entrySet()) { + if (!webSocket.getKey().equals(socket)) { + return webSocket.getValue(); + } + } + throw new RuntimeException("Can't find the opponent. What's wrong with me?"); + } + + @Override + public boolean isApplicationRequest(final Request request) { + return request.requestURI().equals("/war"); + } +} diff --git a/war/src/test/java/com/antwerkz/wsdemos/war/WarGameTest.java b/war/src/test/java/com/antwerkz/wsdemos/war/WarGameTest.java new file mode 100644 index 0000000..639f5d6 --- /dev/null +++ b/war/src/test/java/com/antwerkz/wsdemos/war/WarGameTest.java @@ -0,0 +1,82 @@ +package com.antwerkz.wsdemos.war; + +import java.io.IOException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import com.sun.grizzly.arp.DefaultAsyncHandler; +import com.sun.grizzly.http.SelectorThread; +import com.sun.grizzly.tcp.StaticResourcesAdapter; +import com.sun.grizzly.util.Utils; +import com.sun.grizzly.websockets.ClientWebSocket; +import com.sun.grizzly.websockets.DataFrame; +import com.sun.grizzly.websockets.WebSocket; +import com.sun.grizzly.websockets.WebSocketAdapter; +import com.sun.grizzly.websockets.WebSocketAsyncFilter; +import com.sun.grizzly.websockets.WebSocketEngine; +import org.testng.Assert; +import org.testng.annotations.Test; + +@Test +public class WarGameTest { + public void duel() throws IOException, InstantiationException, InterruptedException { + final SelectorThread st = createSelectorThread(8080); + final WarGame game = new WarGame(); + WebSocketEngine.getEngine().register(game); + try { + final WarAdapter adapter1 = new WarAdapter(); + final WarAdapter adapter2 = new WarAdapter(); + + ClientWebSocket client1 = new ClientWebSocket("ws://localhost:8080/war", adapter1); + client1.send(String.format("place:%s:%s:%s", Type.AA, 20, 20)); + Assert.assertTrue(adapter1.getQueue().poll(10, TimeUnit.SECONDS).startsWith("placed")); + + ClientWebSocket client2 = new ClientWebSocket("ws://localhost:8080/war", adapter2); + client2.send(String.format("place:%s:%s:%s", Type.AA, 0, 0)); + Assert.assertTrue(adapter2.getQueue().poll(10, TimeUnit.SECONDS).startsWith("placed")); + + client1.send("strike:0:0"); + Assert.assertTrue(adapter1.getQueue().poll(10, TimeUnit.SECONDS).startsWith("boom")); + client1.send("strike:10:10"); + Assert.assertTrue(adapter1.getQueue().poll(10, TimeUnit.SECONDS).startsWith("whiff")); + client1.send("strike:0:1"); + Assert.assertEquals(adapter1.getQueue().poll(10, TimeUnit.SECONDS), "you win"); + System.out.println("adapter2.getQueue() = " + adapter2.getQueue()); + } finally { + st.stopEndpoint(); + } + } + + private SelectorThread createSelectorThread(int port) + throws IOException, InstantiationException { + SelectorThread st = new SelectorThread(); + + st.setSsBackLog(8192); + st.setCoreThreads(2); + st.setMaxThreads(2); + st.setPort(port); + st.setDisplayConfiguration(Utils.VERBOSE_TESTS); + st.setAdapter(new StaticResourcesAdapter()); + st.setAsyncHandler(new DefaultAsyncHandler()); + st.setEnableAsyncExecution(true); + st.getAsyncHandler().addAsyncFilter(new WebSocketAsyncFilter()); + st.setTcpNoDelay(true); + st.listen(); + + return st; + } + + private static class WarAdapter extends WebSocketAdapter { + private BlockingQueue queue = new ArrayBlockingQueue(5, true); + @Override + public void onMessage(final WebSocket socket, final DataFrame frame) throws IOException { + System.out.println("queuing response: " + frame); + queue.add(frame.getTextPayload()); + } + + public BlockingQueue getQueue() { + return queue; + } + } +}