diff --git a/app/src/main/java/common/Signal.java b/app/src/main/java/common/Signal.java new file mode 100644 index 0000000..6e4154d --- /dev/null +++ b/app/src/main/java/common/Signal.java @@ -0,0 +1,31 @@ +package common; + +import java.util.HashSet; +import java.util.Set; + +public class Signal { + + private final Set listeners; + + public Signal() { + this.listeners = new HashSet<>(); + } + + public void connect(Runnable listener) { + this.listeners.add(listener); + } + + public void clear() { + this.listeners.clear(); + } + + public void emit() { + for (Runnable listener : this.listeners) { + listener.run(); + } + } + + // public void disconnect(Runnable listener) { + // this.listeners.remove(listener); + // } +} diff --git a/app/src/main/java/game/Game.java b/app/src/main/java/game/Game.java new file mode 100644 index 0000000..e3aaa82 --- /dev/null +++ b/app/src/main/java/game/Game.java @@ -0,0 +1,30 @@ +package game; + +import java.util.HashMap; +import java.util.Map; + +public class Game { + + private final Map players; + + public Game() { + this.players = new HashMap<>(); + } + + public Player getPlayerById(int id) { + return players.get(id); + } + + public void addPlayer(Player player) { + players.put(player.getId(), player); + } + + public void removePlayer(int id) { + players.remove(id); + } + + public Map getPlayers() { + return players; + } + +} diff --git a/app/src/main/java/game/Player.java b/app/src/main/java/game/Player.java new file mode 100644 index 0000000..2336152 --- /dev/null +++ b/app/src/main/java/game/Player.java @@ -0,0 +1,25 @@ +package game; + +import java.io.Serializable; + +public class Player implements Serializable { + + static private final long serialVersionUID = 9999; + + private final String pseudo; + private final int id; + + public Player(int id, String pseudo) { + this.pseudo = pseudo; + this.id = id; + } + + public String getPseudo() { + return this.pseudo; + } + + public int getId() { + return this.id; + } + +} diff --git a/app/src/main/java/gui/menu/BaseView.java b/app/src/main/java/gui/menu/BaseView.java index 9992279..de5c16d 100644 --- a/app/src/main/java/gui/menu/BaseView.java +++ b/app/src/main/java/gui/menu/BaseView.java @@ -12,7 +12,7 @@ public abstract class BaseView { public abstract void render(); - public void onKill() {} + public void cleanResources() {} public void closeMenu() { this.stateMachine.popState(); diff --git a/app/src/main/java/gui/menu/ConnexionStatusView.java b/app/src/main/java/gui/menu/ConnexionStatusView.java new file mode 100644 index 0000000..82cb12f --- /dev/null +++ b/app/src/main/java/gui/menu/ConnexionStatusView.java @@ -0,0 +1,60 @@ +package gui.menu; + +import java.io.IOException; +import java.net.UnknownHostException; + +import imgui.ImGui; +import network.client.Client; +import network.server.Server; + +public class ConnexionStatusView extends BaseView { + + private Client client; + private Server server; + + public ConnexionStatusView(StateMachine stateMachine, String address, short port) throws UnknownHostException, IOException { + this(stateMachine, null, new Client(address, port)); + } + + public ConnexionStatusView(StateMachine stateMachine, short port) throws UnknownHostException, IOException { + this(stateMachine, new Server(port), new Client("localhost", port)); + } + + private ConnexionStatusView(StateMachine stateMachine, Server server, Client client) { + super(stateMachine); + this.client = client; + this.client.onConnect.connect(this::onConnect); + this.client.onClosed.connect(this::onLeave); + this.server = server; + } + + public void onConnect() { + // System.out.println("Connecté"); + this.stateMachine.pushState(new MultiPlayerView(stateMachine, client)); + } + + public void onLeave() { + // System.out.println("Quitté !"); + this.client.onDisconnect.clear(); + this.client = null; + // on passe le menu de la connexion + this.closeMenu(); + } + + @Override + public void render() { + ImGui.text("Connecting ..."); + } + + @Override + public void cleanResources() { + // System.out.println("Bye bye !"); + if (this.server != null) { + this.server.stop(); + } + if (this.client != null) { + this.client.stop(); + } + } + +} diff --git a/app/src/main/java/gui/menu/MultiMenu.java b/app/src/main/java/gui/menu/MultiMenu.java index cecd1fb..090f629 100644 --- a/app/src/main/java/gui/menu/MultiMenu.java +++ b/app/src/main/java/gui/menu/MultiMenu.java @@ -22,7 +22,7 @@ public class MultiMenu extends BaseView { ImGui.inputInt("Port", port); if (ImGui.button("Créer")) { try { - this.stateMachine.pushState(new MultiPlayerView(stateMachine, (short) port.get())); + this.stateMachine.pushState(new ConnexionStatusView(stateMachine, (short) port.get())); } catch (IOException e) { e.printStackTrace(); } @@ -37,7 +37,7 @@ public class MultiMenu extends BaseView { ImGui.inputInt("Port", port); if (ImGui.button("Rejoindre")) { try { - this.stateMachine.pushState(new MultiPlayerView(stateMachine, address.get(), (short) port.get())); + this.stateMachine.pushState(new ConnexionStatusView(stateMachine, address.get(), (short) port.get())); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/app/src/main/java/gui/menu/MultiPlayerView.java b/app/src/main/java/gui/menu/MultiPlayerView.java index 3646f1c..a4d3e83 100644 --- a/app/src/main/java/gui/menu/MultiPlayerView.java +++ b/app/src/main/java/gui/menu/MultiPlayerView.java @@ -1,53 +1,28 @@ package gui.menu; -import java.io.IOException; -import java.net.UnknownHostException; - import imgui.ImGui; import network.client.Client; -import network.server.Server; public class MultiPlayerView extends BaseView { + + private final Client client; - private Client client; - private Server server; - - /** - * Client - * - * @param stateMachine - * @param address - * @param port - * @throws IOException - * @throws UnknownHostException - */ - public MultiPlayerView(StateMachine stateMachine, String address, short port) - throws UnknownHostException, IOException { + public MultiPlayerView(StateMachine stateMachine, Client client) { super(stateMachine); - this.client = new Client(address, port); - } - - /** - * Server - * - * @param stateMachine - * @param port - * @throws IOException - */ - public MultiPlayerView(StateMachine stateMachine, short port) throws IOException { - super(stateMachine); - this.server = new Server(port); - this.client = new Client("localhost", port); + this.client = client; + this.client.onDisconnect.connect(this::onDisconnect); } @Override - public void onKill() { - if (this.server != null) { - this.server.stop(); - } - if (this.client != null) { - this.client.stop(); - } + public void closeMenu() { + this.client.forceDisconnect(); + super.closeMenu(); + } + + public void onDisconnect() { + // System.out.println("ohohohohohohoho"); + this.stateMachine.popState(); + this.client.onDisconnect.clear(); } @Override diff --git a/app/src/main/java/gui/menu/StateMachine.java b/app/src/main/java/gui/menu/StateMachine.java index 9317a25..81ea005 100644 --- a/app/src/main/java/gui/menu/StateMachine.java +++ b/app/src/main/java/gui/menu/StateMachine.java @@ -17,7 +17,7 @@ public class StateMachine { public void clear() { for (BaseView view : menus) { - view.onKill(); + view.cleanResources(); } menus.clear(); } @@ -27,13 +27,13 @@ public class StateMachine { } public void popState() { - menus.get(menus.size() - 1).onKill(); + menus.getLast().cleanResources(); menus.pop(); } private void checkEscape() { if (ImGui.isKeyPressed(ImGuiKey.Escape) && menus.size() > 1) { - popState(); + menus.getLast().closeMenu(); } } diff --git a/app/src/main/java/network/Connexion.java b/app/src/main/java/network/Connexion.java index a565012..1b1e285 100644 --- a/app/src/main/java/network/Connexion.java +++ b/app/src/main/java/network/Connexion.java @@ -20,9 +20,14 @@ public abstract class Connexion implements PacketVisitor { this.connexionThread.start(); } - public void sendPacket(Packet packet) { + public boolean isClosed() { + return this.socket.isClosed(); + } + + public synchronized void sendPacket(Packet packet) { try { objectOutputStream.writeObject(packet); + objectOutputStream.flush(); } catch (IOException e) { System.err.println("Error while sending packet ! " + e.getLocalizedMessage()); close(); diff --git a/app/src/main/java/network/ConnexionThread.java b/app/src/main/java/network/ConnexionThread.java index cba97bf..6752d92 100644 --- a/app/src/main/java/network/ConnexionThread.java +++ b/app/src/main/java/network/ConnexionThread.java @@ -5,7 +5,7 @@ import java.io.ObjectInputStream; import network.protocol.Packet; -public class ConnexionThread extends Thread{ +public class ConnexionThread extends Thread { private final Connexion connexion; private final ObjectInputStream objectInputStream; @@ -17,13 +17,16 @@ public class ConnexionThread extends Thread{ @Override public void run() { - while(!interrupted()) { + while (!interrupted()) { try { + // System.out.println(objectInputStream.available()); Object o = objectInputStream.readObject(); if (o instanceof Packet packet) { connexion.visitPacket(packet); } } catch (ClassNotFoundException | IOException e) { + e.printStackTrace(); + this.connexion.close(); break; } } diff --git a/app/src/main/java/network/client/Client.java b/app/src/main/java/network/client/Client.java index 2baa2a0..a6d8efc 100644 --- a/app/src/main/java/network/client/Client.java +++ b/app/src/main/java/network/client/Client.java @@ -3,20 +3,45 @@ package network.client; import java.io.IOException; import java.net.UnknownHostException; +import common.Signal; +import game.Game; +import game.Player; +import network.protocol.packets.LoginPacket; + public class Client { private final ClientConnexion clientConnection; + private final Game game; + + public final Signal onConnect = new Signal(); + public final Signal onDisconnect = new Signal(); + public final Signal onClosed = new Signal(); public Client(String address, short port) throws UnknownHostException, IOException { this.clientConnection = new ClientConnexion(address, port, this); + this.game = new Game(); + login("Player2" + Math.random()); + } + + public void login(String pseudo) { + System.out.println("Logging in with pseudo " + pseudo + " ..."); + this.clientConnection.sendPacket(new LoginPacket(pseudo)); } public void stop() { this.clientConnection.close(); } - public void onDisconnect() { - // do some stuff - System.out.println("OSEKOUR"); + public void addPlayer(Player player) { + this.game.addPlayer(player); } - + + public Game getGame() { + return game; + } + + public void forceDisconnect() { + this.onClosed.emit(); + stop(); + } + } diff --git a/app/src/main/java/network/client/ClientConnexion.java b/app/src/main/java/network/client/ClientConnexion.java index 42b2f1c..07ca721 100644 --- a/app/src/main/java/network/client/ClientConnexion.java +++ b/app/src/main/java/network/client/ClientConnexion.java @@ -4,14 +4,19 @@ import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; +import game.Player; import network.Connexion; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; import network.protocol.packets.KeepAlivePacket; +import network.protocol.packets.LoginPacket; +import network.protocol.packets.PlayerJoinPacket; +import network.protocol.packets.PlayerLeavePacket; public class ClientConnexion extends Connexion { private final Client client; + private Player player = null; public ClientConnexion(String address, short port, Client client) throws UnknownHostException, IOException { super(new Socket(address, port)); @@ -20,13 +25,16 @@ public class ClientConnexion extends Connexion { @Override public void close() { - super.close(); - client.onDisconnect(); + if (!this.isClosed()) { + super.close(); + client.onDisconnect.emit(); + } } @Override public void visitPacket(ConnexionInfoPacket packet) { - + this.player = this.client.getGame().getPlayerById(packet.getConnectionId()); + client.onConnect.emit(); } @Override @@ -40,4 +48,20 @@ public class ClientConnexion extends Connexion { close(); } + @Override + public void visitPacket(LoginPacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacketLogin'"); + } + + @Override + public void visitPacket(PlayerJoinPacket packet) { + this.client.addPlayer(packet.getPlayer()); + System.out.println("[Client] " + packet.getPlayer().getPseudo() + " joined the game !"); + } + + @Override + public void visitPacket(PlayerLeavePacket packet) { + this.client.getGame().removePlayer(packet.getPlayer()); + } + } diff --git a/app/src/main/java/network/protocol/PacketVisitor.java b/app/src/main/java/network/protocol/PacketVisitor.java index 73babe1..f57e0bf 100644 --- a/app/src/main/java/network/protocol/PacketVisitor.java +++ b/app/src/main/java/network/protocol/PacketVisitor.java @@ -3,6 +3,9 @@ package network.protocol; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; import network.protocol.packets.KeepAlivePacket; +import network.protocol.packets.LoginPacket; +import network.protocol.packets.PlayerJoinPacket; +import network.protocol.packets.PlayerLeavePacket; public interface PacketVisitor { @@ -13,5 +16,8 @@ public interface PacketVisitor { void visitPacket(ConnexionInfoPacket packet); void visitPacket(DisconnectPacket packet); void visitPacket(KeepAlivePacket packet); + void visitPacket(LoginPacket packet); + void visitPacket(PlayerJoinPacket packet); + void visitPacket(PlayerLeavePacket packet); } diff --git a/app/src/main/java/network/protocol/Packets.java b/app/src/main/java/network/protocol/Packets.java index 716b9be..27b5162 100644 --- a/app/src/main/java/network/protocol/Packets.java +++ b/app/src/main/java/network/protocol/Packets.java @@ -2,6 +2,6 @@ package network.protocol; public enum Packets { - ConnectionInfo, KeepAlive, Disconnect + ConnectionInfo, KeepAlive, Disconnect, Login, PlayerJoin, PlayerLeave } diff --git a/app/src/main/java/network/protocol/packets/DisconnectPacket.java b/app/src/main/java/network/protocol/packets/DisconnectPacket.java index 2d8f854..bcec9ba 100644 --- a/app/src/main/java/network/protocol/packets/DisconnectPacket.java +++ b/app/src/main/java/network/protocol/packets/DisconnectPacket.java @@ -2,9 +2,12 @@ package network.protocol.packets; import network.protocol.Packet; import network.protocol.PacketVisitor; +import network.protocol.Packets; public class DisconnectPacket extends Packet { + static private final long serialVersionUID = Packets.Disconnect.ordinal(); + private final String reason; public DisconnectPacket(String reason) { diff --git a/app/src/main/java/network/protocol/packets/LoginPacket.java b/app/src/main/java/network/protocol/packets/LoginPacket.java new file mode 100644 index 0000000..237dd3f --- /dev/null +++ b/app/src/main/java/network/protocol/packets/LoginPacket.java @@ -0,0 +1,26 @@ +package network.protocol.packets; + +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class LoginPacket extends Packet { + + static private final long serialVersionUID = Packets.Login.ordinal(); + + private final String pseudo; + + public LoginPacket(String pseudo) { + this.pseudo = pseudo; + } + + public String getPseudo() { + return pseudo; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + +} diff --git a/app/src/main/java/network/protocol/packets/PlayerJoinPacket.java b/app/src/main/java/network/protocol/packets/PlayerJoinPacket.java new file mode 100644 index 0000000..4badf2d --- /dev/null +++ b/app/src/main/java/network/protocol/packets/PlayerJoinPacket.java @@ -0,0 +1,29 @@ +package network.protocol.packets; + +import game.Player; +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class PlayerJoinPacket extends Packet{ + + static private final long serialVersionUID = Packets.PlayerJoin.ordinal(); + + private final Player player; + + public PlayerJoinPacket(Player player) { + this.player = player; + } + + public Player getPlayer() { + return player; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + + + +} diff --git a/app/src/main/java/network/protocol/packets/PlayerLeavePacket.java b/app/src/main/java/network/protocol/packets/PlayerLeavePacket.java new file mode 100644 index 0000000..b22b7cc --- /dev/null +++ b/app/src/main/java/network/protocol/packets/PlayerLeavePacket.java @@ -0,0 +1,28 @@ +package network.protocol.packets; + +import network.protocol.Packet; +import network.protocol.PacketVisitor; +import network.protocol.Packets; + +public class PlayerLeavePacket extends Packet{ + + static private final long serialVersionUID = Packets.PlayerLeave.ordinal(); + + private final int playerId; + + public PlayerLeavePacket(int playerId) { + this.playerId = playerId; + } + + public int getPlayer() { + return playerId; + } + + @Override + public void accept(PacketVisitor packetVisitor) { + packetVisitor.visitPacket(this); + } + + + +} diff --git a/app/src/main/java/network/server/Server.java b/app/src/main/java/network/server/Server.java index ac07cf7..a8d8765 100644 --- a/app/src/main/java/network/server/Server.java +++ b/app/src/main/java/network/server/Server.java @@ -5,6 +5,8 @@ import java.net.ServerSocket; import java.util.ArrayList; import java.util.List; +import game.Game; +import game.Player; import network.protocol.Packet; public class Server { @@ -13,6 +15,8 @@ public class Server { final List connexions; private final ServerAcceptThread acceptThread; private final ServerLogicThread logicThread; + private final Game game; + private int nextPlayerId = 0; public Server(short port) throws IOException { this.serverSocket = new ServerSocket(port); @@ -21,6 +25,7 @@ public class Server { this.acceptThread.start(); this.logicThread = new ServerLogicThread(this); this.logicThread.start(); + this.game = new Game(); } public void broadcastPacket(Packet packet) { @@ -32,8 +37,9 @@ public class Server { public void update() { for (var it = connexions.iterator(); it.hasNext();) { ServerConnexion connexion = it.next(); - if(!connexion.update()) { + if (!connexion.update()) { connexion.close(); + connexion.nukeConnection(); it.remove(); } } @@ -47,4 +53,15 @@ public class Server { } } + public Player addPlayer(String pseudo) { + Player p = new Player(nextPlayerId, pseudo); + this.game.addPlayer(p); + nextPlayerId++; + return p; + } + + public Game getGame() { + return game; + } + } diff --git a/app/src/main/java/network/server/ServerConnexion.java b/app/src/main/java/network/server/ServerConnexion.java index 9ac20cb..a4eb568 100644 --- a/app/src/main/java/network/server/ServerConnexion.java +++ b/app/src/main/java/network/server/ServerConnexion.java @@ -3,16 +3,21 @@ package network.server; import java.io.IOException; import java.net.Socket; +import game.Player; import network.Connexion; import network.protocol.packets.ConnexionInfoPacket; import network.protocol.packets.DisconnectPacket; import network.protocol.packets.KeepAlivePacket; +import network.protocol.packets.LoginPacket; +import network.protocol.packets.PlayerJoinPacket; +import network.protocol.packets.PlayerLeavePacket; public class ServerConnexion extends Connexion { private final Server server; private final KeepAliveHandler keepAliveHandler; private boolean shouldClose = false; + private Player player = null; public ServerConnexion(Socket socket, Server server) throws IOException { super(socket); @@ -21,21 +26,36 @@ public class ServerConnexion extends Connexion { } public boolean update() { - if (shouldClose) + if (shouldClose | isClosed()) return false; return this.keepAliveHandler.update(); } - @Override - public void close() { - sendPacket(new DisconnectPacket("Server stopped")); - super.close(); - shouldClose = true; + public void nukeConnection() { + if (player != null) { + sendPacket(new DisconnectPacket("Server stopped")); + this.server.broadcastPacket(new PlayerLeavePacket(player.getId())); + this.server.getGame().removePlayer(player.getId()); + } } @Override - public void visitPacket(ConnexionInfoPacket packet) { - throw new UnsupportedOperationException("Unimplemented method 'visitPacket'"); + public synchronized void close() { + if(shouldClose) + return; + super.close(); + shouldClose = true; + System.out.println("[Server] Closing connexion !"); + } + + private void finishLogin() { + // send players that have already joined (excluding this one) + for (Player p : this.server.getGame().getPlayers().values()) { + if (p.getId() != player.getId()) + sendPacket(new PlayerJoinPacket(p)); + } + this.server.broadcastPacket(new PlayerJoinPacket(player)); + sendPacket(new ConnexionInfoPacket(player.getId())); } @Override @@ -48,4 +68,27 @@ public class ServerConnexion extends Connexion { close(); } + @Override + public void visitPacket(LoginPacket packet) { + if (this.player != null) + return; + this.player = this.server.addPlayer(packet.getPseudo()); + finishLogin(); + } + + @Override + public void visitPacket(ConnexionInfoPacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacketConnexionInfo'"); + } + + @Override + public void visitPacket(PlayerJoinPacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacketPlayerJoin'"); + } + + @Override + public void visitPacket(PlayerLeavePacket packet) { + throw new UnsupportedOperationException("Unimplemented method 'visitPacketPlayerLeave'"); + } + }