From 9e28deedf7c76f03455e77b822aaa72825563136 Mon Sep 17 00:00:00 2001 From: Luca Puderbach Date: Mon, 18 Nov 2024 18:30:35 +0100 Subject: [PATCH] Grundlagen Lobby --- .../java/pp/monopoly/client/MonopolyApp.java | 2 + .../java/pp/monopoly/client/StartMenu.java | 1 - .../monopoly/client/gui/CreateGameMenu.java | 69 ++++---- .../client/server/MonopolyServer.java | 167 ++++++++++++++++++ .../client/server/ReceivedMessages.java | 17 ++ 5 files changed, 223 insertions(+), 33 deletions(-) create mode 100644 Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/MonopolyServer.java create mode 100644 Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/ReceivedMessages.java diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java index baac5f7..b2dfdbb 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java @@ -206,4 +206,6 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga guiNode.detachAllChildren(); // Entferne die GUI StartMenu.createStartMenu(this); // Zeige das Startmenü erneut } + + } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java index e1796cc..302bd55 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java @@ -1,7 +1,6 @@ package pp.monopoly.client; import com.jme3.material.Material; -import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java index 3705593..f62a805 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java @@ -1,8 +1,5 @@ package pp.monopoly.client.gui; -import com.jme3.input.KeyInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.KeyTrigger; import com.jme3.material.Material; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; @@ -17,6 +14,7 @@ import com.simsilica.lemur.component.SpringGridLayout; import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.StartMenu; +import pp.monopoly.client.server.MonopolyServer; /** * CreateGameMenu class represents the menu for creating a new game. @@ -26,6 +24,8 @@ public class CreateGameMenu { private final MonopolyApp app; private final Container menuContainer; private Geometry background; + private Label serverStatusLabel; + private MonopolyServer monopolyServer; public CreateGameMenu(MonopolyApp app) { this.app = app; @@ -64,15 +64,18 @@ public class CreateGameMenu { cancelButton.setPreferredSize(new Vector3f(120, 40, 0)); cancelButton.addClickCommands(source -> goBackToStartMenu()); - // "Spiel hosten"-Button (funktionslos) - Button hostButton = buttonContainer.addChild(new Button("Spiel hosten")); + // "Selber hosten"-Button + Button hostButton = buttonContainer.addChild(new Button("Selber hosten")); hostButton.setPreferredSize(new Vector3f(120, 40, 0)); - // Keine Funktion zugewiesen + hostButton.addClickCommands(source -> startServer()); - // "Beitreten"-Button (ebenfalls funktionslos, falls benötigt) + // "Beitreten"-Button (vorerst funktionslos) Button joinButton = buttonContainer.addChild(new Button("Beitreten")); joinButton.setPreferredSize(new Vector3f(120, 40, 0)); - // Keine Funktion zugewiesen + + // Serverstatus-Label + serverStatusLabel = menuContainer.addChild(new Label("Serverstatus: Noch nicht gestartet")); + serverStatusLabel.setFontSize(24); // Zentrierung des Containers menuContainer.setLocalTranslation( @@ -81,10 +84,17 @@ public class CreateGameMenu { 1 // Höhere Z-Ebene für den Vordergrund ); - app.getGuiNode().attachChild(menuContainer); + app.getInputManager().addMapping("OpenTestWorld", new com.jme3.input.controls.KeyTrigger(com.jme3.input.KeyInput.KEY_T)); + app.getInputManager().addListener(new com.jme3.input.controls.ActionListener() { + @Override + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("OpenTestWorld") && isPressed) { + app.startTestWorld(); // Öffnet die TestWorld + } + } + }, "OpenTestWorld"); - // Key-Mapping für Taste "T" - setupKeyMappings(); + app.getGuiNode().attachChild(menuContainer); } /** @@ -112,28 +122,23 @@ public class CreateGameMenu { } /** - * Fügt ein Key-Mapping hinzu, um die TestWorld mit der Taste "T" zu öffnen. + * Startet den Server in einem neuen Thread. */ - private void setupKeyMappings() { - app.getInputManager().addMapping("OpenTestWorld", new KeyTrigger(KeyInput.KEY_T)); - app.getInputManager().addListener(actionListener, "OpenTestWorld"); - } - - /** - * ActionListener, um auf Tastendrücke zu reagieren. - */ - private final ActionListener actionListener = (name, isPressed, tpf) -> { - if (name.equals("OpenTestWorld") && isPressed) { - openTestWorld(); + private void startServer() { + if (monopolyServer == null) { + serverStatusLabel.setText("Serverstatus: Wird gestartet..."); + new Thread(() -> { + try { + monopolyServer = new MonopolyServer(); // Erstelle Serverinstanz + monopolyServer.run(); // Starte Serverlogik + serverStatusLabel.setText("Serverstatus: Läuft auf Port " + monopolyServer.getConfig().getPort()); + } catch (Exception e) { + serverStatusLabel.setText("Serverstatus: Fehler beim Starten"); + e.printStackTrace(); + } + }).start(); + } else { + serverStatusLabel.setText("Serverstatus: Bereits gestartet!"); } - }; - - /** - * Öffnet die TestWorld. - */ - private void openTestWorld() { - app.getGuiNode().detachChild(menuContainer); - app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild - app.startTestWorld(); // Öffne die TestWorld } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/MonopolyServer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/MonopolyServer.java new file mode 100644 index 0000000..d3c5444 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/MonopolyServer.java @@ -0,0 +1,167 @@ +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// + +package pp.monopoly.client.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.LogManager; + +import com.jme3.network.ConnectionListener; +import com.jme3.network.HostedConnection; +import com.jme3.network.Message; +import com.jme3.network.MessageListener; +import com.jme3.network.Network; +import com.jme3.network.Server; +import com.jme3.network.serializing.Serializer; + +import pp.monopoly.MonopolyConfig; +import pp.monopoly.game.server.Player; +import pp.monopoly.game.server.ServerGameLogic; +import pp.monopoly.game.server.ServerSender; +import pp.monopoly.message.client.ClientMessage; +import pp.monopoly.message.server.ServerMessage; +import pp.monopoly.model.IntPoint; + +/** + * Server implementing the visitor pattern as MessageReceiver for ClientMessages + */ +public class MonopolyServer implements MessageListener, ConnectionListener, ServerSender { + private static final Logger LOGGER = System.getLogger(MonopolyServer.class.getName()); + private static final File CONFIG_FILE = new File("server.properties"); + + private final MonopolyConfig config = new MonopolyConfig(); + private Server myServer; + private final ServerGameLogic logic; + private final BlockingQueue pendingMessages = new LinkedBlockingQueue<>(); + + static { + // Configure logging + LogManager manager = LogManager.getLogManager(); + try { + manager.readConfiguration(new FileInputStream("logging.properties")); + LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS + } + catch (IOException e) { + LOGGER.log(Level.INFO, e.getMessage()); + } + } + + /** + * Starts the Monopolys server. + */ + public static void main(String[] args) { + new MonopolyServer().run(); + } + + /** + * Creates the server. + */ + public MonopolyServer() { + config.readFromIfExists(CONFIG_FILE); + LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS + logic = new ServerGameLogic(this, config); + } + + public void run() { + startServer(); + while (true) + processNextMessage(); + } + + private void startServer() { + try { + LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS + myServer = Network.createServer(config.getPort()); + initializeSerializables(); + myServer.start(); + registerListeners(); + LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS + } + catch (IOException e) { + LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS + exit(1); + } + } + + private void processNextMessage() { + try { + pendingMessages.take().process(logic); + } + catch (InterruptedException ex) { + LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS + Thread.currentThread().interrupt(); + } + } + + private void initializeSerializables() { + Serializer.registerClass(IntPoint.class); + } + + private void registerListeners() { + myServer.addConnectionListener(this); + } + + @Override + public void messageReceived(HostedConnection source, Message message) { + LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS + if (message instanceof ClientMessage clientMessage) + pendingMessages.add(new ReceivedMessage(clientMessage, source.getId())); + } + + @Override + public void connectionAdded(Server server, HostedConnection hostedConnection) { + LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS + logic.addPlayer(hostedConnection.getId()); + } + + @Override + public void connectionRemoved(Server server, HostedConnection hostedConnection) { + LOGGER.log(Level.INFO, "connection closed: {0}", hostedConnection); //NON-NLS + final Player player = logic.getPlayerById(hostedConnection.getId()); + if (player == null) + LOGGER.log(Level.INFO, "closed connection does not belong to an active player"); //NON-NLS + else { //NON-NLS + LOGGER.log(Level.INFO, "closed connection belongs to {0}", player); //NON-NLS + exit(0); + } + } + + private void exit(int exitValue) { //NON-NLS + LOGGER.log(Level.INFO, "close request"); //NON-NLS + if (myServer != null) + for (HostedConnection client : myServer.getConnections()) //NON-NLS + if (client != null) client.close("Game over"); //NON-NLS + System.exit(exitValue); + } + + /** + * Send the specified message to the specified connection. + * + * @param id the connection id + * @param message the message + */ + public void send(int id, ServerMessage message) { + if (myServer == null || !myServer.isRunning()) { + LOGGER.log(Level.ERROR, "no server running when trying to send {0}", message); //NON-NLS + return; + } + final HostedConnection connection = myServer.getConnection(id); + if (connection != null) + connection.send(message); + else + LOGGER.log(Level.ERROR, "there is no connection with id={0}", id); //NON-NLS + } + public MonopolyConfig getConfig() { + return config; + } +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/ReceivedMessages.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/ReceivedMessages.java new file mode 100644 index 0000000..fc30df3 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/server/ReceivedMessages.java @@ -0,0 +1,17 @@ +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// + +package pp.monopoly.client.server; + +import pp.monopoly.message.client.ClientInterpreter; +import pp.monopoly.message.client.ClientMessage; + +record ReceivedMessage(ClientMessage message, int from) { + void process(ClientInterpreter interpreter) { + message.accept(interpreter, from); + } +}