Compare commits
2 Commits
1492f078f9
...
ef4fdc1c9c
| Author | SHA1 | Date | |
|---|---|---|---|
|
ef4fdc1c9c
|
|||
|
a425be712e
|
@@ -1,51 +0,0 @@
|
|||||||
package Ex6;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import Ex6.packets.EndGamePacket;
|
|
||||||
import Ex6.packets.InvalidMovePacket;
|
|
||||||
import Ex6.packets.LeavePacket;
|
|
||||||
import Ex6.packets.NewGamePacket;
|
|
||||||
import Ex6.packets.PlayMovePacket;
|
|
||||||
import Ex6.packets.PlayerMovePacket;
|
|
||||||
|
|
||||||
public class Client extends Connexion {
|
|
||||||
|
|
||||||
public Client(Socket socket) throws IOException {
|
|
||||||
super(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(EndGamePacket packet) {
|
|
||||||
System.out.println("Jeu terminé !");
|
|
||||||
// TODO: afficher vainqueur
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InvalidMovePacket packet) {
|
|
||||||
System.out.println("Coups non autorisé !");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewGamePacket packet) {
|
|
||||||
System.out.println("Nouvelle partie !");
|
|
||||||
// TODO: afficher tour
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(PlayerMovePacket packet) {
|
|
||||||
// TODO: afficher plateau
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(LeavePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(PlayMovePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package Ex6;
|
|
||||||
|
|
||||||
public class Jeu {
|
|
||||||
public enum EtatCase {
|
|
||||||
Croix,
|
|
||||||
Rond,
|
|
||||||
Vide
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean tourDeRond = false;
|
|
||||||
|
|
||||||
boolean gameDone = false;
|
|
||||||
|
|
||||||
EtatCase cases[3][3];
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package Ex6;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
import Ex6.packets.EndGamePacket;
|
|
||||||
import Ex6.packets.InvalidMovePacket;
|
|
||||||
import Ex6.packets.LeavePacket;
|
|
||||||
import Ex6.packets.NewGamePacket;
|
|
||||||
import Ex6.packets.PlayMovePacket;
|
|
||||||
import Ex6.packets.PlayerMovePacket;
|
|
||||||
|
|
||||||
public class Joueur extends Connexion {
|
|
||||||
|
|
||||||
public Joueur(Socket socket) throws IOException {
|
|
||||||
super(socket);
|
|
||||||
new Thread(this::readPackets).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readPackets() {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
processPacket();
|
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(LeavePacket packet) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(PlayMovePacket packet) {
|
|
||||||
//TODO: vérifier coups
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(EndGamePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InvalidMovePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewGamePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(PlayerMovePacket packet) {
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package Ex6;
|
|
||||||
|
|
||||||
public abstract class Packet {
|
|
||||||
public abstract void accept(PacketVisitor visitor);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package Ex6;
|
|
||||||
|
|
||||||
import Ex6.packets.EndGamePacket;
|
|
||||||
import Ex6.packets.InvalidMovePacket;
|
|
||||||
import Ex6.packets.LeavePacket;
|
|
||||||
import Ex6.packets.NewGamePacket;
|
|
||||||
import Ex6.packets.PlayMovePacket;
|
|
||||||
import Ex6.packets.PlayerMovePacket;
|
|
||||||
|
|
||||||
public interface PacketVisitor {
|
|
||||||
void visit(EndGamePacket packet);
|
|
||||||
|
|
||||||
void visit(InvalidMovePacket packet);
|
|
||||||
|
|
||||||
void visit(LeavePacket packet);
|
|
||||||
|
|
||||||
void visit(NewGamePacket packet);
|
|
||||||
|
|
||||||
void visit(PlayerMovePacket packet);
|
|
||||||
|
|
||||||
void visit(PlayMovePacket packet);
|
|
||||||
}
|
|
||||||
18
src/Ex6/README.md
Normal file
18
src/Ex6/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
- The main package contains the logic.
|
||||||
|
- The network package contains the protocol implementation details.
|
||||||
|
|
||||||
|
# Running the program
|
||||||
|
|
||||||
|
## Launch server
|
||||||
|
|
||||||
|
Run Serveur.java
|
||||||
|
|
||||||
|
## Launch Client
|
||||||
|
|
||||||
|
Run Client.java
|
||||||
|
|
||||||
|
## Launch Server + Client
|
||||||
|
|
||||||
|
Run App.java
|
||||||
20
src/Ex6/main/App.java
Normal file
20
src/Ex6/main/App.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package Ex6.main;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run this to launch the server and immediatly connect to it with a CLI Client.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
final int port = 6666;
|
||||||
|
|
||||||
|
System.out.println("Launching server ...");
|
||||||
|
Serveur serveur = new Serveur(port);
|
||||||
|
|
||||||
|
System.out.println("Connecting client1 ...");
|
||||||
|
Client client1 = new Client(new Socket("localhost", port));
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/Ex6/main/Client.java
Normal file
160
src/Ex6/main/Client.java
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package Ex6.main;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import Ex6.main.Jeu.EtatCase;
|
||||||
|
import Ex6.network.Connexion;
|
||||||
|
import Ex6.network.packets.EndGamePacket;
|
||||||
|
import Ex6.network.packets.InvalidMovePacket;
|
||||||
|
import Ex6.network.packets.LeavePacket;
|
||||||
|
import Ex6.network.packets.NewGamePacket;
|
||||||
|
import Ex6.network.packets.PlayMovePacket;
|
||||||
|
import Ex6.network.packets.PlayerMovePacket;
|
||||||
|
|
||||||
|
public class Client extends Connexion {
|
||||||
|
|
||||||
|
private EtatCase player = EtatCase.Vide;
|
||||||
|
private EtatCase turn = EtatCase.Vide;
|
||||||
|
private EtatCase[] cases = new EtatCase[9];
|
||||||
|
|
||||||
|
// I swear I typed those by hand
|
||||||
|
private static final String[] CELL_EMOJIS = {
|
||||||
|
"0️⃣ ", "1️⃣ ", "2️⃣ ",
|
||||||
|
"3️⃣ ", "4️⃣ ", "5️⃣ ",
|
||||||
|
"6️⃣ ", "7️⃣ ", "8️⃣ ",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run this to connect to an existing server
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
new Client(new Socket("localhost", 6666));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client(Socket socket) throws IOException {
|
||||||
|
super(socket);
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
String line = scanner.nextLine();
|
||||||
|
switch (line) {
|
||||||
|
case "CLOSE":
|
||||||
|
case "close":
|
||||||
|
case "STOP":
|
||||||
|
case "stop":
|
||||||
|
sendPacket(new LeavePacket());
|
||||||
|
socket.close();
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int cellIndex = Integer.parseInt(line);
|
||||||
|
if (cellIndex > 8 || cellIndex < 0) {
|
||||||
|
System.out.println("Number not in range !");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sendPacket(new PlayMovePacket(cellIndex));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.out.println("Number not recognized !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanner.close();
|
||||||
|
System.out.println("Bye !");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlayerTurn() {
|
||||||
|
return this.player == this.turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlayerCharacter() {
|
||||||
|
return this.player == EtatCase.Rond ? "⭕" : "❌";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchTurn() {
|
||||||
|
if (this.turn == EtatCase.Croix) {
|
||||||
|
this.turn = EtatCase.Rond;
|
||||||
|
} else {
|
||||||
|
this.turn = EtatCase.Croix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String renderBoard() {
|
||||||
|
String board = "";
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
EtatCase cell = cases[i];
|
||||||
|
switch (cell) {
|
||||||
|
case Vide:
|
||||||
|
board += CELL_EMOJIS[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Croix:
|
||||||
|
board += "❌";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Rond:
|
||||||
|
board += "⭕";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i % 3 == 2)
|
||||||
|
board += "\n";
|
||||||
|
}
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printBoard() {
|
||||||
|
System.out.println("\n\n\n\n\n\n\n\n\n");
|
||||||
|
System.out.println(renderBoard());
|
||||||
|
if (isPlayerTurn()) {
|
||||||
|
System.out.println("C'est à votre tour !");
|
||||||
|
} else {
|
||||||
|
System.out.println("En attente de l'adversaire ...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(EndGamePacket packet) {
|
||||||
|
System.out.println("Jeu terminé !");
|
||||||
|
printBoard();
|
||||||
|
System.out.println("\n\n\nVous avez " + (packet.getVainqueur() == player ? "gagné 🤩" : "perdu 💩"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvalidMovePacket packet) {
|
||||||
|
System.out.println("Coups non autorisé !");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NewGamePacket packet) {
|
||||||
|
this.player = packet.isRond() ? EtatCase.Rond : EtatCase.Croix;
|
||||||
|
this.turn = EtatCase.Croix;
|
||||||
|
Arrays.fill(cases, EtatCase.Vide);
|
||||||
|
System.out.printf("Nouvelle partie !\n", this.getPlayerCharacter());
|
||||||
|
System.out.println("Vous êtes les " + getPlayerCharacter());
|
||||||
|
printBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PlayerMovePacket packet) {
|
||||||
|
cases[packet.getCellIndex()] = turn;
|
||||||
|
switchTurn();
|
||||||
|
printBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LeavePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PlayMovePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
115
src/Ex6/main/Jeu.java
Normal file
115
src/Ex6/main/Jeu.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package Ex6.main;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class Jeu {
|
||||||
|
public enum EtatCase {
|
||||||
|
Croix,
|
||||||
|
Rond,
|
||||||
|
Vide
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean gameDone;
|
||||||
|
|
||||||
|
private EtatCase turn = EtatCase.Vide;
|
||||||
|
private EtatCase[] cases = new EtatCase[9];
|
||||||
|
|
||||||
|
private final Serveur serveur;
|
||||||
|
|
||||||
|
private static final int[][] CHECK_INDICES = {
|
||||||
|
// rows
|
||||||
|
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
|
||||||
|
|
||||||
|
// columns
|
||||||
|
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
|
||||||
|
|
||||||
|
// diagonals
|
||||||
|
{0, 4, 8}, {2, 4, 6}
|
||||||
|
};
|
||||||
|
|
||||||
|
public Jeu(Serveur serveur) {
|
||||||
|
this.serveur = serveur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new game and randomly assign symbols to players
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
boolean player1Rond = new Random().nextBoolean();
|
||||||
|
|
||||||
|
this.serveur.joueur1.startGame(player1Rond);
|
||||||
|
this.serveur.joueur2.startGame(!player1Rond);
|
||||||
|
|
||||||
|
this.gameDone = false;
|
||||||
|
this.turn = EtatCase.Croix;
|
||||||
|
Arrays.fill(cases, EtatCase.Vide);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchTurn() {
|
||||||
|
if (this.turn == EtatCase.Croix){
|
||||||
|
this.turn = EtatCase.Rond;
|
||||||
|
} else {
|
||||||
|
this.turn = EtatCase.Croix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for winning pattern by looking at an index table.
|
||||||
|
* @return EtatCase.Vide if no one won.
|
||||||
|
*/
|
||||||
|
private EtatCase checkWin() {
|
||||||
|
for (int i = 0; i < CHECK_INDICES.length; i++) {
|
||||||
|
EtatCase previousCell = null;
|
||||||
|
boolean skip = false;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
int cellIndex = CHECK_INDICES[i][j];
|
||||||
|
EtatCase cell = cases[cellIndex];
|
||||||
|
if (cell == EtatCase.Vide) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (previousCell != null) {
|
||||||
|
if (previousCell != cell) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousCell = cell;
|
||||||
|
}
|
||||||
|
if (skip)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return previousCell;
|
||||||
|
}
|
||||||
|
return EtatCase.Vide;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a move for the current player.
|
||||||
|
* Be aware that the move is not checked and should be done before.
|
||||||
|
* @see Ex6.main.Jeu.checkWin
|
||||||
|
* @param cellIndex index of the cell to replace.
|
||||||
|
* @return EtatCase.Vide if no one won.
|
||||||
|
*/
|
||||||
|
public EtatCase playMove(int cellIndex) {
|
||||||
|
cases[cellIndex] = this.turn;
|
||||||
|
switchTurn();
|
||||||
|
EtatCase winner = checkWin();
|
||||||
|
if (winner != EtatCase.Vide)
|
||||||
|
this.gameDone = true;
|
||||||
|
return winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EtatCase getCell(int index) {
|
||||||
|
return cases[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public EtatCase getTurn() {
|
||||||
|
return turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGameDone() {
|
||||||
|
return this.gameDone;
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/Ex6/main/Joueur.java
Normal file
90
src/Ex6/main/Joueur.java
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package Ex6.main;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import Ex6.main.Jeu.EtatCase;
|
||||||
|
import Ex6.network.Connexion;
|
||||||
|
import Ex6.network.packets.EndGamePacket;
|
||||||
|
import Ex6.network.packets.InvalidMovePacket;
|
||||||
|
import Ex6.network.packets.LeavePacket;
|
||||||
|
import Ex6.network.packets.NewGamePacket;
|
||||||
|
import Ex6.network.packets.PlayMovePacket;
|
||||||
|
import Ex6.network.packets.PlayerMovePacket;
|
||||||
|
|
||||||
|
public class Joueur extends Connexion {
|
||||||
|
|
||||||
|
private final Jeu jeu;
|
||||||
|
private final Serveur serveur;
|
||||||
|
private EtatCase player;
|
||||||
|
|
||||||
|
public Joueur(Socket socket, Jeu jeu, Serveur serveur) throws IOException {
|
||||||
|
super(socket);
|
||||||
|
this.jeu = jeu;
|
||||||
|
this.serveur = serveur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify a new game has started to the client
|
||||||
|
* @param rond True if player is 'O'
|
||||||
|
*/
|
||||||
|
public void startGame(boolean rond) {
|
||||||
|
this.player = rond ? EtatCase.Rond : EtatCase.Croix;
|
||||||
|
sendPacket(new NewGamePacket(rond));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LeavePacket packet) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PlayMovePacket packet) {
|
||||||
|
// player should not play
|
||||||
|
if (this.jeu.isGameDone() || this.jeu.getTurn() != this.player) {
|
||||||
|
sendPacket(new InvalidMovePacket());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check placing on empty cell
|
||||||
|
if (this.jeu.getCell(packet.getCellIndex()) != EtatCase.Vide) {
|
||||||
|
sendPacket(new InvalidMovePacket());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EtatCase winner = this.jeu.playMove(packet.getCellIndex());
|
||||||
|
this.serveur.broadcastPacket(new PlayerMovePacket(packet.getCellIndex()));
|
||||||
|
if (winner != EtatCase.Vide) {
|
||||||
|
this.serveur.broadcastPacket(new EndGamePacket(winner));
|
||||||
|
try {
|
||||||
|
Thread.sleep(10000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatically launch new game after 10s
|
||||||
|
this.jeu.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(EndGamePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvalidMovePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NewGamePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PlayerMovePacket packet) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented method 'visit'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
69
src/Ex6/main/Serveur.java
Normal file
69
src/Ex6/main/Serveur.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package Ex6.main;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import Ex6.network.Packet;
|
||||||
|
|
||||||
|
public class Serveur {
|
||||||
|
|
||||||
|
private final Jeu jeu;
|
||||||
|
|
||||||
|
private final ServerSocket serverSocket;
|
||||||
|
public Joueur joueur1;
|
||||||
|
public Joueur joueur2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run this to launch the server in headless mode
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
new Serveur(6666);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a TicTacToe server on the specified port
|
||||||
|
* @param port the port to listen to
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public Serveur(int port) throws IOException {
|
||||||
|
this.jeu = new Jeu(this);
|
||||||
|
this.serverSocket = new ServerSocket(port);
|
||||||
|
this.joueur1 = null;
|
||||||
|
this.joueur2 = null;
|
||||||
|
new Thread(this::acceptLoop).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet to both players
|
||||||
|
* @param packet the packet to send
|
||||||
|
*/
|
||||||
|
public void broadcastPacket(Packet packet) {
|
||||||
|
this.joueur1.sendPacket(packet);
|
||||||
|
this.joueur2.sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here we accept only the first two players.
|
||||||
|
* If a player leaves, the server needs to be restarted.
|
||||||
|
* It's not robust but enough for a demo.
|
||||||
|
*/
|
||||||
|
private void acceptLoop() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Socket socket = this.serverSocket.accept();
|
||||||
|
if (this.joueur1 == null) {
|
||||||
|
this.joueur1 = new Joueur(socket, jeu, this);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (this.joueur2 == null) {
|
||||||
|
this.joueur2 = new Joueur(socket, jeu, this);
|
||||||
|
this.jeu.start();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,29 @@
|
|||||||
package Ex6;
|
package Ex6.network;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction of TCP connection implementing PacketVisitor
|
||||||
|
*/
|
||||||
public abstract class Connexion implements PacketVisitor {
|
public abstract class Connexion implements PacketVisitor {
|
||||||
|
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
private final ObjectOutputStream out;
|
private final ObjectOutputStream out;
|
||||||
private final ObjectInputStream in;
|
private ObjectInputStream in;
|
||||||
|
|
||||||
public Connexion(Socket socket) throws IOException {
|
public Connexion(Socket socket) throws IOException {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.out = new ObjectOutputStream(this.socket.getOutputStream());
|
this.out = new ObjectOutputStream(this.socket.getOutputStream());
|
||||||
this.in = new ObjectInputStream(this.socket.getInputStream());
|
new Thread(this::readPackets).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet through network
|
||||||
|
* @param packet The packet to send
|
||||||
|
*/
|
||||||
public void sendPacket(Packet packet) {
|
public void sendPacket(Packet packet) {
|
||||||
try {
|
try {
|
||||||
out.writeObject(packet);
|
out.writeObject(packet);
|
||||||
@@ -25,17 +32,45 @@ public abstract class Connexion implements PacketVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop to process new packets infinitely.
|
||||||
|
*/
|
||||||
|
private void readPackets() {
|
||||||
|
try {
|
||||||
|
this.in = new ObjectInputStream(this.socket.getInputStream());
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
processPacket();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a packet.
|
||||||
|
* @return The packet read.
|
||||||
|
*/
|
||||||
private Packet readPacket() throws ClassNotFoundException, IOException {
|
private Packet readPacket() throws ClassNotFoundException, IOException {
|
||||||
Object o = in.readObject();
|
Object o = in.readObject();
|
||||||
Packet packet = (Packet) o;
|
Packet packet = (Packet) o;
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing the packet through double-dispatch.
|
||||||
|
*/
|
||||||
public void processPacket() throws ClassNotFoundException, IOException {
|
public void processPacket() throws ClassNotFoundException, IOException {
|
||||||
Packet packet = readPacket();
|
Packet packet = readPacket();
|
||||||
packet.accept(this);
|
visit(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection.
|
||||||
|
*/
|
||||||
protected void close() {
|
protected void close() {
|
||||||
try {
|
try {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
8
src/Ex6/network/Packet.java
Normal file
8
src/Ex6/network/Packet.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package Ex6.network;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class representing a packet (implementing the Visitor design pattern)
|
||||||
|
*/
|
||||||
|
public abstract class Packet {
|
||||||
|
public abstract void accept(PacketVisitor visitor);
|
||||||
|
}
|
||||||
29
src/Ex6/network/PacketVisitor.java
Normal file
29
src/Ex6/network/PacketVisitor.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package Ex6.network;
|
||||||
|
|
||||||
|
import Ex6.network.packets.EndGamePacket;
|
||||||
|
import Ex6.network.packets.InvalidMovePacket;
|
||||||
|
import Ex6.network.packets.LeavePacket;
|
||||||
|
import Ex6.network.packets.NewGamePacket;
|
||||||
|
import Ex6.network.packets.PlayMovePacket;
|
||||||
|
import Ex6.network.packets.PlayerMovePacket;
|
||||||
|
|
||||||
|
public interface PacketVisitor {
|
||||||
|
void visit(EndGamePacket packet);
|
||||||
|
|
||||||
|
void visit(InvalidMovePacket packet);
|
||||||
|
|
||||||
|
void visit(LeavePacket packet);
|
||||||
|
|
||||||
|
void visit(NewGamePacket packet);
|
||||||
|
|
||||||
|
void visit(PlayerMovePacket packet);
|
||||||
|
|
||||||
|
void visit(PlayMovePacket packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Double-dispatch
|
||||||
|
*/
|
||||||
|
default void visit(Packet packet) {
|
||||||
|
packet.accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package Ex6.packets;
|
package Ex6.network.packets;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import Ex6.Packet;
|
|
||||||
import Ex6.PacketVisitor;
|
|
||||||
import Ex6.Jeu.EtatCase;
|
import Ex6.Jeu.EtatCase;
|
||||||
|
import Ex6.network.Packet;
|
||||||
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Packet envoyé pour annoncer la fin de la partie
|
* Packet sent to both clients to end a game (providing the winner)
|
||||||
*/
|
*/
|
||||||
public class EndGamePacket extends Packet implements Serializable{
|
public class EndGamePacket extends Packet implements Serializable{
|
||||||
|
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
package Ex6.packets;
|
package Ex6.network.packets;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import Ex6.Packet;
|
import Ex6.network.Packet;
|
||||||
import Ex6.PacketVisitor;
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet send to a client to indicate an invalid move.
|
||||||
|
*/
|
||||||
public class InvalidMovePacket extends Packet implements Serializable {
|
public class InvalidMovePacket extends Packet implements Serializable {
|
||||||
public InvalidMovePacket() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(PacketVisitor visitor) {
|
public void accept(PacketVisitor visitor) {
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
18
src/Ex6/network/packets/LeavePacket.java
Normal file
18
src/Ex6/network/packets/LeavePacket.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package Ex6.network.packets;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import Ex6.network.Packet;
|
||||||
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet sent by a client to indicate he is leaving.
|
||||||
|
*/
|
||||||
|
public class LeavePacket extends Packet implements Serializable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(PacketVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package Ex6.packets;
|
package Ex6.network.packets;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import Ex6.Packet;
|
import Ex6.network.Packet;
|
||||||
import Ex6.PacketVisitor;
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet sent to both client to indicate a new game has started (providing the symbol of the player for this game).
|
||||||
|
*/
|
||||||
public class NewGamePacket extends Packet implements Serializable {
|
public class NewGamePacket extends Packet implements Serializable {
|
||||||
|
|
||||||
private final boolean rond;
|
private final boolean rond;
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package Ex6.packets;
|
package Ex6.network.packets;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import Ex6.Packet;
|
import Ex6.network.Packet;
|
||||||
import Ex6.PacketVisitor;
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet sent by a client to draw a symbol in a cell.
|
||||||
|
*/
|
||||||
public class PlayMovePacket extends Packet implements Serializable{
|
public class PlayMovePacket extends Packet implements Serializable{
|
||||||
|
|
||||||
private final int cellIndex;
|
private final int cellIndex;
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
package Ex6.packets;
|
package Ex6.network.packets;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import Ex6.Packet;
|
import Ex6.network.Packet;
|
||||||
import Ex6.PacketVisitor;
|
import Ex6.network.PacketVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Packet envoyé par le serveur pour confirmer un coup et changer le
|
* Packet sent to both client to confirm a move and switch turns.
|
||||||
* tour.
|
|
||||||
*/
|
*/
|
||||||
public class PlayerMovePacket extends Packet implements Serializable {
|
public class PlayerMovePacket extends Packet implements Serializable {
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package Ex6.packets;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import Ex6.Packet;
|
|
||||||
import Ex6.PacketVisitor;
|
|
||||||
|
|
||||||
public class LeavePacket extends Packet implements Serializable{
|
|
||||||
public LeavePacket() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(PacketVisitor visitor) {
|
|
||||||
visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user