Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/ch/unibas/dmi/dbis/cs108/multiplayer/server/Lobby.java
This commit is contained in:
Seraina 2022-04-16 13:15:28 +02:00
commit 8e434e3e14
24 changed files with 222 additions and 634 deletions

View File

@ -117,7 +117,6 @@ public class Game implements Runnable {
lobby.getAdmin().broadcastAnnouncementToLobby(gameOverCheck); lobby.getAdmin().broadcastAnnouncementToLobby(gameOverCheck);
lobby.removeGameFromRunningGames(this); lobby.removeGameFromRunningGames(this);
lobby.addGameToFinishedGames(this); lobby.addGameToFinishedGames(this);
lobby.setLobbyIsOpen(true);
return; return;
} }
} }

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ChatController">
<children>
<SplitPane dividerPositions="0.5565326633165829" layoutX="220.0" layoutY="53.0" orientation="VERTICAL" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<ScrollPane fx:id="scrollPaneIncomeingChatMsg" layoutX="111.0" layoutY="-2.0" prefHeight="196.0" prefWidth="598.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content>
<AnchorPane maxHeight="1.7976931348623157E308" maxWidth="-Infinity">
<children>
<VBox fx:id="inChatMsg" layoutY="-130.0" prefHeight="82.0" prefWidth="595.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
</ScrollPane>
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<AnchorPane layoutX="8.0" prefHeight="179.0" prefWidth="598.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<AnchorPane layoutX="6.0" layoutY="14.0" prefHeight="141.0" prefWidth="190.0" AnchorPane.bottomAnchor="27.0" AnchorPane.topAnchor="20.0">
<children>
<AnchorPane layoutX="32.0" layoutY="3.0" AnchorPane.bottomAnchor="130.0" AnchorPane.topAnchor="3.0">
<children>
<Button fx:id="sendButton" mnemonicParsing="false" onAction="#sendButtonPressedEvent" prefHeight="26.0" prefWidth="129.0" text="Send" />
</children>
</AnchorPane>
<AnchorPane layoutX="5.0" layoutY="110.0" AnchorPane.bottomAnchor="20.0" AnchorPane.topAnchor="110.0">
<children>
<TextField fx:id="whisperTarget" onInputMethodTextChanged="#setWhisperTarget" prefHeight="26.0" prefWidth="176.0" promptText="enter who you want to whisper to" text="whisper..." />
</children>
</AnchorPane>
<AnchorPane layoutX="9.0" layoutY="37.0" prefHeight="66.0" prefWidth="176.0" AnchorPane.bottomAnchor="60.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="40.0" AnchorPane.topAnchor="35.0">
<children>
<VBox layoutX="10.0" layoutY="10.0" prefHeight="74.0" prefWidth="200.0" spacing="4.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<RadioButton fx:id="broadcastToggle" mnemonicParsing="false" onAction="#setChatModeToBroadcast" prefHeight="18.0" prefWidth="140.0" text="Broadcast" />
<RadioButton fx:id="whisperToggle" mnemonicParsing="false" onAction="#setChatModeToWhisper" prefHeight="18.0" prefWidth="151.0" text="Whisper" />
<RadioButton fx:id="ghostToggle" mnemonicParsing="false" onAction="#setChatModeToGhost" prefHeight="18.0" prefWidth="169.0" text="Ghost" />
</children>
</VBox>
</children>
</AnchorPane>
</children>
</AnchorPane>
<TextArea fx:id="outChatMsg" layoutX="223.0" layoutY="18.0" prefHeight="132.0" prefWidth="361.0" AnchorPane.bottomAnchor="27.0" AnchorPane.topAnchor="20.0" />
</children>
</AnchorPane>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>

View File

@ -1,25 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.collections.ObservableMap;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.Pane;
/**
* Represents toggling to broadcast to everyone
*/
public class BroadcastButton extends Node implements ControlWrapper {
private static RadioButton broadcast = new RadioButton("Broadcast");
@Override
public Control getControl() {
return broadcast;
}
}

View File

@ -22,8 +22,4 @@ public class ChatApp extends Application {
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
} }
public static void main(String[] args){
}
} }

View File

