Implemented threading in Client

- GUI doesn't get blocked anymore
- Implemented client side protocol based control
- Client is now ready for player versus player
This commit is contained in:
2021-03-18 03:47:40 +01:00
parent 8299f680b0
commit f1fc88511b
15 changed files with 494 additions and 238 deletions

View File

@@ -1,7 +0,0 @@
import game.TicTacToe_Client;
public class Launcher {
public static void main(String[] args) {
TicTacToe_Client.main(args);
}
}

View File

@@ -1,3 +1,3 @@
Manifest-Version: 1.0
Main-Class: game.TicTacToe_Client
Main-Class: TicTacToe_Client

View File

@@ -0,0 +1,131 @@
import javafx.application.Platform;
import networking.Client;
import render.Engine;
import java.util.Arrays;
import java.util.LinkedList;
public class TicTacToe_Client {
private Engine renderEngine;
private Client client;
private boolean isSingleServer;
public TicTacToe_Client(){
renderEngine = Engine.waitForEngine();
}
private void ticTacToe_gameloop(){
//Setup
client = new Client("server", 2589, "second");
client.handshake();
isSingleServer = client.getServerType();
if (isSingleServer){
this.setWindowTitle(client.isPlayerOne());
client.sendToServer("ready");
while (client.isConnected()) {
String message = client.getResponse();
//Check if message is gamestate
if (message.charAt(0) == 'x' || message.charAt(0) == '-' || message.charAt(0) == 'o') {
this.drawBoard(message);
this.gameFlow("userInput");
}
//Handle everything else
else {
this.gameFlow(message);
}
}
} else {
this.setWindowTitle(client.isPlayerOne());
}
}
private void gameFlow(String input){
switch (input) {
case "userInput":
while(!renderEngine.isMouseClicked()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
renderEngine.setMouseClicked(false);
this.userInput();
break;
}
}
private void userInput(){
//Send command
client.sendToServer("clientMove");
//Send position
client.sendToServer(String.format("%f|%f", renderEngine.getCoordinates().getX(), renderEngine.getCoordinates().getY()));
//Get gameState
String gameState = client.getResponse();
if (gameState.length() == 9) {
this.drawBoard(gameState);
//Send command
if (!client.getGameEnded()) {
client.sendToServer("computerMove");
this.drawBoard(client.getResponse());
} else {
LinkedList<Integer> winCoordinates = new LinkedList<>();
//Get winning fields
String response = client.getResponse();
for (String s : Arrays.copyOfRange(response.split(";"), 0, 4)) {
winCoordinates.add(Integer.valueOf(s) * 300);
}
//this.drawWinningLine(winCoordinates);
client.exitProcess();
System.exit(0);
}
} else {
int column = (int) renderEngine.getCoordinates().getX() / 300;
int row = (int) renderEngine.getCoordinates().getY() / 300;
System.err.printf("You are not allowed to place at %d|%d%n", column, row);
}
}
private void drawBoard(String gameState) {
Platform.runLater(new Runnable() {
@Override
public void run() {
renderEngine.drawBoard(gameState);
}
});
}
private void setWindowTitle(boolean isClientOne){
if (isClientOne){
Platform.runLater(new Runnable() {
@Override
public void run() {
renderEngine.updateTitle("Client One");
}
});
} else {
Platform.runLater(new Runnable() {
@Override
public void run() {
renderEngine.updateTitle("Client Two");
}
});
}
}
public static void main(String[] args) {
new Thread(){
@Override
public void run(){
javafx.application.Application.launch(Engine.class);
}
}.start();
TicTacToe_Client test = new TicTacToe_Client();
test.ticTacToe_gameloop();
}
}

View File

@@ -1,135 +0,0 @@
package game;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import networking.Client;
import java.util.Arrays;
import java.util.LinkedList;
public class TicTacToe_Client extends Application {
GridPane grid;
Client client;
Scene scene;
private void initializeGrid() {
grid = new GridPane();
grid.setMinSize(900, 900);
grid.setAlignment(Pos.CENTER);
grid.setHgap(150);
grid.setVgap(75);
}
private void drawCross(int column, int row) {
Text cross = new Text("X");
cross.setFont(Font.font("Tahoma", FontWeight.NORMAL, 200));
grid.add(cross, column, row);
}
private void drawCircle(int column, int row) {
Text circle = new Text("O");
circle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 200));
grid.add(circle, column, row);
}
private void drawEmptyField(int column, int row) {
Text emptyField = new Text(" ");
emptyField.setFont(Font.font("Tahoma", FontWeight.NORMAL, 220));
grid.add(emptyField, column, row);
}
private void drawBoard(String gameState) {
if (gameState.length() != 9) {
System.err.println("Wrong length of gameState string");
return;
}
for (int i = 0; i < gameState.length(); i++) {
int column = i / 3;
int row = i % 3;
if (gameState.charAt(i) == 'x') {
this.drawCross(column, row);
} else if (gameState.charAt(i) == 'o') {
this.drawCircle(column, row);
} else {
this.drawEmptyField(column, row);
}
}
}
private Scene setScene() {
scene = new Scene(grid, 900, 900);
scene.getStylesheets().add
(TicTacToe_Client.class.getResource("TicTacToe_Client.css").toExternalForm());
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
onMouseClick(event);
}
});
return scene;
}
private void onMouseClick(MouseEvent event) {
client.sendToServer("update");
client.sendToServer(String.format("%f|%f", event.getX(), event.getY()));
String gameState = client.getResponse();
System.out.println(gameState);
if (gameState.length() == 9) {
drawBoard(gameState);
if (!client.getGameEnded()) {
gameState = client.getResponse();
drawBoard(gameState);
} else {
LinkedList<Integer> winCoordinates = new LinkedList<>();
String response = client.getResponse();
for (String s : Arrays.copyOfRange(response.split(";"), 0, 4)) {
winCoordinates.add(Integer.valueOf(s) * 300);
}
this.drawWinningLine(winCoordinates);
client.exitProcess();
}
} else {
int column = (int) event.getX() / 300;
int row = (int) event.getY() / 300;
System.err.printf("You are not allowed to place at %d|%d%n", column, row);
}
}
private void drawWinningLine(LinkedList<Integer> winCoordinates) {
Line winningLine = new Line(winCoordinates.get(0), winCoordinates.get(1), winCoordinates.get(2), winCoordinates.get(3));
winningLine.setFill(Color.RED);
grid.add(winningLine, winCoordinates.get(0) / 300, winCoordinates.get(1) / 300, 3, 3);
}
@Override
public void start(Stage primaryStage) {
client = new Client("server", 2589, "Cato");
client.handshake();
primaryStage.setTitle("TicTacToe");
primaryStage.setResizable(false);
this.initializeGrid();
primaryStage.setScene(this.setScene());
primaryStage.show();
this.drawBoard(client.getGameState());
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -10,6 +10,7 @@ import java.net.Socket;
import java.util.Scanner;
public class Client {
private Socket serverSocket;
private DataOutputStream out;
private DataInputStream in;
private static Scanner scanner;
@@ -21,7 +22,8 @@ public class Client {
public Client(String ip, int port, String name) {
try {
scanner = new Scanner(System.in);
Socket serverSocket = new Socket(ip, port);
serverSocket = new Socket(ip, port);
serverName = serverSocket.getRemoteSocketAddress().toString();
out = new DataOutputStream(serverSocket.getOutputStream());
in = new DataInputStream(serverSocket.getInputStream());
clientLogger = new ClientLogger();
@@ -41,7 +43,6 @@ public class Client {
if (success) {
out.writeUTF(name);
out.flush();
serverName = in.readUTF();
clientLogger.printLog("You successfully connected to me", serverName, success, LogType.Log);
} else {
clientLogger.printLog("Connection failed try again", success, LogType.Log);
@@ -63,10 +64,20 @@ public class Client {
}
}
public void sendConfirmation(){
try {
out.writeInt(200);
out.flush();
clientLogger.printLog("Sent verification code", serverName, true, LogType.Log);
} catch (IOException e) {
e.printStackTrace();
}
}
public String getResponse() {
try {
String message = in.readUTF();
clientLogger.printLog(String.format("Message recieved: %s", message), serverName, success, LogType.Log);
clientLogger.printLog(String.format("Message recieved: %s", message), serverName, true, LogType.Log);
return message;
} catch (IOException e) {
e.printStackTrace();
@@ -74,17 +85,29 @@ public class Client {
return "";
}
public boolean getBooleanResponse(String value){
public boolean getBooleanResponse(String message) {
try {
boolean booleanResponse = in.readBoolean();
clientLogger.printLog(String.format(value + ": %b", booleanResponse), serverName, booleanResponse, LogType.Log);
return booleanResponse;
boolean state = in.readBoolean();
clientLogger.printLog(String.format("%s: %b", message, state), serverName, true, LogType.Log);
return state;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public boolean isPlayerOne(){
this.sendToServer("isClientOne");
boolean isClientOne = this.getBooleanResponse("isClientOne");
return isClientOne;
}
public boolean getServerType(){
this.sendToServer("serverType");
boolean serverType = this.getBooleanResponse("isSingleServer");
return serverType;
}
public String getGameState() {
this.sendToServer("gameState");
String gameState = this.getResponse();
@@ -93,10 +116,20 @@ public class Client {
public boolean getGameEnded() {
this.sendToServer("gameEnded");
boolean gameEnded = this.getBooleanResponse("Game ended");
boolean gameEnded = false;
try {
gameEnded = in.readBoolean();
clientLogger.printLog(String.format("Game ended: %b", gameEnded), serverName, gameEnded, LogType.Log);
} catch (IOException e) {
e.printStackTrace();
}
return gameEnded;
}
public void waitForInput() {
}
public void exitProcess(){
try {
out.writeUTF("exit()");
@@ -108,12 +141,13 @@ public class Client {
}
}
public boolean hasSucceeded() {
return success;
}
public String getName() {
return name;
}
public boolean isConnected(){
return serverSocket.isConnected();
}
}

View File

@@ -0,0 +1,141 @@
package render;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.awt.*;
import java.util.concurrent.CountDownLatch;
public class Engine extends Application {
private GridPane grid;
private Scene scene;
private static final CountDownLatch latch = new CountDownLatch(1);
private static Engine engine = null;
private boolean mouseClicked = false;
private Point coordinates = new Point();
private Stage primaryStage;
private MouseEvent mouseEvent;
public Engine() {
setEngine(this);
}
private void initializeGrid() {
grid = new GridPane();
grid.setPrefSize(900, 900);
grid.setAlignment(Pos.CENTER);
grid.setHgap(150);
grid.setVgap(75);
}
private Scene setScene() {
scene = new Scene(grid, 900, 900);
scene.getStylesheets().add("res/TicTacToe_Client.css");
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
onMouseClick(event);
}
});
return scene;
}
public static Engine waitForEngine() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return engine;
}
public static void setEngine(Engine new_engine) {
engine = new_engine;
latch.countDown();
}
public void updateTitle(String title){
System.out.println("title: " + title);
primaryStage.setTitle(title);
}
public void drawCross(int column, int row) {
Text cross = new Text("X");
cross.setFont(Font.font("Tahoma", FontWeight.NORMAL, 200));
grid.add(cross, column, row);
}
public void drawCircle(int column, int row) {
Text circle = new Text("O");
circle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 200));
grid.add(circle, column, row);
}
public void drawEmptyField(int column, int row) {
Text emptyField = new Text(" ");
emptyField.setFont(Font.font("Tahoma", FontWeight.NORMAL, 220));
grid.add(emptyField, column, row);
}
public void drawBoard(String gameState) {
if (gameState.length() != 9) {
System.err.println("Wrong length of gameState string");
return;
}
for (int i = 0; i < gameState.length(); i++) {
int column = i / 3;
int row = i % 3;
if (gameState.charAt(i) == 'x') {
this.drawCross(column, row);
} else if (gameState.charAt(i) == 'o') {
this.drawCircle(column, row);
} else {
this.drawEmptyField(column, row);
}
}
}
private void onMouseClick(MouseEvent event) {
mouseClicked = true;
coordinates.setLocation(event.getX(), event.getY());
System.out.println(coordinates.getX() + ":" + coordinates.getY());
}
public boolean isMouseClicked() {
return mouseClicked;
}
public void setMouseClicked(boolean mouseClicked) {
this.mouseClicked = mouseClicked;
}
public Point getCoordinates() {
return coordinates;
}
@Override
public void start(Stage primaryStage) throws Exception {
//initialize window
this.primaryStage = primaryStage;
primaryStage.setTitle("Test");
primaryStage.setResizable(true);
this.initializeGrid();
primaryStage.setScene(this.setScene());
primaryStage.sizeToScene();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB