diff --git a/README.md b/README.md index 56a31b0..df719e2 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ This project contains a TCP Game Server, a Java Swing Client, and a Spring Boot ## Structure -- `server/`: Java TCP Server (Port 1870). With Anticheat (Server-side validation). -- `client/`: Standalone Java Swing Client. Connects to `dokploy.lona-development.org:1870`. +- `server/`: Java TCP Server (Port 1870). Includes Anticheat logic (validates turns, moves, wins). +- `client/`: Standalone Java Swing Client. Defaults to `dokploy.lona-development.org:1870` but allows custom host/port. - `web-client/`: Spring Boot Web Application. Acts as a proxy to the TCP Server via WebSocket. ## Running with Docker Compose @@ -16,28 +16,44 @@ To start the Server and Web Client: docker-compose up --build ``` -- **Server** runs on port `1870`. +- **Server** runs on port `1870` (exposed). - **Web Client** runs on `http://localhost:8080`. -## Building the Java Client +## Building and Running the Standalone Java Client -To build the standalone Java client: +Go to the `client` directory and run the build script: ```bash -./build_client.sh +cd client +chmod +x build.sh +./build.sh ``` -To run it: +### Usage + +By default, the client attempts to connect to `dokploy.lona-development.org:1870`. ```bash -java -jar client/target/client-1.0-SNAPSHOT.jar +java -jar target/client-1.0-SNAPSHOT.jar ``` -**Note:** The Java client is hardcoded to connect to `dokploy.lona-development.org`. Ensure this domain resolves to your server IP (or adds `127.0.0.1 dokploy.lona-development.org` to `/etc/hosts` for local testing). +**Custom Host/Port (Local Testing):** +If you want to connect to a local server (e.g., running via Docker Compose), pass the host and port as arguments: -## Game Protocol +```bash +java -jar target/client-1.0-SNAPSHOT.jar localhost 1870 +``` -- **CREATE**: Starts a new game. Server returns a 6-character code. -- **JOIN <CODE>**: Joins an existing game. -- **MOVE <ROW> <COL>**: Places your symbol. -- **SURRENDER**: Forfeits the game. +## Game Protocol (TCP 1870) + +The server creates game sessions identified by a unique 6-character code. + +- **CREATE**: Starts a new game. Server returns `GAME_CREATED `. +- **JOIN <CODE>**: Joins an existing game. Server returns `JOIN_SUCCESS` or `ERROR`. +- **MOVE <ROW> <COL>**: Places your symbol (0-2). Validated by server. +- **SURRENDER**: Forfeits the game immediately. + +## Troubleshooting + +- **Server Connection Refused?** Ensure the server container is running and port 1870 is mapped. +- **Client stuck connecting?** Verify the hostname is reachable. Use `localhost` if running locally. diff --git a/client/build.sh b/client/build.sh index 0486014..d95de8b 100755 --- a/client/build.sh +++ b/client/build.sh @@ -5,8 +5,11 @@ mvn clean package if [ $? -eq 0 ]; then echo "--------------------------------------------------" echo "Build Successful!" - echo "You can run the client with:" + echo "You can run the client with default settings (dokploy.lona-development.org):" echo "java -jar target/client-1.0-SNAPSHOT.jar" + echo "" + echo "Or specify a custom host and port (e.g. for local testing):" + echo "java -jar target/client-1.0-SNAPSHOT.jar localhost 1870" echo "--------------------------------------------------" else echo "Build Failed!" diff --git a/client/src/main/java/com/lona/tictactoe/client/Main.java b/client/src/main/java/com/lona/tictactoe/client/Main.java index c91af14..b04f795 100644 --- a/client/src/main/java/com/lona/tictactoe/client/Main.java +++ b/client/src/main/java/com/lona/tictactoe/client/Main.java @@ -23,16 +23,58 @@ public class Main extends JFrame { private boolean myTurn = false; private char mySymbol = ' '; // assigned by server implied turn - public Main() { + public static void main(String[] args) { + String host = "dokploy.lona-development.org"; + int port = 1870; + + if (args.length > 0) { + host = args[0]; + } + if (args.length > 1) { + try { + port = Integer.parseInt(args[1]); + } catch (NumberFormatException e) { + System.err.println("Invalid port number provided, using default 1870"); + } + } + + final String serverHost = host; + final int serverPort = port; + + System.out.println("Starting TicTacToe Client..."); + System.out.println("Target Server: " + serverHost + ":" + serverPort); + + SwingUtilities.invokeLater(() -> { + try { + Main client = new Main(serverHost, serverPort); + client.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Failed to start GUI: " + e.getMessage()); + } + }); + } + + // Instance variables + private final String serverHost; + private final int serverPort; + + public Main(String host, int port) { super("TicTacToe Client"); + this.serverHost = host; + this.serverPort = port; + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 500); setLocationRelativeTo(null); - initUI(); - setContentPane(mainPanel); - - connectToServer(); + try { + initUI(); + setContentPane(mainPanel); + connectToServer(); + } catch (Exception e) { + e.printStackTrace(); + } } private void initUI() { @@ -95,21 +137,24 @@ public class Main extends JFrame { private void connectToServer() { new Thread(() -> { try { - // Using the specific domain provided - socket = new Socket("dokploy.lona-development.org", 1870); + System.out.println("Attempting to connect to " + serverHost + ":" + serverPort); + socket = new Socket(serverHost, serverPort); out = new PrintWriter(socket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + System.out.println("Connected successfully!"); SwingUtilities.invokeLater(() -> statusLabel.setText("Connected to Server.")); String line; while ((line = in.readLine()) != null) { + System.out.println("Received: " + line); // Log received messages processMessage(line); } } catch (IOException e) { + System.err.println("Connection Error: " + e.getMessage()); SwingUtilities.invokeLater(() -> { + statusLabel.setText("Connection Failed."); JOptionPane.showMessageDialog(this, "Connection Error: " + e.getMessage()); - statusLabel.setText("Disconnected."); }); } }).start(); @@ -189,8 +234,4 @@ public class Main extends JFrame { statusLabel.setText("Connected."); codeLabel.setText("Code: -"); } - - public static void main(String[] args) { - SwingUtilities.invokeLater(() -> new Main().setVisible(true)); - } } diff --git a/client/target/classes/com/lona/tictactoe/client/Main.class b/client/target/classes/com/lona/tictactoe/client/Main.class new file mode 100644 index 0000000..3768a7d Binary files /dev/null and b/client/target/classes/com/lona/tictactoe/client/Main.class differ diff --git a/client/target/client-1.0-SNAPSHOT.jar b/client/target/client-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..ea3dd9b Binary files /dev/null and b/client/target/client-1.0-SNAPSHOT.jar differ diff --git a/client/target/maven-archiver/pom.properties b/client/target/maven-archiver/pom.properties new file mode 100644 index 0000000..4c4f104 --- /dev/null +++ b/client/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Tue Feb 10 13:27:55 CET 2026 +artifactId=client +groupId=com.lona.tictactoe +version=1.0-SNAPSHOT diff --git a/client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..0128c4a --- /dev/null +++ b/client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1 @@ +com/lona/tictactoe/client/Main.class diff --git a/client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..099d3dd --- /dev/null +++ b/client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1 @@ +/home/collin/tictactoe/client/src/main/java/com/lona/tictactoe/client/Main.java diff --git a/client/target/original-client-1.0-SNAPSHOT.jar b/client/target/original-client-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..d5b25a0 Binary files /dev/null and b/client/target/original-client-1.0-SNAPSHOT.jar differ diff --git a/server/target/classes/com/lona/tictactoe/server/ClientHandler.class b/server/target/classes/com/lona/tictactoe/server/ClientHandler.class index 8a815f9..f59a9d5 100644 Binary files a/server/target/classes/com/lona/tictactoe/server/ClientHandler.class and b/server/target/classes/com/lona/tictactoe/server/ClientHandler.class differ diff --git a/server/target/classes/com/lona/tictactoe/server/GameInstance.class b/server/target/classes/com/lona/tictactoe/server/GameInstance.class index 85a942e..bb7ffcc 100644 Binary files a/server/target/classes/com/lona/tictactoe/server/GameInstance.class and b/server/target/classes/com/lona/tictactoe/server/GameInstance.class differ diff --git a/server/target/classes/com/lona/tictactoe/server/GameManager.class b/server/target/classes/com/lona/tictactoe/server/GameManager.class index 4b0344f..ccf35f6 100644 Binary files a/server/target/classes/com/lona/tictactoe/server/GameManager.class and b/server/target/classes/com/lona/tictactoe/server/GameManager.class differ diff --git a/server/target/classes/com/lona/tictactoe/server/GameServer.class b/server/target/classes/com/lona/tictactoe/server/GameServer.class index 3d5d71d..e6f90c6 100644 Binary files a/server/target/classes/com/lona/tictactoe/server/GameServer.class and b/server/target/classes/com/lona/tictactoe/server/GameServer.class differ diff --git a/server/target/maven-archiver/pom.properties b/server/target/maven-archiver/pom.properties new file mode 100644 index 0000000..f92dd76 --- /dev/null +++ b/server/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Tue Feb 10 13:26:46 CET 2026 +artifactId=server +groupId=com.lona.tictactoe +version=1.0-SNAPSHOT diff --git a/server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..953087d --- /dev/null +++ b/server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,4 @@ +com/lona/tictactoe/server/ClientHandler.class +com/lona/tictactoe/server/GameServer.class +com/lona/tictactoe/server/GameInstance.class +com/lona/tictactoe/server/GameManager.class diff --git a/server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..849d169 --- /dev/null +++ b/server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,4 @@ +/home/collin/tictactoe/server/src/main/java/com/lona/tictactoe/server/GameInstance.java +/home/collin/tictactoe/server/src/main/java/com/lona/tictactoe/server/ClientHandler.java +/home/collin/tictactoe/server/src/main/java/com/lona/tictactoe/server/GameServer.java +/home/collin/tictactoe/server/src/main/java/com/lona/tictactoe/server/GameManager.java diff --git a/server/target/original-server-1.0-SNAPSHOT.jar b/server/target/original-server-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..8d1fd2e Binary files /dev/null and b/server/target/original-server-1.0-SNAPSHOT.jar differ diff --git a/server/target/server-1.0-SNAPSHOT.jar b/server/target/server-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..7deb4df Binary files /dev/null and b/server/target/server-1.0-SNAPSHOT.jar differ diff --git a/web-client/target/classes/com/lona/tictactoe/web/Application.class b/web-client/target/classes/com/lona/tictactoe/web/Application.class new file mode 100644 index 0000000..4811b13 Binary files /dev/null and b/web-client/target/classes/com/lona/tictactoe/web/Application.class differ diff --git a/web-client/target/classes/com/lona/tictactoe/web/GameWebSocketHandler.class b/web-client/target/classes/com/lona/tictactoe/web/GameWebSocketHandler.class new file mode 100644 index 0000000..797410a Binary files /dev/null and b/web-client/target/classes/com/lona/tictactoe/web/GameWebSocketHandler.class differ diff --git a/web-client/target/classes/com/lona/tictactoe/web/TcpSession.class b/web-client/target/classes/com/lona/tictactoe/web/TcpSession.class new file mode 100644 index 0000000..95f3714 Binary files /dev/null and b/web-client/target/classes/com/lona/tictactoe/web/TcpSession.class differ diff --git a/web-client/target/classes/com/lona/tictactoe/web/WebController.class b/web-client/target/classes/com/lona/tictactoe/web/WebController.class new file mode 100644 index 0000000..06f6a70 Binary files /dev/null and b/web-client/target/classes/com/lona/tictactoe/web/WebController.class differ diff --git a/web-client/target/classes/com/lona/tictactoe/web/WebSocketConfig.class b/web-client/target/classes/com/lona/tictactoe/web/WebSocketConfig.class new file mode 100644 index 0000000..dfb60de Binary files /dev/null and b/web-client/target/classes/com/lona/tictactoe/web/WebSocketConfig.class differ diff --git a/web-client/target/classes/static/css/style.css b/web-client/target/classes/static/css/style.css new file mode 100644 index 0000000..1b04c69 --- /dev/null +++ b/web-client/target/classes/static/css/style.css @@ -0,0 +1,191 @@ +:root { + --bg-color: #09090b; + --card-bg: #18181b; + --primary: #2563eb; + --secondary: #db2777; + --accent: #10b981; + --text: #e4e4e7; + --text-muted: #a1a1aa; + --border: #27272a; + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +body { + margin: 0; + padding: 0; + font-family: 'Roboto', sans-serif; + background-color: var(--bg-color); + color: var(--text); + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; +} + +.container { + width: 100%; + max-width: 500px; + padding: 20px; + text-align: center; +} + +h1 { + font-family: 'Orbitron', sans-serif; + font-size: 3rem; + margin-bottom: 20px; + background: linear-gradient(to right, var(--primary), var(--secondary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0 0 30px rgba(37, 99, 235, 0.5); +} + +.status-bar { + margin-bottom: 20px; + padding: 10px; + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 8px; + font-size: 0.9rem; + color: var(--text-muted); +} + +.panel { + background: var(--card-bg); + padding: 30px; + border-radius: 12px; + border: 1px solid var(--border); + box-shadow: var(--shadow); +} + +.hidden { + display: none !important; +} + +.btn { + padding: 12px 24px; + border: none; + border-radius: 6px; + font-family: 'Orbitron', sans-serif; + font-weight: 700; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; + width: 100%; + margin: 5px 0; +} + +.btn:active { + transform: scale(0.98); +} + +.primary { + background: var(--primary); + color: white; + box-shadow: 0 0 15px rgba(37, 99, 235, 0.4); +} + +.secondary { + background: var(--card-bg); /* Use card background for join button */ + color: white; /* Ensure text is visible */ + border: 1px solid var(--border); /* Add a border to distinguish it */ +} + +/* Add hover effect for secondary button too */ +.secondary:hover { + background: #27272a; /* Slightly lighter on hover */ +} + +.danger { + background: #ef4444; + color: white; + margin-top: 20px; +} + +input[type="text"] { + width: 100%; + box-sizing: border-box; /* This ensures padding is included in width */ + padding: 12px; + margin: 10px 0; + background: #27272a; + border: 1px solid var(--border); + border-radius: 6px; + color: white; + font-family: 'Orbitron', sans-serif; + text-align: center; + font-size: 1.2rem; + letter-spacing: 2px; +} + +input[type="text"]:focus { + outline: 2px solid var(--primary); +} + +.divider { + color: var(--text-muted); + margin: 15px 0; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* Game Interface */ +.game-info { + display: flex; + justify-content: space-between; + margin-bottom: 20px; + background: #27272a; + padding: 10px; + border-radius: 8px; +} + +.info-item { + display: flex; + flex-direction: column; +} + +.info-item .label { + font-size: 0.7rem; + color: var(--text-muted); + text-transform: uppercase; +} + +.info-item .value { + font-family: 'Orbitron', sans-serif; + font-size: 1.1rem; + color: white; +} + +.board { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + aspect-ratio: 1; + margin: 0 auto; + max-width: 350px; +} + +.cell { + background: #27272a; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 3rem; + font-family: 'Orbitron', sans-serif; + cursor: pointer; + transition: background 0.2s; + user-select: none; +} + +.cell:hover { + background: #3f3f46; +} + +.cell.x { + color: var(--secondary); /* Pink for X */ + text-shadow: 0 0 10px rgba(219, 39, 119, 0.6); +} + +.cell.o { + color: var(--accent); /* Green for O */ + text-shadow: 0 0 10px rgba(16, 185, 129, 0.6); +} diff --git a/web-client/target/classes/static/js/app.js b/web-client/target/classes/static/js/app.js new file mode 100644 index 0000000..e6c6033 --- /dev/null +++ b/web-client/target/classes/static/js/app.js @@ -0,0 +1,155 @@ +const menuPanel = document.getElementById('menu'); +const gamePanel = document.getElementById('game'); +const statusDiv = document.getElementById('connection-status'); +const createBtn = document.getElementById('create-btn'); +const joinBtn = document.getElementById('join-btn'); +const joinInput = document.getElementById('join-code'); +const displayCode = document.getElementById('display-code'); +const turnStatus = document.getElementById('turn-status'); +const surrenderBtn = document.getElementById('surrender-btn'); +const cells = document.querySelectorAll('.cell'); + +let mySymbol = ''; +let ws = null; +let currentCode = ''; + +function connect() { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + ws = new WebSocket(`${protocol}//${window.location.host}/game`); + + ws.onopen = () => { + statusDiv.textContent = 'Connected to Server'; + statusDiv.style.color = '#10b981'; + }; + + ws.onclose = () => { + statusDiv.textContent = 'Disconnected. Reconnecting...'; + statusDiv.style.color = '#ef4444'; + setTimeout(connect, 2000); + }; + + ws.onmessage = (event) => { + handleMessage(event.data); + }; +} + +function send(msg) { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(msg); + } +} + +createBtn.addEventListener('click', () => { + send('CREATE'); +}); + +joinBtn.addEventListener('click', () => { + const code = joinInput.value.trim(); + if (code) send(`JOIN ${code}`); +}); + +surrenderBtn.addEventListener('click', () => { + if (confirm('Are you sure you want to surrender?')) { + send('SURRENDER'); + } +}); + +cells.forEach(cell => { + cell.addEventListener('click', () => { + const r = cell.dataset.r; + const c = cell.dataset.c; + send(`MOVE ${r} ${c}`); + }); +}); + +function handleMessage(msg) { + console.log("Received:", msg); + const parts = msg.split(' '); + const cmd = parts[0]; + + switch (cmd) { + case 'GAME_CREATED': + currentCode = parts[1]; + mySymbol = 'X'; + enterGame(); + updateStatus("Waiting for opponent..."); + break; + case 'JOIN_SUCCESS': + currentCode = joinInput.value; + mySymbol = 'O'; + enterGame(); + updateStatus("Connected! Game starting soon..."); + break; + case 'START': + updateStatus("Game Started! X goes first."); + break; + case 'BOARD': + const boardStr = msg.substring(6); + updateBoard(boardStr); + break; + case 'TURN': + const turn = parts[1]; + if (turn === mySymbol) { + updateStatus(`Your Turn (${mySymbol})`, true); + } else { + updateStatus(`Opponent's Turn (${turn})`, false); + } + break; + case 'WIN': + const winner = msg.substring(4); + alert(`Game Over! Winner: ${winner}`); + resetGame(); + break; + case 'DRAW': + alert("Game Over! It's a draw!"); + resetGame(); + break; + case 'ERROR:': + alert(msg); + break; + } +} + +function enterGame() { + menuPanel.classList.add('hidden'); + gamePanel.classList.remove('hidden'); + displayCode.textContent = currentCode; + // Clear board + cells.forEach(c => { + c.textContent = ''; + c.className = 'cell'; + }); +} + +function resetGame() { + menuPanel.classList.remove('hidden'); + gamePanel.classList.add('hidden'); + currentCode = ''; + mySymbol = ''; + joinInput.value = ''; + statusDiv.textContent = 'Connected to Server'; + statusDiv.style.color = '#10b981'; +} + +function updateBoard(boardStr) { + cells.forEach((cell, i) => { + if (i < boardStr.length) { + const char = boardStr.charAt(i); + cell.textContent = char === ' ' ? '' : char; + cell.className = 'cell'; + if (char === 'X') cell.classList.add('x'); + if (char === 'O') cell.classList.add('o'); + } + }); +} + +function updateStatus(text, isAction = false) { + turnStatus.textContent = text; + if (isAction) { + turnStatus.style.color = '#10b981'; // Green for 'your turn' + } else { + turnStatus.style.color = '#a1a1aa'; + } +} + +connect(); diff --git a/web-client/target/classes/templates/index.html b/web-client/target/classes/templates/index.html new file mode 100644 index 0000000..d29f35e --- /dev/null +++ b/web-client/target/classes/templates/index.html @@ -0,0 +1,61 @@ + + + + + + TicTacToe + + + + +
+