@ -1,7 +1,95 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat; package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public class ChatController { import ch.unibas.dmi.dbis.cs108.multiplayer.client.Client;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
public void setChatView(){}; public class ChatController implements Initializable {
@FXML
private SplitPane chatPaneRoot;
@FXML
private VBox vBoxChatMessages;
@FXML
private Button sendButton;
@FXML
private TextField whisperTargetSelectField;
@FXML
private TextArea chatMsgField;
private Client client;
private SimpleBooleanProperty whisperTargetChosen;
public ChatController(Client client) {
this.client = client;
whisperTargetChosen = new SimpleBooleanProperty();
}
/**
* Called to initialize a controller after its root element has been completely processed.
*
* @param location The location used to resolve relative paths for the root object, or {@code
* null} if the location is not known.
* @param resources The resources used to localize the root object, or {@code null} if
*/
@Override
public void initialize(URL location, ResourceBundle resources) {
vBoxChatMessages.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
vBoxChatMessages.setMaxHeight(newValue.doubleValue());
}
});
sendButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
String msg = chatMsgField.getText();
if (!msg.isEmpty()) {
}
}
});
whisperTargetChosen.bind(whisperTargetSelectField.textProperty().isEmpty());
whisperTargetSelectField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
whisperTargetSelectField.setText(newValue);
}
});
}
/**
* @return the client who's chat controller this is
*/
public Client getClient() {
return client;
}
/**
* @param client who's gui controller this should be
*/
public void setClient(Client client) {
this.client = client;
}
} }

View File

@ -1,7 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public interface ChatMsg {
public String display(String msg);
}

View File

@ -1,5 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public interface ChatObserver {
}

View File

@ -1,81 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.collections.ObservableMap;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
/**
* If this is toggled than the client chat is operating in whisper mode.
*/
public abstract class ChatTargetToggle extends Node implements Toggle{
BooleanProperty isToggled;
ObjectProperty<ToggleGroup> myFriends;
/**
* Returns The {@link ToggleGroup} to which this {@code Toggle} belongs.
*
* @return The {@link ToggleGroup} to which this {@code Toggle} belongs.
*/
@Override
public ToggleGroup getToggleGroup() {
return myFriends.get();
}
/**
* Sets the {@link ToggleGroup} to which this {@code Toggle} belongs.
*
* @param toggleGroup The new {@link ToggleGroup}.
*/
@Override
public void setToggleGroup(ToggleGroup toggleGroup) {
myFriends.bindBidirectional((Property<ToggleGroup>) toggleGroup);
}
/**
* The {@link ToggleGroup} to which this {@code Toggle} belongs.
*
* @return the toggle group property
*/
@Override
public ObjectProperty<ToggleGroup> toggleGroupProperty() {
return myFriends;
}
/**
* Indicates whether this {@code Toggle} is selected.
*
* @return {@code true} if this {@code Toggle} is selected.
*/
@Override
public boolean isSelected() {
return isToggled.get();
}
/**
* Sets this {@code Toggle} as selected or unselected.
*
* @param selected {@code true} to make this {@code Toggle} selected.
*/
@Override
public void setSelected(boolean selected) {
this.isToggled.set(selected);
}
/**
* The selected state for this {@code Toggle}.
*
* @return the selected property
*/
@Override
public BooleanProperty selectedProperty() {
return isToggled;
}
}

View File

@ -1,78 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javax.print.attribute.standard.OrientationRequested;
/**
* This is the view of the client chat gui.
*/
public class ChatView extends Node implements NodeWithChildren, ChildNode {
private Pane root;
public void createNodeHierarchy(){
Button send = new SendButton();
AnchorPane whereTheSendFieldLives = new AnchorPane();
whereTheSendFieldLives.getChildren().add(send);
OutMsgTargetChooserNode chooseTarget = new OutMsgTargetChooserNode();
AnchorPane whereTheTargetFieldLives = new AnchorPane();
whereTheTargetFieldLives.getChildren().add(chooseTarget.getChildren());
TextArea clientOutgoingChatMsg = new TextArea();
AnchorPane whereOutTextLives = new AnchorPane();
whereOutTextLives.getChildren().add(clientOutgoingChatMsg);
TextArea target = new TextArea();
SplitPane inputOutputSeperation = new SplitPane();
SplitPane sendAndToggleSeperation = new SplitPane();
HBox buttonAndTextSeperation = new HBox();
sendAndToggleSeperation.setOrientation(Orientation.HORIZONTAL);
sendAndToggleSeperation.getItems().add(whereTheSendFieldLives);
sendAndToggleSeperation.getItems().add(whereTheTargetFieldLives);
/*
buttonAndTextSeperation.a
buttonAndTextSeperation.getItems().add(sendAndToggleSeperation);
buttonAndTextSeperation.getItems().add()
*/
inputOutputSeperation.setOrientation(Orientation.HORIZONTAL);
inputOutputSeperation.getItems().add(sendAndToggleSeperation);
}
@Override
public Pane getRootPane() {
return root;
}
@Override
public ChildNode getInstance() {
return this;
}
@Override
public void create() {
}
@Override
public Node getChildren() {
//TODO implement
return NodeWithChildren.super.getChildren();
}
}

