91 Commits

Author SHA1 Message Date
Johannes Schmelz
d26c29b561 Merge branch 'logic' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into logic 2024-11-18 17:11:40 +01:00
Johannes Schmelz
7246133363 refactor models 2024-11-18 17:10:36 +01:00
Johannes Schmelz
4279d130dd Merge branch 'logic' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into logic 2024-11-18 16:53:11 +01:00
Johannes Schmelz
e38a7e8cdd Merge branch 'gui' into 'main'
Gui

See merge request progproj/gruppen-ht24/Gruppe-02!10
2024-11-18 15:46:18 +00:00
Johannes Schmelz
dbd9dd0001 refactor models 2024-11-18 16:43:54 +01:00
Dennis_Malkmus
ccb1743a01 Einfügen Übergangsmodell (6 gefärbte Spielsteine) 2024-11-18 14:57:02 +01:00
Dennis_Malkmus
4ac897693c Merge remote-tracking branch 'origin/gui' into gui 2024-11-18 14:56:48 +01:00
Dennis_Malkmus
dfb962e9fe Einfügen Übergangsmodell (6 gefärbte Spielsteine) 2024-11-18 14:53:51 +01:00
Luca Puderbach
f97cbd778d Hintergrund + Felder ID hinzugefügt 2024-11-18 09:09:05 +01:00
Luca Puderbach
e889a61d43 Merge branch 'gui' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into gui 2024-11-18 08:14:10 +01:00
Luca Puderbach
a33d422547 Protoyp Würfel eingefügt 2024-11-18 08:14:05 +01:00
Yvonne Schmidt
c4b4001b96 Merge remote-tracking branch 'origin/gui' into gui 2024-11-18 08:07:11 +01:00
Luca Puderbach
20949b641e Merge branch 'gui' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into gui 2024-11-18 08:05:35 +01:00
Luca Puderbach
56e2174b6d Figur ist jetzt flawless übers Spielfeld am flyen 2024-11-18 08:02:53 +01:00
Yvonne Schmidt
acfeebca14 Startmenü fertig 2024-11-18 07:52:47 +01:00
Yvonne Schmidt
cc47acbd3f Merge remote-tracking branch 'origin/gui' into gui 2024-11-18 07:27:21 +01:00
Yvonne Schmidt
869e16406d eigenes GUI Styling eingeführt 2024-11-18 07:26:31 +01:00
Luca Puderbach
1c99da4fc5 Hinzugefügt Kamera und 1. Spielfigut 2024-11-18 07:25:15 +01:00
Yvonne Schmidt
b74ca4e702 eigenes GUI Styling eingeführt 2024-11-18 07:04:44 +01:00
Luca Puderbach
5fde7451c8 Verbesserung TestWorld 2024-11-18 06:54:38 +01:00
Luca Puderbach
6fd9209336 Angleichen MonopolyApp 2024-11-18 05:58:18 +01:00
Luca Puderbach
0213dc3560 Merge branch 'gui' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into gui 2024-11-18 05:55:15 +01:00
Luca Puderbach
e73c434c1b Angleichen Monopolyapp 2024-11-18 05:55:10 +01:00
Johannes Schmelz
b135f3fa50 idk 2024-11-18 05:51:31 +01:00
Johannes Schmelz
0e580a3180 Figure Syncronizer now added 2024-11-18 05:38:04 +01:00
Yvonne Schmidt
8e3cb43244 eigenes GUI Styling eingeführt 2024-11-18 05:32:33 +01:00
Johannes Schmelz
6dcfb92dba resolve merge conflict 2024-11-18 05:07:41 +01:00
Johannes Schmelz
a7ea5773da resolve merge conflict 2024-11-18 05:05:37 +01:00
Johannes Schmelz
5b66131d30 Merge branch 'logic' into 'gui'
Refactor

See merge request progproj/gruppen-ht24/Gruppe-02!9
2024-11-18 04:01:41 +00:00
Johannes Schmelz
cac06dda7d Refactor 2024-11-18 04:01:41 +00:00
Johannes Schmelz
fc5315d35a Merge branch 'logic' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into logic 2024-11-18 05:00:36 +01:00
Johannes Schmelz
c7c2d95aed refactor for java 20 2024-11-18 05:00:31 +01:00
Johannes Schmelz
84e68d9fa8 Merge branch 'gui' into 'logic'
Gui

See merge request progproj/gruppen-ht24/Gruppe-02!8
2024-11-18 03:50:55 +00:00
Johannes Schmelz
465aef567e Gui 2024-11-18 03:50:54 +00:00
Johannes Schmelz
12c1c97a99 Merge branch 'logic' into 'gui'
# Conflicts:
#   Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java
#   Projekte/monopoly/model/src/main/java/pp/monopoly/notification/Sound.java
2024-11-18 03:50:41 +00:00
Luca Puderbach
af4b4243ea Resolve merge conflicts 2024-11-18 04:42:30 +01:00
Johannes Schmelz
5189c74058 Merge branch 'gui' into 'main'
Gui

