Fix: full FA refactor

This commit is contained in:
2026-06-14 13:49:54 +02:00
parent 3888529b96
commit 6c070e1ce5
5 changed files with 37 additions and 25 deletions
@@ -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();
}
}
@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
* </ul>
*
* <p>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<String> toRemove = playerList.entrySet().stream()
.filter(e -> !e.getValue())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
.toList();
toRemove.forEach(playerList::remove);
}
}
@@ -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 <K> the type of keys maintained by this map.
* @param <V> the type of mapped values.
@@ -63,7 +62,7 @@ public class LimitedMap<K, V> implements Map<K, V> {
}
@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<K, V> implements Map<K, V> {
/**
* 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;
}
}
@@ -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);
@@ -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;
}