View File

@ -1,10 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.scene.layout.Pane;
public interface ChildNode {
public Pane getRootPane();
public ChildNode getInstance();
}

View File

@ -1,9 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.scene.control.Control;
public interface ControlWrapper {
public Control getControl();
}

View File

@ -1,5 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public interface InChatObserver {
}

View File

@ -1,225 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.beans.InvalidationListener;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
/**
* This class represents an incoming chat message to be displayed by the clients gui. When creating
* an instance we should make sure we are also passing a String, otherwise {@code incomingChatMsg}
* will have a null value. For now the {@code getValue()} and {@code setValue(Object value)} are the
* main focus.
*/
public class InComingChMsg implements Property {
private String incomingChatMsg;
public InComingChMsg(String incomingChatMsg) {
this.incomingChatMsg = incomingChatMsg;
}
public InComingChMsg() {
this.incomingChatMsg = null;
}
/**
* Create a unidirection binding for this {@code Property}.
* <p>
* Note that JavaFX has all the bind calls implemented through weak listeners. This means the
* bound property can be garbage collected and stopped from being updated.
*
* @param observable The observable this {@code Property} should be bound to.
* @throws NullPointerException if {@code observable} is {@code null}
*/
@Override
public void bind(ObservableValue observable) {
}
/**
* Remove the unidirectional binding for this {@code Property}.
* <p>
* If the {@code Property} is not bound, calling this method has no effect.
*
* @see #bind(ObservableValue)
*/
@Override
public void unbind() {
}
/**
* Can be used to check, if a {@code Property} is bound.
*
* @return {@code true} if the {@code Property} is bound, {@code false} otherwise
* @see #bind(ObservableValue)
*/
@Override
public boolean isBound() {
return false;
}
/**
* Create a bidirectional binding between this {@code Property} and another one. Bidirectional
* bindings exists independently of unidirectional bindings. So it is possible to add
* unidirectional binding to a property with bidirectional binding and vice-versa. However, this
* practice is discouraged.
* <p>
* It is possible to have multiple bidirectional bindings of one Property.
* <p>
* JavaFX bidirectional binding implementation use weak listeners. This means bidirectional
* binding does not prevent properties from being garbage collected.
*
* @param other the other {@code Property}
* @throws NullPointerException if {@code other} is {@code null}
* @throws IllegalArgumentException if {@code other} is {@code this}
*/
@Override
public void bindBidirectional(Property other) {
}
/**
* Remove a bidirectional binding between this {@code Property} and another one.
* <p>
* If no bidirectional binding between the properties exists, calling this method has no effect.
* <p>
* It is possible to unbind by a call on the second property. This code will work:
*
* <blockquote><pre>
* property1.bindBirectional(property2);
* property2.unbindBidirectional(property1);
* </pre></blockquote>
*
* @param other the other {@code Property}
* @throws NullPointerException if {@code other} is {@code null}
* @throws IllegalArgumentException if {@code other} is {@code this}
*/
@Override
public void unbindBidirectional(Property other) {
}
/**
* Returns the {@code Object} that contains this property. If this property is not contained in an
* {@code Object}, {@code null} is returned.
*
* @return the containing {@code Object} or {@code null}
*/
@Override
public Object getBean() {
return null;
}
/**
* Returns the name of this property. If the property does not have a name, this method returns an
* empty {@code String}.
*
* @return the name or an empty {@code String}
*/
@Override
public String getName() {
return null;
}
/**
* Adds a {@link ChangeListener} which will be notified whenever the value of the {@code
* ObservableValue} changes. If the same listener is added more than once, then it will be
* notified more than once. That is, no check is made to ensure uniqueness.
* <p>
* Note that the same actual {@code ChangeListener} instance may be safely registered for
* different {@code ObservableValues}.
* <p>
* The {@code ObservableValue} stores a strong reference to the listener which will prevent the
* listener from being garbage collected and may result in a memory leak. It is recommended to
* either unregister a listener by calling {@link #removeListener(ChangeListener) removeListener}
* after use or to use an instance of {@link WeakChangeListener} avoid this situation.
*
* @param listener The listener to register
* @throws NullPointerException if the listener is null
* @see #removeListener(ChangeListener)
*/
@Override
public void addListener(ChangeListener listener) {
}
/**
* Removes the given listener from the list of listeners that are notified whenever the value of
* the {@code ObservableValue} changes.
* <p>
* If the given listener has not been previously registered (i.e. it was never added) then this
* method call is a no-op. If it had been previously added then it will be removed. If it had been
* added more than once, then only the first occurrence will be removed.
*
* @param listener The listener to remove
* @throws NullPointerException if the listener is null
* @see #addListener(ChangeListener)
*/
@Override
public void removeListener(ChangeListener listener) {
}
/**
* Returns the current value of this {@code ObservableValue}
*
* @return The current value
*/
@Override
public String getValue() {
return this.incomingChatMsg;
}
/**
* Set the wrapped value.
*
* @param value The new value
*/
@Override
public void setValue(Object value) {
this.incomingChatMsg = (String) value;
}
/**
* Adds an {@link InvalidationListener} which will be notified whenever the {@code Observable}
* becomes invalid. If the same listener is added more than once, then it will be notified more
* than once. That is, no check is made to ensure uniqueness.
* <p>
* Note that the same actual {@code InvalidationListener} instance may be safely registered for
* different {@code Observables}.
* <p>
* The {@code Observable} stores a strong reference to the listener which will prevent the
* listener from being garbage collected and may result in a memory leak. It is recommended to
* either unregister a listener by calling {@link #removeListener(InvalidationListener)
* removeListener} after use or to use an instance of {@link WeakInvalidationListener} avoid this
* situation.
*
* @param listener The listener to register
* @throws NullPointerException if the listener is null
* @see #removeListener(InvalidationListener)
*/
@Override
public void addListener(InvalidationListener listener) {
}
/**
* Removes the given listener from the list of listeners, that are notified whenever the value of
* the {@code Observable} becomes invalid.
* <p>
* If the given listener has not been previously registered (i.e. it was never added) then this
* method call is a no-op. If it had been previously added then it will be removed. If it had been
* added more than once, then only the first occurrence will be removed.
*
* @param listener The listener to remove
* @throws NullPointerException if the listener is null
* @see #addListener(InvalidationListener)
*/
@Override
public void removeListener(InvalidationListener listener) {
}
}