See merge request progproj/gruppen-ht24/Gruppe-02!7
2024-11-18 03:14:39 +00:00
Johannes Schmelz
fffd32e13c Merge branch 'main' into 'gui'
# Conflicts:
#   Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy
#   Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameSound.java
#   Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java
#   Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java
#   Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java
#   Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java
#   Projekte/monopoly/model/src/main/resources/monopoly.properties
2024-11-18 03:14:07 +00:00
Johannes Schmelz
b37a5095f0 client recieve message logic 2024-11-18 03:20:39 +01:00
Johannes Schmelz
951b4c198c Server bug fix. Add player did not correspond with the params 2024-11-18 00:24:15 +01:00
Yvonne Schmidt
8698eed273 Tooolbar implementiert 2024-11-17 22:33:58 +01:00
Yvonne Schmidt
1af8614b35 Tooolbar in Testworld implementiert 2024-11-17 22:33:12 +01:00
Johannes Schmelz
11faeeaf45 Merge branch 'Testhandbuch' into 'main'
Erste Tests

See merge request progproj/gruppen-ht24/Gruppe-02!5
2024-11-17 18:28:55 +00:00
Johannes Schmelz
9d0fcc3370 Erste Tests 2024-11-17 18:28:55 +00:00
Johannes Schmelz
2f6d6037de Merge branch 'logic' into 'main'
Serverseitige Logik

See merge request progproj/gruppen-ht24/Gruppe-02!6
2024-11-17 18:25:32 +00:00
Johannes Schmelz
1d69bcc814 refactor and bug fixes 2024-11-17 17:43:05 +01:00
Johannes Schmelz
62c363068b fixed bug with client server messages 2024-11-16 18:57:52 +01:00
Johannes Schmelz
d2e0b8187b implemented PlayerStates 2024-11-16 18:42:05 +01:00
Johannes Schmelz
628b98af9b recive logic for messages 2024-11-16 18:03:19 +01:00
Johannes Schmelz
627e3dbd7f added fine to FineField 2024-11-16 17:53:07 +01:00
Yvonne Schmidt
61b88f6bf8 Merge remote-tracking branch 'origin/gui' into gui 2024-11-15 18:39:47 +01:00
Yvonne Schmidt
8ab37e73c1 Anpassung des Button designs 2024-11-15 18:39:21 +01:00
Luca Puderbach
afdf43ebf1 Übergang zu TestMap verbessert 2024-11-15 08:36:16 +01:00
Luca Puderbach
2e0d1c059d TestWord eingefügt 2024-11-15 07:00:55 +01:00
Yvonne Schmidt
2b772199b0 Merge remote-tracking branch 'origin/gui' into gui
# Conflicts:
#	Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java
2024-11-15 06:49:13 +01:00
Yvonne Schmidt
c37b850798 Buttons im Startmenü angepasst 2024-11-15 06:48:25 +01:00
Luca Puderbach
c31f924d77 CreateGame fertig für feinschliff (Icon test) 2024-11-15 06:25:21 +01:00
Luca Puderbach
ed87a6167d Verbesserung SettingsMenu 2024-11-15 05:36:57 +01:00
Yvonne Schmidt
0d86ba0ca9 Merge remote-tracking branch 'origin/gui' into gui
# Conflicts:
#	Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java
2024-11-15 05:12:39 +01:00
Luca Puderbach
f2fd283d06 Positionierung StartMenu Buttons & SettingsMenu 2024-11-15 05:04:25 +01:00
Johannes Schmelz
10b978debf fixed addPlayer 2024-11-15 04:27:32 +01:00
Luca Puderbach
85756713df Erweitern des Setting und CreateGameMenü 2024-11-15 03:23:27 +01:00
Johannes Schmelz
3bdfd6a78a TradeHandler logic 2024-11-15 03:19:48 +01:00
Johannes Schmelz
e59ab4a320 cleintmessages logic 2024-11-15 03:06:07 +01:00
Luca Puderbach
15b3902bd3 SettingsMenü Anpassungen 2024-11-15 02:31:12 +01:00
Yvonne Schmidt
b622d66942 Buttons im Startmenü ausgerichtet 2024-11-15 02:28:10 +01:00
Luca Puderbach
12ef219064 Änderungs des StartBildschirms 2024-11-15 01:25:09 +01:00
Luca Puderbach
853b52b52d Implementierung eines grundlegenden Startmenüs 2024-11-15 00:39:47 +01:00
Johannes Schmelz
7a2ad1d31a corrected server states 2024-11-14 23:50:18 +01:00
Johannes Schmelz
c5ad476eaf added rent payment logic
added player on field logic
2024-11-14 23:50:06 +01:00
Filip Szepielewicz
12978ff410 T001 - T034 erster versuch 2024-11-14 21:54:27 +01:00
Yvonne Schmidt
caa45097c3 klassen angepasst 2024-11-14 21:31:47 +01:00
Johannes Schmelz
232e3a117c corrected rent prices 2024-11-14 01:08:22 +01:00
Johannes Schmelz
3668382911 FineField in Player 2024-11-13 23:50:06 +01:00
Johannes Schmelz
fafa53ffb7 rent calculation 2024-11-13 23:49:37 +01:00
Johannes Schmelz
44673fd57e createBoard method 2024-11-13 23:30:04 +01:00
Johannes Schmelz
f7149f225c Interpreter fuer messages hinzugefuegt 2024-11-13 18:54:24 +01:00
Johannes Schmelz
6773e18d34 Message Klassen hinzugefuegt 2024-11-13 14:11:20 +01:00
Johannes Schmelz
19a9b06f3c Merge branch 'gui' into 'main'
First compileable version of Client

