edited the 'networkDialog' and added 'battleShipLocalServer' and 'ReceivedMessage' and 'README'
edited the networkDialog so the client can decide, whether he want, to connect to a derver or create and host a new one, added the 'BattleshipLocalServer', the class responcible for creating a new server and added 'Receivedmessage' and added a README for the Background-music licence
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
package pp.battleship.client;
|
||||
|
||||
/**
|
||||
*this class is used for the server, when one player decides to host the server locally
|
||||
*/
|
||||
public class ClientServer {
|
||||
}
|
||||
@@ -30,7 +30,6 @@ public class GameSound extends AbstractAppState implements GameEventListener {
|
||||
private static final Logger LOGGER = System.getLogger(GameSound.class.getName());
|
||||
private static final Preferences PREFERENCES = getPreferences(GameSound.class);
|
||||
private static final String ENABLED_PREF = "enabled"; //NON-NLS
|
||||
|
||||
private AudioNode splashSound;
|
||||
private AudioNode shipDestroyedSound;
|
||||
private AudioNode explosionSound;
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
package pp.battleship.client;
|
||||
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.Label;
|
||||
import com.simsilica.lemur.TextField;
|
||||
import com.simsilica.lemur.component.SpringGridLayout;
|
||||
import pp.battleship.client.server.BattleshipLocalServer;
|
||||
import pp.battleship.client.server.BattleshipLocalServer;
|
||||
import pp.dialog.Dialog;
|
||||
import pp.dialog.DialogBuilder;
|
||||
import pp.dialog.SimpleDialog;
|
||||
@@ -37,6 +40,7 @@ class NetworkDialog extends SimpleDialog {
|
||||
private int portNumber;
|
||||
private Future<Object> connectionFuture;
|
||||
private Dialog progressDialog;
|
||||
private boolean localServer = false;
|
||||
|
||||
/**
|
||||
* Constructs a new NetworkDialog.
|
||||
@@ -57,6 +61,13 @@ class NetworkDialog extends SimpleDialog {
|
||||
input.addChild(new Label(lookup("port.number") + ": "));
|
||||
input.addChild(port, 1);
|
||||
|
||||
/*
|
||||
* this checkbox controls, whether it will connect to an existing server or create a separate server
|
||||
*/
|
||||
Checkbox localServerCb = new Checkbox(lookup("local.server"));
|
||||
localServerCb.setChecked(false);
|
||||
localServerCb.addClickCommands(s -> localServer = !localServer);
|
||||
|
||||
DialogBuilder.simple(app.getDialogManager())
|
||||
.setTitle(lookup("server.dialog"))
|
||||
.setExtension(d -> d.addChild(input))
|
||||
@@ -65,13 +76,14 @@ class NetworkDialog extends SimpleDialog {
|
||||
.setOkClose(false)
|
||||
.setNoClose(false)
|
||||
.build(this);
|
||||
addChild(localServerCb); // adds the checkbox to the network dialog at the bottom
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the action for the connect button in the connection dialog.
|
||||
* Tries to parse the port number and initiate connection to the server.
|
||||
*/
|
||||
private void connect() {
|
||||
private void connectServer() {
|
||||
LOGGER.log(Level.INFO, "connect to host={0}, port={1}", host, port); //NON-NLS
|
||||
try {
|
||||
hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
|
||||
@@ -84,6 +96,40 @@ private void connect() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* connects to the server, creates one, if no server is up
|
||||
*/
|
||||
public void connect() {
|
||||
if (localServer) {
|
||||
startLocalServer(); //starts a local server
|
||||
try {
|
||||
Thread.sleep(1000); // waits, to ensure, that the server in the separate Thread has enough time to start properly
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
connectServer(); // connects to the server, that was just created
|
||||
}
|
||||
else {
|
||||
connectServer(); // connects to existing server, when the server si not running, or not started by one of the clients
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*starts a local server, in another thread
|
||||
*/
|
||||
private void startLocalServer() {
|
||||
new Thread(() -> { //creates new Thread
|
||||
try {
|
||||
BattleshipLocalServer battleshipLocalServer = new BattleshipLocalServer(Integer.parseInt(port.getText())); // creates local server, with the given port numbers
|
||||
battleshipLocalServer.run(); // runs the local server
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.log(Level.ERROR, e.getMessage(), e);
|
||||
}
|
||||
}).start(); // starts the thread
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog indicating that the connection is in progress.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.battleship.client.server;
|
||||
|
||||
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.battleship.BattleshipConfig;
|
||||
import pp.battleship.game.server.Player;
|
||||
import pp.battleship.game.server.ServerGameLogic;
|
||||
import pp.battleship.game.server.ServerSender;
|
||||
import pp.battleship.message.client.ClientMessage;
|
||||
import pp.battleship.message.client.MapMessage;
|
||||
import pp.battleship.message.client.ShootMessage;
|
||||
import pp.battleship.message.server.EffectMessage;
|
||||
import pp.battleship.message.server.GameDetails;
|
||||
import pp.battleship.message.server.ServerMessage;
|
||||
import pp.battleship.message.server.StartBattleMessage;
|
||||
import pp.battleship.model.Battleship;
|
||||
import pp.battleship.model.IntPoint;
|
||||
import pp.battleship.model.Shot;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Server implementing the visitor pattern as MessageReceiver for ClientMessages
|
||||
*/
|
||||
public class BattleshipLocalServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
|
||||
private static final Logger LOGGER = System.getLogger(BattleshipLocalServer.class.getName());
|
||||
private static final File CONFIG_FILE = new File("server.properties");
|
||||
private final BattleshipConfig config = new BattleshipConfig();
|
||||
private Server myServer;
|
||||
private static int PORT_NUMBER;
|
||||
private final ServerGameLogic logic;
|
||||
private final BlockingQueue<ReceivedMessage> 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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this constructs the local battleship server
|
||||
*
|
||||
* @param portNumber the port number, the local-host should be accessed at
|
||||
*/
|
||||
public BattleshipLocalServer(int portNumber) {
|
||||
config.readFromIfExists(CONFIG_FILE);
|
||||
PORT_NUMBER = portNumber;
|
||||
LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
|
||||
logic = new ServerGameLogic(this, config);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
startServer();
|
||||
while (true)
|
||||
processNextMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* this method is used to start a server
|
||||
*/
|
||||
private void startServer() {
|
||||
try {
|
||||
LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS
|
||||
myServer = Network.createServer(PORT_NUMBER); // creates new server, with the port given before
|
||||
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(GameDetails.class);
|
||||
Serializer.registerClass(StartBattleMessage.class);
|
||||
Serializer.registerClass(MapMessage.class);
|
||||
Serializer.registerClass(ShootMessage.class);
|
||||
Serializer.registerClass(EffectMessage.class);
|
||||
Serializer.registerClass(Battleship.class);
|
||||
Serializer.registerClass(IntPoint.class);
|
||||
Serializer.registerClass(Shot.class);
|
||||
}
|
||||
|
||||
private void registerListeners() {
|
||||
myServer.addMessageListener(this, MapMessage.class);
|
||||
myServer.addMessageListener(this, ShootMessage.class);
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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.battleship.client.server;
|
||||
|
||||
import pp.battleship.message.client.ClientInterpreter;
|
||||
import pp.battleship.message.client.ClientMessage;
|
||||
|
||||
record ReceivedMessage(ClientMessage message, int from) {
|
||||
void process(ClientInterpreter interpreter) {
|
||||
message.accept(interpreter, from);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
The soundtrack for the background-music is from:
|
||||
https://pixabay.com/music/future-bass-nightfall-future-bass-music-228100/
|
||||
the soundtrack is free to use
|
||||
@@ -40,3 +40,4 @@ map.doesnt.fit=The map doesn't fit to this game
|
||||
background.music.checkbox= Background music on/off
|
||||
background.music.volume= Background volume
|
||||
player.submitted.invalid.map= invalid map submitted
|
||||
local.server= instantiate local server
|
||||
|
||||
@@ -40,4 +40,5 @@ map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel
|
||||
background.music.checkbox= Musik an/aus
|
||||
background.music.volume= Musiklautst<EFBFBD>rke
|
||||
player.submitted.invalid.map= Invalide karte gegeben
|
||||
local.server=Lokalen Server erstellen
|
||||
|
||||
|
||||
Reference in New Issue
Block a user