View File

@ -1,17 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.scene.Node;
/**
* Any class that represents a JavaFX node and has children should implement this interface
*/
public interface NodeWithChildren {
void create();
public default Node getChildren() {
return null;
}
void createNodeHierarchy();
}

View File

@ -1,5 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public interface OutChatObserver {
}

View File

@ -1,32 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
public class OutMsgTargetChooserNode extends ToggleGroup implements NodeWithChildren {
private Pane root;
private ObservableList<Node> targets;
@Override
public void create() {
}
@Override
public Node getChildren() {
return NodeWithChildren.super.getChildren();
}
@Override
public void createNodeHierarchy() {
this.root = new HBox();
for(Node n : targets)
root.getChildren().add(n);
}
}

View File

@ -1,5 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
public interface PropertyButton {
}

View File

@ -1,20 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.scene.Node;
import javafx.scene.control.Button;
/**
* Represents the button in the chat to send a chat message.
*/
public class SendButton extends Button implements UINode {
public SendButton() {
super("Send");
}
@Override
public void listen() {
}
}

View File

@ -1,9 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
/**
* Any class that represents a JavaFX node that takes user input should implement this interface.
*/
public interface UINode {
void listen();
}

View File

@ -1,16 +0,0 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import javafx.scene.control.Control;
import javafx.scene.control.RadioButton;
/**
* Represents the toggle for a whisper chat.
*/
public class WhisperButton implements ControlWrapper {
private static RadioButton whisper = new RadioButton("Whisper");
@Override
public Control getControl() {
return null;
}
}