See merge request progproj/gruppen-ht24/Gruppe-02!4
2024-11-13 12:42:37 +00:00
Johannes Schmelz
7ee2273761 First compileable version of Client 2024-11-13 12:42:37 +00:00
Johannes Schmelz
86b8297c9d fixed bugs, game now compiles 2024-11-13 13:31:02 +01:00
Yvonne Schmidt
17533112a1 Startmenü implementiert und Spielmenü Stummel erstellt 2024-11-12 23:30:44 +01:00
Johannes Schmelz
2cc1a338ec added Figure to Player for visual representation 2024-11-12 22:36:02 +01:00
Johannes Schmelz
81731247c7 added Fields 2024-11-12 22:35:41 +01:00
Johannes Schmelz
65c85aacf0 started UC-gameplay-15 2024-11-12 22:34:46 +01:00
Johannes Schmelz
dca23151a8 added Visitors 2024-11-12 22:33:10 +01:00
Johannes Schmelz
25305760c5 added Player 2024-11-12 22:32:55 +01:00
Yvonne Schmidt
3ef2459ad0 Hintergrundfarbe der Menüs zu Weiß geändert 2024-11-12 21:32:46 +01:00
Johannes Schmelz
29a56f42a8 Merge branch 'main' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 2024-11-12 14:53:08 +01:00
Johannes Schmelz
3784348f91 init for monopoly 2024-11-12 14:53:04 +01:00
Johannes Schmelz
188ec03abd working version of battleship 2024-11-12 00:37:05 +01:00
325 changed files with 1252541 additions and 147464 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -10,8 +10,6 @@ dependencies {
implementation libs.jme3.desktop
implementation libs.jme3.effects
implementation project(path: ':battleship:server')
implementation project(path: ':battleship:server')
runtimeOnly libs.jme3.awt.dialogs
runtimeOnly libs.jme3.plugins

View File

@@ -22,10 +22,10 @@ import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.style.BaseStyles;
import pp.battleship.client.gui.BattleAppState;
import pp.battleship.client.gui.EditorAppState;
import pp.battleship.client.gui.GameMusic;
import pp.battleship.client.gui.SeaAppState;
import pp.battleship.game.client.BattleshipClient;
import pp.battleship.game.client.ClientGameLogic;
import pp.battleship.game.client.GameMusic;
import pp.battleship.game.client.ServerConnection;
import pp.battleship.game.singlemode.BattleshipClientConfig;
import pp.battleship.game.singlemode.ServerConnectionMockup;
@@ -266,24 +266,14 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
flyCam.setEnabled(false);
stateManager.detach(stateManager.getState(StatsAppState.class));
stateManager.detach(stateManager.getState(DebugKeysAppState.class));
atttachGameMusic();
attachGameSound();
attachGameMusic();
stateManager.attachAll(new EditorAppState(), new BattleAppState(), new SeaAppState());
}
private void atttachGameMusic() {
final GameMusic gameSound = new GameMusic();
gameSound.setEnabled(GameMusic.enabledInPreferences());
stateManager.attach(gameSound);
}
/**
* Attaches the game sound state and sets its initial enabled state.
*
*/
private void attachGameSound() {
final GameSound gameSound = new GameSound();
@@ -292,6 +282,15 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
stateManager.attach(gameSound);
}
/**
* Attaches the background music state and sets its initial enabled state.
*/
private void attachGameMusic() {
final GameMusic gameSound = new GameMusic();
gameSound.setEnabled(GameMusic.enabledInPreferences());
stateManager.attach(gameSound);
}
/**
* Updates the application state every frame.
* This method is called once per frame during the game loop.

View File

@@ -12,8 +12,6 @@ import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import pp.battleship.game.client.ClientGameLogic;
/**
* Abstract class representing a state in the Battleship game.
* Extends the AbstractAppState from jMonkeyEngine to manage state behavior.

View File

@@ -34,6 +34,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
private AudioNode splashSound;
private AudioNode shipDestroyedSound;
private AudioNode explosionSound;
private AudioNode shellFlyingSound;
/**
* Checks if sound is enabled in the preferences.
@@ -78,6 +79,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
shellFlyingSound = loadSound(app, "Sound/Effects/shell_flying.wav");
}
/**
@@ -87,7 +89,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
* @param name The name of the sound file.
* @return The loaded AudioNode.
*/
public AudioNode loadSound(Application app, String name) {
private AudioNode loadSound(Application app, String name) {
try {
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
sound.setLooping(false);
@@ -124,12 +126,27 @@ public class GameSound extends AbstractAppState implements GameEventListener {
shipDestroyedSound.playInstance();
}
/**
* Plays the shell flying sound effect.
*/
public void shellFly() {
if (isEnabled() && shellFlyingSound != null) {
shellFlyingSound.playInstance();
}
}
/**
* Handles a recieved {@code SoundEvent} and plays the according sound.
*
* @param event the Sound event to be processed
*/
@Override
public void receivedEvent(SoundEvent event) {
switch (event.sound()) {
case EXPLOSION -> explosion();
case SPLASH -> splash();
case DESTROYED_SHIP -> shipDestroyed();
case SHELL_FLYING -> shellFly();
}
}
}

View File

@@ -11,7 +11,9 @@ import com.simsilica.lemur.Button;
import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.ElementId;
import pp.battleship.game.client.GameMusic;
import pp.battleship.client.gui.GameMusic;
import pp.battleship.client.gui.VolumeSlider;
import pp.dialog.Dialog;
import pp.dialog.StateCheckboxModel;
import pp.dialog.TextInputDialog;
@@ -31,39 +33,33 @@ import static pp.util.PreferencesUtils.getPreferences;
class Menu extends Dialog {
private static final Preferences PREFERENCES = getPreferences(Menu.class);
private static final String LAST_PATH = "last.file.path";
// private final VolumeSlider slider;
private final BattleshipApp app;
private final Button loadButton = new Button(lookup("menu.map.load"));
private final Button saveButton = new Button(lookup("menu.map.save"));
private final VolumeSlider slider;
/**
* Constructs the Menu dialog for the Battleship application.
*
* @param app the BattleshipApp instance
*/
public Menu(BattleshipApp app) {
super(app.getDialogManager());
this.app = app;
slider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
addChild(new Label(lookup("battleship.name"), new ElementId("header"))); //NON-NLS
addChild(new Checkbox(lookup("menu.sound-enabled"),
new StateCheckboxModel(app, GameSound.class)));
// slider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
addChild(loadButton)
.addClickCommands(s -> ifTopDialog(this::loadDialog));
addChild(saveButton)
.addClickCommands(s -> ifTopDialog(this::saveDialog));
addChild(new Button(lookup("menu.return-to-game")))
.addClickCommands(s -> ifTopDialog(this::close));
addChild(new Button(lookup("menu.quit")))
.addClickCommands(s -> ifTopDialog(app::closeApp));
update();
addChild(new Checkbox(lookup("menu.sound-enabled"), new StateCheckboxModel(app, GameSound.class)));
addChild(new Checkbox(lookup("menu.background-sound-enabled"), new StateCheckboxModel(app, GameMusic.class)));
// addChild(slider);
addChild(slider);
addChild(loadButton).addClickCommands(s -> ifTopDialog(this::loadDialog));
addChild(saveButton).addClickCommands(s -> ifTopDialog(this::saveDialog));
addChild(new Button(lookup("menu.return-to-game"))).addClickCommands(s -> ifTopDialog(this::close));
addChild(new Button(lookup("menu.quit"))).addClickCommands(s -> ifTopDialog(app::closeApp));
update();
}
/**
@@ -75,14 +71,11 @@ class Menu extends Dialog {
saveButton.setEnabled(app.getGameLogic().maySaveMap());
}
/* @Override
@Override
public void update(float delta) {
slider.update();
}
*/
/**
* As an escape action, this method closes the menu if it is the top dialog.
*/

View File

@@ -12,6 +12,7 @@ import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.battleship.server.BattleshipServer;
import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
@@ -35,9 +36,10 @@ class NetworkDialog extends SimpleDialog {
private final NetworkSupport network;
private final TextField host = new TextField(LOCALHOST);
private final TextField port = new TextField(DEFAULT_PORT);
// private final Button serverButton = new Button(lookup("client.server-star"));
private final Button serverButton = new Button(lookup("client.server-start"));
private String hostname;
private int portNumber;
private final Button serverButton = new Button(lookup("client.server-start"));
private Future<Object> connectionFuture;
private Dialog progressDialog;
@@ -68,11 +70,10 @@ class NetworkDialog extends SimpleDialog {
.setOkClose(false)
.setNoClose(false)
.build(this);
//Add the button to start the sever
addChild(serverButton).addClickCommands(s -> ifTopDialog(this::startServerInThread));
}
//Add the button to start the sever
/**
* Handles the action for the connect button in the connection dialog.
@@ -157,6 +158,7 @@ class NetworkDialog extends SimpleDialog {
network.getApp().errorDialog(lookup("server.connection.failed"));
network.getApp().setInfoText(e.getLocalizedMessage());
}
/**
* Starts the server in a separate thread.
*/
@@ -173,5 +175,4 @@ class NetworkDialog extends SimpleDialog {
});
serverThread.start();
}
}

View File

@@ -1,4 +1,10 @@
package pp.battleship.game.client;
package pp.battleship.client.gui;
import static pp.util.PreferencesUtils.getPreferences;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.prefs.Preferences;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
@@ -8,12 +14,9 @@ import com.jme3.asset.AssetNotFoundException;
import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.prefs.Preferences;
import static pp.util.PreferencesUtils.getPreferences;
/**
* Handles the background music beeing played. Is able to start and stop the music. Set the Volume of the Audio.
*/
public class GameMusic extends AbstractAppState{
private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
@@ -31,10 +34,10 @@ public class GameMusic extends AbstractAppState{
return PREFERENCES.getBoolean(ENABLED_PREF, true);
}
/**
/**
* Checks if sound is enabled in the preferences.
*
*
* @return float to which the volume is set
*/
public static float volumeInPreferences() {
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
@@ -42,7 +45,7 @@ public class GameMusic extends AbstractAppState{
/**
* Initializes the sound effects for the game.
* Overrides {@link com.jme3.app.state.AbstractAppState#initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)}
* Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
*
* @param stateManager The state manager
* @param app The application
@@ -50,7 +53,7 @@ public class GameMusic extends AbstractAppState{
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
music = loadSoundb(app, "Sound/Music/My_heart_will_go_on.ogg");
music = loadSound(app, "Sound/background.ogg");
setVolume(volumeInPreferences());
music.setLooping(true);
if (isEnabled() && music != null) {
@@ -65,8 +68,8 @@ public class GameMusic extends AbstractAppState{
* @param name The name of the sound file.
* @return The loaded AudioNode.
*/
private AudioNode loadSoundb(Application app, String name) {
try{
private AudioNode loadSound(Application app, String name) {
try {
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
sound.setLooping(false);
sound.setPositional(false);
@@ -78,8 +81,6 @@ public class GameMusic extends AbstractAppState{
return null;
}
/**
* Sets the enabled state of this AppState.
* Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
@@ -89,11 +90,15 @@ public class GameMusic extends AbstractAppState{
@Override
public void setEnabled(boolean enabled) {
if (isEnabled() == enabled) return;
else if(!isEnabled() && enabled) {
if (music != null) music.play();
} else if (isEnabled() && !enabled) {
if (music != null) music.stop();
if (music != null) {
if (enabled) {
music.play();
} else {
music.stop();
}
}
super.setEnabled(enabled);
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
@@ -106,9 +111,12 @@ public class GameMusic extends AbstractAppState{
setEnabled(!isEnabled());
}
/**
* Sets the volume of music
* @param vol the volume to which the music should be set
*/
public void setVolume(float vol){
music.setVolume(vol);
PREFERENCES.putFloat(VOLUME_PREF, vol);
}
}

View File

@@ -28,7 +28,7 @@ import pp.util.Position;
* and interaction between the model and the view.
*/
class MapView {
private static final float FIELD_SIZE = 40f;
public static final float FIELD_SIZE = 40f;
private static final float GRID_LINE_WIDTH = 2f;
private static final float BACKGROUND_DEPTH = -4f;
private static final float GRID_DEPTH = -1f;

View File

@@ -7,16 +7,23 @@
package pp.battleship.client.gui;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Sphere;
import pp.battleship.model.Battleship;
import pp.battleship.model.Shell;
import pp.battleship.model.Shot;
import pp.util.Position;
import static com.jme3.material.Materials.UNSHADED;
/**
* Synchronizes the visual representation of the ship map with the game model.
* It handles the rendering of ships and shots on the map view, updating the view
@@ -38,7 +45,6 @@ class MapViewSynchronizer extends ShipMapSynchronizer {
// The MapView associated with this synchronizer
private final MapView view;
private Shell shell;
/**
* Constructs a new MapViewSynchronizer for the given MapView.
@@ -125,16 +131,24 @@ class MapViewSynchronizer extends ShipMapSynchronizer {
private Geometry shipLine(float x1, float y1, float x2, float y2, ColorRGBA color) {
return view.getApp().getDraw().makeFatLine(x1, y1, x2, y2, SHIP_DEPTH, color, SHIP_LINE_WIDTH);
}
public void update(float deltaTime) {
if (shell != null) {
shell.updatePosition(deltaTime);
drawShell(shell.getCurrentPosition());
}
}
private void drawShell(Vector3f position){
//TODO implement
}
/**
* Creates and returns a Spatial representation of the given {@code Shell} object
* for 2D visualization in the game. The shell is represented as a circle.
*
* @param shell The {@code Shell} object to be visualized.
* @return A {@code Spatial} object representing the shell on the map.
*/
@Override
public Spatial visit(Shell shell) {
final ColorRGBA color = ColorRGBA.Black;
Geometry ellipse = new Geometry("ellipse", new Sphere(50, 50, MapView.FIELD_SIZE / 2 * 0.8f));
Material mat = new Material(view.getApp().getAssetManager(), UNSHADED); //NON-NLS
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
mat.setColor("Color", color);
ellipse.setMaterial(mat);
ellipse.addControl(new Shell2DControl(view, shell));
return ellipse;
}
}

View File

@@ -240,22 +240,23 @@ public class ParticleEffectFactory {
return smokeEmitter;
}
/**
* Creates a one-time water splash particle emitter.
*
*
* @return a configured one-time water splash particle emitter
*/
public ParticleEmitter createWaterSplash() {
// Create a new particle emitter for the splash effect
ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30);
// Set the shape of the emitter, making particles emit from a point or small area
waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f));
// Start and end colors for water (blue, fading out)
waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f)); // Light blue at start
waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f)); // Transparent at the end
// Particle size: small at start, larger before fading out
waterSplash.setStartSize(0.1f);
waterSplash.setEndSize(0.3f);
@@ -266,14 +267,14 @@ public class ParticleEffectFactory {
// Gravity: Pull the water particles downwards
waterSplash.setGravity(0, -9.81f, 0); // Earth's gravity simulation
// Velocity: Give particles an initial burst upward (simulates splash)
waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0));
waterSplash.getParticleInfluencer().setVelocityVariation(0.6f); // Add randomness to splash
// Set how many particles are emitted per second (0 to emit all particles at once)
waterSplash.setParticlesPerSec(0);
// Load a texture for the water splash (assuming a texture exists at this path)
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png"));
@@ -281,5 +282,4 @@ public class ParticleEffectFactory {
return waterSplash;
}
}

