diff --git a/src/main/java/it/polimi/ingsw/gc14/Controller/GameController.java b/src/main/java/it/polimi/ingsw/gc14/Controller/GameController.java
index 2fdd2f1..23b9752 100644
--- a/src/main/java/it/polimi/ingsw/gc14/Controller/GameController.java
+++ b/src/main/java/it/polimi/ingsw/gc14/Controller/GameController.java
@@ -210,7 +210,7 @@ public class GameController {
* Ends the game due to forfeit: all remaining players are absent,
* so the game is terminated and final scores are computed.
*/
- public synchronized void endGameForFeit() {
- model.endGameForFeit();
+ public synchronized void endGameForfeit() {
+ model.endGameForfeit();
}
}
\ No newline at end of file
diff --git a/src/main/java/it/polimi/ingsw/gc14/GameEventProcessor.java b/src/main/java/it/polimi/ingsw/gc14/GameEventProcessor.java
index ea61e7a..00065d7 100644
--- a/src/main/java/it/polimi/ingsw/gc14/GameEventProcessor.java
+++ b/src/main/java/it/polimi/ingsw/gc14/GameEventProcessor.java
@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
*
*
*
This class is not thread-safe by itself: it relies on the caller
- * (the game loop in {@code ServerLauncherTest}) to drive it from a single
+ * (the game loop in {@code ServerLauncher}) to drive it from a single
* thread via {@link #doFirstEvent()}.
*/
public class GameEventProcessor {
@@ -40,9 +40,13 @@ public class GameEventProcessor {
private final CompositeClientBroadcaster broadcaster;
private final SaveManager saveManager;
- /** Single-thread executor used exclusively for the forfeit timer. */
+ /** Single-thread executor used exclusively for the forfeit timer. Daemon so it does not block JVM shutdown. */
private final ScheduledExecutorService timerExecutor =
- Executors.newSingleThreadScheduledExecutor();
+ Executors.newSingleThreadScheduledExecutor(r -> {
+ Thread t = new Thread(r, "forfeit-timer");
+ t.setDaemon(true);
+ return t;
+ });
/**
* Handle to the running forfeit timer, or {@code null} when no timer is active.
@@ -267,7 +271,7 @@ public class GameEventProcessor {
}
disconnectionTimer = timerExecutor.schedule(
- () -> endGameForFeit(game),
+ () -> endGameForfeit(game),
1, TimeUnit.MINUTES
);
}
@@ -278,9 +282,9 @@ public class GameEventProcessor {
*
* @param game the game model captured when the timer was scheduled.
*/
- private void endGameForFeit(Game game) {
+ private void endGameForfeit(Game game) {
synchronized (gameController) {
- gameController.endGameForFeit();
+ gameController.endGameForfeit();
EndedGame forfeitEnd = new EndedGame(
game.getSlotMap(), game.getOrderLogicCard(),
game.getCurrentState(), game.getPlayerStanding()
@@ -363,7 +367,7 @@ public class GameEventProcessor {
List toRemove = playerList.entrySet().stream()
.filter(e -> !e.getValue())
.map(Map.Entry::getKey)
- .collect(Collectors.toList());
+ .toList();
toRemove.forEach(playerList::remove);
}
}
diff --git a/src/main/java/it/polimi/ingsw/gc14/LimitedMap.java b/src/main/java/it/polimi/ingsw/gc14/LimitedMap.java
index afaf634..3673b0c 100644
--- a/src/main/java/it/polimi/ingsw/gc14/LimitedMap.java
+++ b/src/main/java/it/polimi/ingsw/gc14/LimitedMap.java
@@ -1,15 +1,14 @@
package it.polimi.ingsw.gc14;
import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
- * A {@link LinkedHashMap} with a configurable size limit and an associated action.
+ * A {@link ConcurrentHashMap}-backed map with a configurable size limit and an associated action.
* When the number of elements reaches or exceeds the limit, the specified action is automatically triggered.
- * This implementation is thread-safe.
+ * This implementation is thread-safe for {@code put}; callers that need compound operations must synchronize externally.
*
* @param the type of keys maintained by this map.
* @param the type of mapped values.
@@ -50,8 +49,8 @@ public class LimitedMap implements Map {
@Override
public synchronized V put(K key, V value) {
boolean added = true;
- if(map.size()==limit) {
- if(!map.containsKey(key))
+ if (map.size() == limit) {
+ if (!map.containsKey(key))
return null;
added = false;
}
@@ -63,7 +62,7 @@ public class LimitedMap implements Map {
}
@Override
- public V remove(Object key) { return map.remove(key); }
+ public synchronized V remove(Object key) { return map.remove(key); }
@Override
public V get(Object key) { return map.get(key); }
@@ -112,7 +111,10 @@ public class LimitedMap implements Map {
/**
* Sets a new action to execute when the map size reaches or exceeds the limit.
*
- * @param action the new action to set.
+ * @param action the new action to set; must not be {@code null}.
*/
- public void setAction(Runnable action) { this.action = action; }
+ public void setAction(Runnable action) {
+ if (action == null) throw new IllegalArgumentException("action must not be null");
+ this.action = action;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/it/polimi/ingsw/gc14/Model/Game.java b/src/main/java/it/polimi/ingsw/gc14/Model/Game.java
index 23a85c8..345a0f1 100644
--- a/src/main/java/it/polimi/ingsw/gc14/Model/Game.java
+++ b/src/main/java/it/polimi/ingsw/gc14/Model/Game.java
@@ -1006,7 +1006,7 @@ public class Game implements Serializable {
* in the first position of the final ranking. The remaining players are
* ordered by prestige value and, in case of a tie, by food value.
*/
- public synchronized void endGameForFeit() {
+ public synchronized void endGameForfeit() {
Player winner = playersList.stream()
.filter(x -> !disconnectedPlayers.containsKey(x) || !disconnectedPlayers.get(x))
.findFirst().orElse(null);
diff --git a/src/main/java/it/polimi/ingsw/gc14/SaveManager.java b/src/main/java/it/polimi/ingsw/gc14/SaveManager.java
index 346e926..f699164 100644
--- a/src/main/java/it/polimi/ingsw/gc14/SaveManager.java
+++ b/src/main/java/it/polimi/ingsw/gc14/SaveManager.java
@@ -2,7 +2,14 @@ package it.polimi.ingsw.gc14;
import it.polimi.ingsw.gc14.Model.Game;
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -55,8 +62,8 @@ public class SaveManager {
public boolean save(Game game) {
try {
Files.createDirectories(filePath.getParent());
- try (ObjectOutputStream oos =
- new ObjectOutputStream(new FileOutputStream(filePath.toFile()))) {
+ try (ObjectOutputStream oos = new ObjectOutputStream(
+ new BufferedOutputStream(new FileOutputStream(filePath.toFile())))) {
oos.writeObject(game);
System.out.println("Game saved to: " + filePath.toAbsolutePath());
return true;
@@ -76,8 +83,8 @@ public class SaveManager {
* the classpath (indicates a deployment mismatch).
*/
public Game load() {
- try (ObjectInputStream ois =
- new ObjectInputStream(new FileInputStream(filePath.toFile()))) {
+ try (ObjectInputStream ois = new ObjectInputStream(
+ new BufferedInputStream(new FileInputStream(filePath.toFile())))) {
return (Game) ois.readObject();
} catch (FileNotFoundException e) {
return null;
@@ -97,8 +104,7 @@ public class SaveManager {
*/
public boolean delete() {
try {
- Files.delete(filePath);
- return true;
+ return Files.deleteIfExists(filePath);
} catch (IOException e) {
return false;
}