View File

@ -0,0 +1,41 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class outChatCmd implements ChangeListener {
private String cmd;
private static final Protocol prtcl = new Protocol();
private static final String whisper = Protocol.whisper;
private static final String chatToAll = Protocol.chatMsgToAll;
private static final String chatToLobby = Protocol.chatMsgToLobby
public outChatCmd(String cmd, String parameters) {
this.cmd = cmd;
this.parameters = parameters;
}
public String getCmd() {
return cmd;
}
public String getParameters() {
return parameters;
}
/**
* Called when the value of an {@link ObservableValue} changes.
* <p>
* In general, it is considered bad practice to modify the observed value in this method.
*
* @param observable The {@code ObservableValue} which value changed
* @param oldValue The old value
* @param newValue
*/
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
}
}

View File

@ -314,7 +314,6 @@ public class ClientHandler implements Runnable {
Thread t = new Thread(game); Thread t = new Thread(game);
t.start(); t.start();
l.addGameToRunningGames(game); l.addGameToRunningGames(game);
l.setLobbyIsOpen(false);
} else { } else {
sendAnnouncementToClient("Only the admin can start the game"); sendAnnouncementToClient("Only the admin can start the game");
} }
@ -378,9 +377,8 @@ public class ClientHandler implements Runnable {
} }
/** /**
* The client wants to join the lobby with the index i. * The client wants to join the lobby with the index i. If the lobby is closed, the client will be notified.
* //todo: needs more doc. * @param i the number of the lobby to join
* @param i
*/ */
public void joinLobby(int i) { public void joinLobby(int i) {
Lobby l = Lobby.getLobbyFromID(i); Lobby l = Lobby.getLobbyFromID(i);
@ -388,7 +386,7 @@ public class ClientHandler implements Runnable {
if (l.getLobbyIsOpen()) { if (l.getLobbyIsOpen()) {
l.addPlayer(this); l.addPlayer(this);
} else { } else {
sendAnnouncementToClient("The game in Lobby " + l.getLobbyID() + " has already started"); sendAnnouncementToClient("The game in Lobby " + l.getLobbyID() + " has already started, or the lobby is already full.");
} }
} else { } else {
sendAnnouncementToClient("Invalid Lobby nr."); sendAnnouncementToClient("Invalid Lobby nr.");

View File

@ -23,6 +23,8 @@ public class Lobby {
* The currently ongoing game, is set, when a game is started * The currently ongoing game, is set, when a game is started
*/ */
private Game game; private Game game;
/** /**
* true by default * true by default
* true if game has not started yet, false if game has. If true, potential players can still join the game. * true if game has not started yet, false if game has. If true, potential players can still join the game.
@ -31,6 +33,8 @@ public class Lobby {
*/ */
private boolean lobbyIsOpen = true; private boolean lobbyIsOpen = true;
private boolean gameIsRunning = false;
private static final int MAX_NO_OF_CLIENTS = 6; private static final int MAX_NO_OF_CLIENTS = 6;
@ -119,6 +123,11 @@ public class Lobby {
} }
public boolean getLobbyIsOpen() { public boolean getLobbyIsOpen() {
if (lobbyClients.size() >= MAX_NO_OF_CLIENTS || gameIsRunning ) {
setLobbyIsOpen(false);
} else {
setLobbyIsOpen(true);
}
return lobbyIsOpen; return lobbyIsOpen;
} }
@ -135,12 +144,11 @@ public class Lobby {
this.lobbyIsOpen = lobbyIsOpen; this.lobbyIsOpen = lobbyIsOpen;
} }
/** /**
* Returns the ID of the lobby that the client is in. If the client is not in any * Returns the ID of the lobby that the client is in. If the client is not in any
* lobby, it returns -1. * lobby, it returns -1.
*/ */
public static int clientIsInLobby(ClientHandler h) { public static int clientIsInLobby(ClientHandler h) {
for (Lobby l: lobbies) { for (Lobby l: lobbies) {
for (ClientHandler clientHandler: l.getLobbyClients()) { for (ClientHandler clientHandler: l.getLobbyClients()) {
@ -158,8 +166,7 @@ public class Lobby {
* @param client who wants to join the lobby. * @param client who wants to join the lobby.
*/ */
public synchronized boolean addPlayer(ClientHandler client) { public synchronized boolean addPlayer(ClientHandler client) {
if (lobbyClients.size() < MAX_NO_OF_CLIENTS) { if (getLobbyIsOpen()) {
//todo: check that game hasn't started yet (handled in cleintHandler)
if (clientIsInLobby(client) == -1) { if (clientIsInLobby(client) == -1) {
lobbyClients.add(client); lobbyClients.add(client);
ClientHandler.broadcastAnnouncementToAll(client.getClientUserName() + " has joined lobby " + this.getLobbyID()); ClientHandler.broadcastAnnouncementToAll(client.getClientUserName() + " has joined lobby " + this.getLobbyID());
@ -170,7 +177,7 @@ public class Lobby {
client.sendAnnouncementToClient("You are already in lobby nr. " + clientIsInLobby(client)); client.sendAnnouncementToClient("You are already in lobby nr. " + clientIsInLobby(client));
} }
} else { } else {
client.sendAnnouncementToClient("This lobby is full. Please try joining a different lobby or create a new lobby"); client.sendAnnouncementToClient("This lobby is closed. Please try joining a different lobby or create a new lobby");
} }
return false; return false;
} }
@ -195,11 +202,19 @@ public class Lobby {
return false; return false;
} }
/**
* Adds game to list of running games and sets its lobby's gameIsRunning to true.
*/
public void addGameToRunningGames(Game game) { public void addGameToRunningGames(Game game) {
game.getLobby().gameIsRunning = true;
runningGames.add(game); runningGames.add(game);
} }
/**
* Removes game from list of running games and sets its lobby's gameIsRunning to false.
*/
public void removeGameFromRunningGames(Game game) { public void removeGameFromRunningGames(Game game) {
game.getLobby().gameIsRunning = false;
runningGames.remove(game); runningGames.remove(game);
} }
@ -213,9 +228,10 @@ public class Lobby {
*/ */
public void closeLobby() { public void closeLobby() {
lobbies.remove(this); lobbies.remove(this);
ClientHandler.broadcastAnnouncementToAll("Lobby nr. " + this.getLobbyID() + " has been closed."); //ClientHandler.broadcastAnnouncementToAll("Lobby nr. " + this.getLobbyID() + " has been closed.");
/* /*
TODO: close game when lobby is closed
Todo: theoretically, this is enough to close a lobby. Todo: theoretically, this is enough to close a lobby.
ClientHandlers dont have to manually be removed from the lobby ClientHandlers dont have to manually be removed from the lobby
since if the lobby is removed from the lobbies since if the lobby is removed from the lobbies

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<fx:root maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" type="AnchorPane" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.unibas.dmi.dbis.cs108.multiplayer.client.gui.chat.ChatController">
<children>
<SplitPane fx:id ="chatPaneRoot" dividerPositions="0.5" layoutX="214.0" layoutY="92.0" orientation="VERTICAL" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<ScrollPane layoutX="149.0" layoutY="-18.0" prefHeight="196.0" prefWidth="598.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content>
<VBox fx:id="vBoxChatMessages" prefHeight="200.0" prefWidth="581.0" />
</content>
</ScrollPane>
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<SplitPane dividerPositions="0.29797979797979796" layoutX="166.0" layoutY="8.0" prefHeight="195.0" prefWidth="598.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<SplitPane dividerPositions="0.5" layoutY="-3.0" orientation="VERTICAL" prefHeight="193.0" prefWidth="174.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<Button fx:id="sendButton" layoutX="50.0" layoutY="21.0" mnemonicParsing="false" prefHeight="92.0" prefWidth="172.0" text="Send" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<TextField fx:id="whisperTargetSelectField" layoutY="14.0" prefHeight="92.0" prefWidth="172.0" promptText="whisper..." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
<padding>
<Insets bottom="5.0" left="5.0" top="3.0" />
</padding>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TextArea fx:id="chatMsgField" layoutX="120.0" layoutY="-30.0" prefHeight="193.0" prefWidth="415.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
<padding>
<Insets bottom="5.0" right="5.0" />
</padding>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</fx:root>