Skip to content

Commit

Permalink
Timer and backend fixtures (#74)
Browse files Browse the repository at this point in the history
* Patch ghost timer and better leave session

* Maybe patch #60

* Change starting timer system to new one

* Patching concurrent modification on maps

* Patch build problems

* Patch some weird code

---------

Co-authored-by: Zelytra <[email protected]>
  • Loading branch information
zelytra and zelytra authored Mar 11, 2024
1 parent 084af14 commit 07f4faa
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 85 deletions.
47 changes: 39 additions & 8 deletions backend/src/main/java/fr/zelytra/session/SessionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import io.quarkus.logging.Log;
import jakarta.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
* Manages sessions for a multiplayer game, allowing players to create, join, and leave sessions.
Expand All @@ -18,17 +18,17 @@ public class SessionManager {

private static SessionManager instance;

private final HashMap<String, Fleet> sessions;
private final ConcurrentHashMap<String, Fleet> sessions;

// SotServer cached to avoid API spam and faster server response
private final HashMap<String, SotServer> sotServers;
private final ConcurrentHashMap<String, SotServer> sotServers;

/**
* Private constructor for singleton pattern.
*/
private SessionManager() {
this.sessions = new HashMap<>();
this.sotServers = new HashMap<>();
this.sessions = new ConcurrentHashMap<>();
this.sotServers = new ConcurrentHashMap<>();
}

/**
Expand Down Expand Up @@ -96,10 +96,14 @@ public Fleet joinSession(String sessionId, Player player) {
*/
public void leaveSession(Player player) {
for (Fleet fleet : sessions.values()) {

SotServer sotServer = getSotServerFromPlayer(player);
if (sotServer != null) {
playerLeaveSotServer(player, sotServer);
}

fleet.getPlayers().remove(player);
fleet.getServers().forEach((key, value) -> {
value.getConnectedPlayers().remove(player);
});
fleet.getServers().forEach((key, value) -> value.getConnectedPlayers().remove(player));
SessionSocket.broadcastDataToSession(fleet.getSessionId(), MessageType.UPDATE, fleet);
Log.info("[" + fleet.getSessionId() + "] " + player.getUsername() + " Leave the session !");

Expand All @@ -111,6 +115,33 @@ public void leaveSession(Player player) {
}
}

/**
* Retrieves the {@link SotServer} instance that a specified player is currently connected to.
* <p>
* This method iterates through all sessions and their corresponding fleets to find the SotServer
* to which the specified player is connected. If the player is found within a SotServer's
* connected players list, that SotServer is returned.
* <p>
* It is assumed that a player can only be connected to one SotServer at any given time.
* If the player is not connected to any SotServer, or if the player does not exist,
* this method returns {@code null}.
*
* @param player The {@link Player} whose SotServer connection is to be retrieved.
* @return The {@link SotServer} instance the player is connected to, or {@code null} if the player
* is not connected to any SotServer or does not exist.
*/
public SotServer getSotServerFromPlayer(Player player) {
for (Fleet fleet : sessions.values()) {
for (SotServer server : fleet.getServers().values()) {
if (server.getConnectedPlayers().contains(player)) {
return server;
}
}
}
return null;
}


/**
* Retrieves a Fleet instance for a given session ID, if it exists.
*
Expand Down
29 changes: 7 additions & 22 deletions backend/src/main/java/fr/zelytra/session/SessionSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import fr.zelytra.session.countdown.SessionCountDown;
import fr.zelytra.session.fleet.Fleet;
import fr.zelytra.session.player.Player;
import fr.zelytra.session.server.SotServer;
Expand All @@ -29,6 +28,7 @@ public class SessionSocket {

private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final ConcurrentHashMap<String, Future<?>> sessionTimeoutTasks = new ConcurrentHashMap<>();
private static final int RISE_ANCHOR_TIMER = 3; // in seconds

@OnOpen
public void onOpen(Session session) {
Expand Down Expand Up @@ -71,10 +71,7 @@ public void onMessage(String message, Session session, @PathParam("sessionId") S
Player player = objectMapper.convertValue(socketMessage.data(), Player.class);
handleLeaveMessage(player);
}
case START_COUNTDOWN -> {
SessionCountDown countDown = objectMapper.convertValue(socketMessage.data(), SessionCountDown.class);
handleStartCountdown(session, countDown);
}
case START_COUNTDOWN -> handleStartCountdown(session);
case CLEAR_STATUS -> handleClearStatus(session);
case JOIN_SERVER -> {
SotServer sotServer = objectMapper.convertValue(socketMessage.data(), SotServer.class);
Expand All @@ -100,17 +97,15 @@ private void handleClearStatus(Session session) {
broadcastDataToSession(fleet.getSessionId(), MessageType.UPDATE, fleet);
}

private void handleStartCountdown(Session session, SessionCountDown countDown) {

countDown.calculateClickTime();
private void handleStartCountdown(Session session) {

SessionManager manager = SessionManager.getInstance();
Player player = manager.getPlayerFromSessionId(session.getId());
Fleet fleet = manager.getFleetByPlayerName(player.getUsername());
fleet.getStats().addTry();

Log.info("[" + fleet.getSessionId() + "] Starting countdown at " + countDown.getClickTime().toString());
broadcastDataToSession(fleet.getSessionId(), MessageType.RUN_COUNTDOWN, countDown);
Log.info("[" + fleet.getSessionId() + "] Starting countdown in " + SessionSocket.RISE_ANCHOR_TIMER + "s");
broadcastDataToSession(fleet.getSessionId(), MessageType.RUN_COUNTDOWN, SessionSocket.RISE_ANCHOR_TIMER);
broadcastDataToSession(fleet.getSessionId(), MessageType.UPDATE, fleet);
}

Expand Down Expand Up @@ -172,35 +167,25 @@ private void handleLeaveMessage(Player player) {

@OnClose
public void onClose(Session session) {

// Clean up resources related to the session
sessionTimeoutTasks.remove(session.getId());

SessionManager manager = SessionManager.getInstance();
Player player = manager.getPlayerFromSessionId(session.getId());

if (player != null) {
manager.leaveSession(player);
Log.info("[" + player.getUsername() + "] Disconnected");
return;
} else {
Log.warn("[UNDEFINED PLAYER] Disconnected");
}
Log.warn("[UNDEFINED PLAYER] Disconnected");
}

@OnError
public void onError(Session session, Throwable throwable) throws IOException {
Log.error("WebSocket error for session " + session.getId() + ": " + throwable);

SessionManager manager = SessionManager.getInstance();
Player player = manager.getPlayerFromSessionId(session.getId());
if (player != null) {
manager.leaveSession(player);
}

session.close();
}


/**
* Broadcasts a message to all players within a session.
* <p>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fr.zelytra.session.fleet;

import io.quarkus.logging.Log;

public class FleetStats {


Expand Down Expand Up @@ -31,7 +29,6 @@ public void setSuccessPrediction(int successPrediction) {

public void addTry() {
this.tryAmount++;
Log.info(this.tryAmount);
}
}

4 changes: 0 additions & 4 deletions frontend/src/components/fleet/FleetLobby.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ import PlayerFleet from "@/vue/fleet/PlayerFleet.vue";
import {useI18n} from "vue-i18n";
import BannerTemplate from "@/vue/templates/BannerTemplate.vue";
import {UserStore} from "@/objects/stores/UserStore.ts";
import {LocalTime} from "@js-joda/core";
import SessionCountdown from "@/components/fleet/SessionCountdown.vue";
import ServerContainer from "@/vue/templates/ServerContainer.vue";
Expand Down Expand Up @@ -126,9 +125,6 @@ function startSession() {
if (!UserStore.player.isMaster) {
return;
}
UserStore.player.countDown = {
startingTimer: LocalTime.now().toJSON()
}
props.session!.runCountDown()
}
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/fleet/SessionCountdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ const alerts = inject<AlertProvider>("alertProvider");
const updateTimer = setInterval(() => {
if (!UserStore.player.countDown || !UserStore.player.countDown.clickTime) return;
const start: LocalTime = LocalTime.now();
const click: LocalTime = LocalTime.parse(UserStore.player.countDown.clickTime);
const start:LocalTime = LocalTime.now()
const click:LocalTime = UserStore.player.countDown.clickTime as LocalTime
if (click.isBefore(start)) {
UserStore.player.countDown = undefined;
Expand Down Expand Up @@ -72,6 +73,7 @@ const props = defineProps({session: {type: Object as PropType<Fleet>, required:
onUnmounted(() => {
clearInterval(updateTimer);
UserStore.player.countDown = undefined;
})
</script>

Expand Down
14 changes: 7 additions & 7 deletions frontend/src/objects/Fleet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {WebSocketMessage, WebSocketMessageType} from "@/objects/WebSocet.ts";
import {AlertType} from "@/vue/alert/Alert.ts";
import {alertProvider} from "@/main.ts";
import i18n from "@/objects/i18n";
import {SessionRunner} from "@/objects/SessionRunner.ts";
import {Player} from "@/objects/Player.ts";
import {SotServer} from "@/objects/SotServer.ts";
import {LocalTime} from "@js-joda/core";

const {t} = i18n.global;

Expand Down Expand Up @@ -78,7 +78,7 @@ export class Fleet {
break;
}
case WebSocketMessageType.RUN_COUNTDOWN: {
this.handleSessionRunner(message.data as SessionRunner);
this.handleSessionRunner(message.data as number);
break;
}
default: {
Expand All @@ -105,6 +105,7 @@ export class Fleet {
type: AlertType.ERROR,
});
UserStore.player.fleet!.sessionId = "";
UserStore.player.countDown = undefined; // Reset timer to avoid app freeze
}
this.safeClose = false;
}
Expand All @@ -122,9 +123,8 @@ export class Fleet {
UserStore.player.isReady = player.isReady;
}

private handleSessionRunner(countdown: SessionRunner) {
UserStore.player.countDown = countdown

private handleSessionRunner(countdown: number) {
UserStore.player.countDown = {clickTime: LocalTime.now().plusSeconds(countdown)}
}

leaveSession(): void {
Expand All @@ -148,14 +148,14 @@ export class Fleet {
runCountDown() {
if (!this.socket) return;
const message: WebSocketMessage = {
data: UserStore.player.countDown,
data: null,
messageType: WebSocketMessageType.START_COUNTDOWN,
};
this.socket.send(JSON.stringify(message));
}

clearPlayersStatus() {
if (!this.socket) return;
if (!this.socket || !UserStore.player.isMaster) return;
const message: WebSocketMessage = {
data: undefined,
messageType: WebSocketMessageType.CLEAR_STATUS,
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/objects/SessionRunner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {LocalTime} from "@js-joda/core";

export interface SessionRunner {
startingTimer: string
clickTime?: string
clickTime: LocalTime
}

0 comments on commit 07f4faa

Please sign in to comment.