TicTacToe

+ +
Connecting to server...
+ + + + + + +
+ + + + diff --git a/web-client/target/maven-archiver/pom.properties b/web-client/target/maven-archiver/pom.properties new file mode 100644 index 0000000..1fa9bc1 --- /dev/null +++ b/web-client/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=web-client +groupId=com.lona.tictactoe +version=1.0-SNAPSHOT diff --git a/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..6dd1997 --- /dev/null +++ b/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,5 @@ +com/lona/tictactoe/web/TcpSession.class +com/lona/tictactoe/web/WebSocketConfig.class +com/lona/tictactoe/web/Application.class +com/lona/tictactoe/web/GameWebSocketHandler.class +com/lona/tictactoe/web/WebController.class diff --git a/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..8c72cc3 --- /dev/null +++ b/web-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,5 @@ +/home/collin/tictactoe/web-client/src/main/java/com/lona/tictactoe/web/TcpSession.java +/home/collin/tictactoe/web-client/src/main/java/com/lona/tictactoe/web/WebSocketConfig.java +/home/collin/tictactoe/web-client/src/main/java/com/lona/tictactoe/web/GameWebSocketHandler.java +/home/collin/tictactoe/web-client/src/main/java/com/lona/tictactoe/web/Application.java +/home/collin/tictactoe/web-client/src/main/java/com/lona/tictactoe/web/WebController.java diff --git a/web-client/target/web-client-1.0-SNAPSHOT.jar b/web-client/target/web-client-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..3e284e4 Binary files /dev/null and b/web-client/target/web-client-1.0-SNAPSHOT.jar differ diff --git a/web-client/target/web-client-1.0-SNAPSHOT.jar.original b/web-client/target/web-client-1.0-SNAPSHOT.jar.original new file mode 100644 index 0000000..c5bd5a8 Binary files /dev/null and b/web-client/target/web-client-1.0-SNAPSHOT.jar.original differ