View File

@@ -20,8 +20,10 @@ import com.jme3.scene.shape.Cylinder;
import pp.battleship.client.BattleshipApp;
import pp.battleship.model.Battleship;
import pp.battleship.model.Rotation;
import pp.battleship.model.Shell;
import pp.battleship.model.ShipMap;
import pp.battleship.model.Shot;
import static java.util.Objects.requireNonNull;
import static pp.util.FloatMath.HALF_PI;
import static pp.util.FloatMath.PI;
@@ -34,13 +36,11 @@ import static pp.util.FloatMath.PI;
*/
class SeaSynchronizer extends ShipMapSynchronizer {
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
private static final String UX23 = "Models/UX23/UX23.j3o";
private static final String BOJE = "Models/Boje/Boje.j3o";
private static final String ALIENSHIP = "Models/Alienship/Alienship.j3o";
private static final String MARLOW66 = "Models/Marlow66/Marlow66.j3o";
private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o";
private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS
private static final String BOAT_SMALL_MODEL = "Models/BoatSmall/12219_boat_v2_L2.j3o"; //NON-NLS
private static final String CV_MODEL = "Models/CV/CV.j3o"; //NON-NLS
private static final String BATTLE_MODEL = "Models/Battle/Battle.j3o"; //NON-NLS
private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
private static final String COLOR = "Color"; //NON-NLS
private static final String SHIP = "ship"; //NON-NLS
private static final String SHOT = "shot"; //NON-NLS
@@ -80,13 +80,49 @@ class SeaSynchronizer extends ShipMapSynchronizer {
return shot.isHit() ? handleHit(shot) : handleMiss(shot);
}
/**
/**
* Handles a miss by representing it with a blue cylinder
* and attaching a water splash effect to it.
* @param shot the shot to be processed
* @return a Spatial simulating a miss with water splash effect
*/
private Spatial handleMiss(Shot shot) {
Node shotNode = new Node("ShotNode");
Geometry shotCylinder = createCylinder(shot);
shotNode.attachChild(shotCylinder);
ParticleEmitter waterSplash = particleFactory.createWaterSplash();
waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
shotNode.attachChild(waterSplash);
waterSplash.emitAllParticles();
return shotNode;
}
/**
* Handles the sinking animation and removal of ship if destroyed
* @param ship the ship to be sunk
*/
private void sinkAndRemoveShip(Battleship ship) {
Battleship wilkeningklaunichtmeinencode = ship;
final Node shipNode = (Node) getSpatial(wilkeningklaunichtmeinencode);
if (shipNode == null) return;
// Add sinking control to animate the sinking
shipNode.addControl(new SinkingControl(shipNode));
// Add particle effects
ParticleEmitter bubbles = particleFactory.createWaterSplash();
bubbles.setLocalTranslation(shipNode.getLocalTranslation());
shipNode.attachChild(bubbles);
bubbles.emitAllParticles();
}
/**
* Handles a hit by attaching its representation to the node that
* contains the ship model as a child so that it moves with the ship.
*
* @param shot a hit
* @return always null to prevent the representation from being attached
* to the items node as well
* @return always null to prevent the representation from being attached to the items node as well
*/
private Spatial handleHit(Shot shot) {
final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship");
@@ -133,21 +169,14 @@ class SeaSynchronizer extends ShipMapSynchronizer {
shockwave.emitAllParticles();
flame.emitAllParticles();
roundSpark.emitAllParticles();
//Checks if ship is destroyed and triggers animation accordingly
if (ship.isDestroyed()) {
sinkAndRemoveShip(ship);
}
return null;
}
private Spatial handleMiss(Shot shot) {
Node shotNode = new Node("ShotNode");
Geometry shotCylinder = createCylinder(shot);
shotNode.attachChild(shotCylinder);
ParticleEmitter waterSplash = particleFactory.createWaterSplash();
waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
shotNode.attachChild(waterSplash);
waterSplash.emitAllParticles();
return shotNode;
}
/**
* Creates a cylinder geometry representing the specified shot.
@@ -201,9 +230,9 @@ class SeaSynchronizer extends ShipMapSynchronizer {
private Spatial createShip(Battleship ship) {
switch (ship.getLength()) {
case 4: return createBattleship(ship);
case 3: return createMarlow66(ship);
case 2: return createUX23(ship);
case 1: return createAllienship(ship);
case 3: return createCV(ship);
case 2: return createBattle(ship);
case 1: return createSmallship(ship);
default: return createBox(ship);
}
}
@@ -260,37 +289,52 @@ class SeaSynchronizer extends ShipMapSynchronizer {
return model;
}
private Spatial createAllienship(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(ALIENSHIP);
/**
* Creates a detailed 3D model to represent a small tug boat.
*
* @param ship the battleship to be represented
* @return the spatial representing a small tug boat
*/
private Spatial createSmallship(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(BOAT_SMALL_MODEL);
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
model.scale(0.10f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;}
private Spatial createUX23(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(UX23);
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
// model.move(0f, -0.05f, 0f);
model.scale(0.89f);
model.scale(0.0005f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;
}
private Spatial createMarlow66(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(MARLOW66);
/**
* Creates a detailed 3D model to represent a "German WWII UBoat".
*
* @param ship the battleship to be represented
* @return the spatial representing the "German WWII UBoat"
*/
private Spatial createCV(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(CV_MODEL);
model.rotate(0f, calculateRotationAngle(ship.getRot()), 0f);
model.move(0f, 0.25f, 0f);
model.scale(0.135f);
model.scale(0.85f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;
}
/**
* Creates a detailed 3D model to represent a battleship.
*
* @param ship the battleship to be represented
* @return the spatial representing a battleship
*/
private Spatial createBattle(Battleship ship) {
final Spatial model = app.getAssetManager().loadModel(BATTLE_MODEL);
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
model.move(0f, -0.06f, 0f);
model.scale(0.27f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;
@@ -310,4 +354,25 @@ class SeaSynchronizer extends ShipMapSynchronizer {
case UP -> PI;
};
}
/**
* Creates and returns a 3D model representation of the given {@code Shell} object
* for visualization in the game.
*
* @param shell The {@code Shell} object to be visualized.
* @return A {@code Spatial} object representing the 3D model of the shell.
*/
@Override
public Spatial visit(Shell shell) {
final Spatial model = app.getAssetManager().loadModel("Models/Shell/shell.j3o");
model.setLocalScale(.05f);
model.setShadowMode(ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), LIGHTING);
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture("Models/Shell/shell_color.png"));
mat.setReceivesShadows(true);
model.setMaterial(mat);
model.addControl(new ShellControl(shell));
return model;
}
}

View File

@@ -0,0 +1,57 @@
package pp.battleship.client.gui;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import pp.battleship.model.Shell;
import pp.util.Position;
/**
* Controls the 2D representation of a {@code Shell} in the game, updating its position
* based on the shell's current state in the game model. The {@code Shell2DControl} class
* is responsible for translating the shell's 3D position to a 2D view position within
* the game's map view.
*/
public class Shell2DControl extends AbstractControl {
private final Shell shell;
private final MapView view;
/**
* Constructs a new {@code Shell2DControl} to manage the 2D visualization of the given {@code Shell}.
*
* @param view The {@code MapView} used to get information about the map to display.
* @param shell The {@code Shell} being visualized.
*/
public Shell2DControl(MapView view, Shell shell){
this.shell = shell;
this.view = view;
}
/**
* Updates the position of the shell's 2D representation based on the shell's current
* 3D position in the game model. The position is mapped from model space to view space
* coordinates and translated to the appropriate location within the {@code MapView}.
*
* @param tpf Time per frame, representing the time elapsed since the last frame.
*/
@Override
protected void controlUpdate(float tpf) {
Vector3f shellPos = shell.getPosition();
Position viewPos = view.modelToView(shellPos.x, shellPos.z);
spatial.setLocalTranslation(viewPos.getX() + MapView.FIELD_SIZE / 2, viewPos.getY() + MapView.FIELD_SIZE / 2, 0);
}
/**
* This method is called during the rendering phase, but it does not perform any
* operations in this implementation as the control only influences the spatial's
* transformation, not its rendering process.
*
* @param rm the RenderManager rendering the controlled Spatial (not null)
* @param vp the ViewPort being rendered (not null)
*/
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// No rendering-specific behavior required for this control
}
}

View File

@@ -0,0 +1,58 @@
package pp.battleship.client.gui;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import pp.battleship.model.Shell;
import static pp.util.FloatMath.PI;
/**
* Controls the 3D representation of a {@code Shell} in the game, updating its position
* and rotation based on the shell's current state in the game model. The {@code ShellControl}
* class ensures that the spatial associated with the shell is positioned and oriented correctly
* within the world.
*/
public class ShellControl extends AbstractControl {
private final Shell shell;
/**
* Constructs a new {@code ShellControl} to manage the 3D visualization of the given {@code Shell}.
*
* @param shell The {@code Shell} being visualized and controlled.
*/
public ShellControl(Shell shell){
super();
this.shell = shell;
}
/**
* Updates the 3D position and rotation of the shell based on its current state.
* Converts map coordinates to world coordinates and applies the shell's orientation.
*
* @param tpf Time per frame, representing the elapsed time since the last update.
*/
@Override
protected void controlUpdate(float tpf) {
Vector3f pos = shell.getPosition();
Vector3f fixed = new Vector3f(pos.z + 0.5f, pos.y, pos.x + 0.5f);
fixed.setY(pos.y);
spatial.setLocalTranslation(fixed);
spatial.setLocalRotation(shell.getRotation());
spatial.rotate(PI/2,0,0);
}
/**
* This method is called during the rendering phase, but it does not perform any
* operations in this implementation as the control only influences the spatial's
* transformation, not its rendering process.
*
* @param rm the RenderManager rendering the controlled Spatial (not null)
* @param vp the ViewPort being rendered (not null)
*/
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// No rendering-specific behavior required for this control
}
}

View File

@@ -0,0 +1,61 @@
package pp.battleship.client.gui;
import com.jme3.scene.control.AbstractControl;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
/**
* Control that handles the sinking effect for destroyed ships.
* It will gradually move the ship downwards and then remove it from the scene.
*/
class SinkingControl extends AbstractControl {
private static final float SINK_DURATION = 5f; // Duration of the sinking animation
private static final float SINK_SPEED = 0.1f; // Speed at which the ship sinks
private float elapsedTime = 0;
private final Node shipNode;
/**
* Constructs a {@code SinkingControl} object with the shipNode to be to be sunk
* @param shipNode the node to handeld
*/
public SinkingControl(Node shipNode) {
this.shipNode = shipNode;
}
/**
* Updated the Map to sink the ship
*
* @param tpf time per frame
*/
@Override
protected void controlUpdate(float tpf) {
// Update the sinking effect
elapsedTime += tpf;
// Move the ship down over time
Vector3f currentPos = shipNode.getLocalTranslation();
shipNode.setLocalTranslation(currentPos.x, currentPos.y - SINK_SPEED * tpf, currentPos.z);
// Check if sinking duration has passed
if (elapsedTime >= SINK_DURATION) {
// Remove the ship from the scene
shipNode.removeFromParent();
}
}
/**
* This method is called during the rendering phase, but it does not perform any
* operations in this implementation as the control only influences the spatial's
* transformation, not its rendering process.
*
* @param rm the RenderManager rendering the controlled Spatial (not null)
* @param vp the ViewPort being rendered (not null)
*/
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// No rendering-related code needed
}
}

View File

@@ -0,0 +1,35 @@
package pp.battleship.client.gui;
import com.simsilica.lemur.Slider;
/**
* The VolumeSlider class represents the Volume Slider in the Menu.
* It extends the Slider class and provides functionalities for setting the music volume,
* with the help of the Slider in the GUI
*/
public class VolumeSlider extends Slider {
private final GameMusic music;
private double vol;
/**
* Constructs the Volume Slider for the Menu dialog
* @param music the music instance
*/
public VolumeSlider(GameMusic music) {
super();
this.music = music;
vol = GameMusic.volumeInPreferences();
getModel().setPercent(vol);
}
/**
* when triggered it updates the volume to the value set with the slider
*/
public void update() {
if (vol != getModel().getPercent()) {
vol = getModel().getPercent();
music.setVolume( (float) vol);
}
}
}

View File

@@ -0,0 +1,182 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.battleship.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.AnimationFinishedMessage;
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 BattleshipServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
private static final Logger LOGGER = System.getLogger(BattleshipServer.class.getName());
private static final File CONFIG_FILE = new File("server.properties");
private final BattleshipConfig config = new BattleshipConfig();
private Server myServer;
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());
}
}
/**
* Starts the Battleships server.
*/
public static void main(String[] args) {
new BattleshipServer().run();
}
/**
* Creates the server.
*/
BattleshipServer() {
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(GameDetails.class);
Serializer.registerClass(StartBattleMessage.class);
Serializer.registerClass(MapMessage.class);
Serializer.registerClass(ShootMessage.class);
Serializer.registerClass(EffectMessage.class);
Serializer.registerClass(AnimationFinishedMessage.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.addMessageListener(this, AnimationFinishedMessage.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
}
}

View File

@@ -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.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);
}
}

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Some files were not shown because too many files have changed in this diff Show More