Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/connector/databaseConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class DatabaseConnector {

public static async registerMatch(match: any) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, ...toSend } = match;
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, leftTimeoutCancellationTimer, rightTimeoutCancellationTimer, ...toSend } = match;
const res = await this.apiRequest(`system/match/${match.matchId}/register`, "post", {
match: toSend,
});
Expand All @@ -77,7 +77,7 @@ export class DatabaseConnector {

public static async updateMatch(match: any): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, ...toSend } = match;
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, leftTimeoutCancellationTimer, rightTimeoutCancellationTimer, ...toSend } = match;
const res = await this.apiRequest(`system/match/${match.matchId}/update`, "put", {
match: toSend,
});
Expand All @@ -92,7 +92,7 @@ export class DatabaseConnector {

public static async completeMatch(match: any): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, ...toSend } = match;
const { replayLog, eventNumber, timeoutEndTimeout, timeoutRemainingLoop, leftTimeoutCancellationTimer, rightTimeoutCancellationTimer, ...toSend } = match;
const res = await this.apiRequest(`system/match/${match.matchId}/complete`, "put", {
match: toSend,
});
Expand Down
4 changes: 4 additions & 0 deletions src/connector/websocketOutgoing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export class WebsocketOutgoing {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
timeoutRemainingLoop,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
leftTimeoutCancellationTimer,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
rightTimeoutCancellationTimer,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
playercamUrl,
...formattedData
} = data;
Expand Down
114 changes: 96 additions & 18 deletions src/model/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class Match {
};
private timeoutEndTimeout: any = undefined;
private timeoutRemainingLoop: any = undefined;

private leftTimeoutCancellationTimer: any = undefined;
private rightTimeoutCancellationTimer: any = undefined;
private hasEnteredOvertime: boolean = false;

private tools: ToolsData;

Expand Down Expand Up @@ -201,6 +205,8 @@ export class Match {
this.processRoundReasons();
}

this.checkAndGrantOvertimeTimeout();

if (process.env.USE_BACKEND === "true") {
this.updateNameOverridesAndPlayercams().then(() => {});
}
Expand Down Expand Up @@ -238,6 +244,13 @@ export class Match {

case "game_end":
this.isRunning = false;

// Clean up all timeout-related timers
clearTimeout(this.leftTimeoutCancellationTimer);
clearTimeout(this.rightTimeoutCancellationTimer);
this.leftTimeoutCancellationTimer = undefined;
this.rightTimeoutCancellationTimer = undefined;

this.eventNumber++;
MatchController.getInstance().removeMatch(this.groupCode);

Expand Down Expand Up @@ -293,31 +306,21 @@ export class Match {
clearTimeout(this.timeoutEndTimeout);
clearInterval(this.timeoutRemainingLoop);
this.timeoutEndTimeout = null;

// Clean up cancellation timers
clearTimeout(this.leftTimeoutCancellationTimer);
clearTimeout(this.rightTimeoutCancellationTimer);
this.leftTimeoutCancellationTimer = undefined;
this.rightTimeoutCancellationTimer = undefined;
}
break;

case DataTypes.LEFT_TIMEOUT:
this.timeoutState.leftTeam = !this.timeoutState.leftTeam;
if (this.timeoutState.leftTeam) {
this.timeoutState.rightTeam = false;
this.timeoutState.techPause = false;
this.timeoutState.timeRemaining = this.tools.timeoutDuration;
this.startTimeoutEndTimeout();
} else {
clearTimeout(this.timeoutEndTimeout);
}
this.handleTeamTimeout("left");
break;

case DataTypes.RIGHT_TIMEOUT:
this.timeoutState.rightTeam = !this.timeoutState.rightTeam;
if (this.timeoutState.rightTeam) {
this.timeoutState.leftTeam = false;
this.timeoutState.techPause = false;
this.timeoutState.timeRemaining = this.tools.timeoutDuration;
this.startTimeoutEndTimeout();
} else {
clearTimeout(this.timeoutEndTimeout);
}
this.handleTeamTimeout("right");
break;

case DataTypes.SWITCH_KDA_CREDITS:
Expand Down Expand Up @@ -439,6 +442,81 @@ export class Match {
this.spikeDetonationTime = timestamp + 45 * 1000; // Add 45 seconds to the current time
}

private handleTeamTimeout(team: "left" | "right") {
const isLeftTeam = team === "left";
const currentState = isLeftTeam ? this.timeoutState.leftTeam : this.timeoutState.rightTeam;
const cancellationTimer = isLeftTeam ? this.leftTimeoutCancellationTimer : this.rightTimeoutCancellationTimer;

if (cancellationTimer) {
clearTimeout(cancellationTimer);
if (isLeftTeam) {
this.leftTimeoutCancellationTimer = undefined;
} else {
this.rightTimeoutCancellationTimer = undefined;
}

// Stop the current timeout
this.timeoutState.leftTeam = false;
this.timeoutState.rightTeam = false;
clearTimeout(this.timeoutEndTimeout);
clearInterval(this.timeoutRemainingLoop);
this.timeoutEndTimeout = undefined;
this.timeoutRemainingLoop = undefined;

return;
}

if (currentState) {
this.timeoutState.leftTeam = false;
this.timeoutState.rightTeam = false;
clearTimeout(this.timeoutEndTimeout);
clearInterval(this.timeoutRemainingLoop);
this.timeoutEndTimeout = undefined;
this.timeoutRemainingLoop = undefined;
} else {
const timeoutsRemaining = isLeftTeam ? this.tools.timeoutCounter.left : this.tools.timeoutCounter.right;

if (timeoutsRemaining <= 0) {
return;
}

this.timeoutState.leftTeam = isLeftTeam;
this.timeoutState.rightTeam = !isLeftTeam;
this.timeoutState.techPause = false;
this.timeoutState.timeRemaining = this.tools.timeoutDuration;
this.startTimeoutEndTimeout();

const gracePeriodTimer = setTimeout(() => {
if (isLeftTeam) {
this.tools.timeoutCounter.left = Math.max(0, this.tools.timeoutCounter.left - 1);
this.leftTimeoutCancellationTimer = undefined;
} else {
this.tools.timeoutCounter.right = Math.max(0, this.tools.timeoutCounter.right - 1);
this.rightTimeoutCancellationTimer = undefined;
}
this.eventNumber++;
}, this.tools.timeoutCancellationGracePeriod * 1000);

if (isLeftTeam) {
this.leftTimeoutCancellationTimer = gracePeriodTimer;
} else {
this.rightTimeoutCancellationTimer = gracePeriodTimer;
}
}
}

private checkAndGrantOvertimeTimeout() {
if (this.roundNumber >= this.firstOtRound && !this.hasEnteredOvertime) {
this.hasEnteredOvertime = true;

this.tools.timeoutCounter.left = Math.min(2, this.tools.timeoutCounter.left + 1);
this.tools.timeoutCounter.right = Math.min(2, this.tools.timeoutCounter.right + 1);

this.eventNumber++;
Log.info(`Overtime reached! Each team granted an additional timeout.`);
}
}

private async updateNameOverridesAndPlayercams() {
if (
!this.tools.playercamsInfo.identifier ||
Expand Down
10 changes: 10 additions & 0 deletions src/model/ToolsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export class ToolsData {
enabled: false,
};
public timeoutDuration: number = 60;
public timeoutCounter: ITimeoutInfo = {
left: 2,
right: 2,
};
public timeoutCancellationGracePeriod: number = 10;
public sponsorInfo: ISponsorInfo = {
enabled: false,
duration: 5,
Expand Down Expand Up @@ -68,6 +73,11 @@ export type ITournamentInfo = {
enabled: boolean;
};

export type ITimeoutInfo = {
left: number;
right: number;
};

export type ISponsorInfo = {
enabled: boolean;
duration: number;
Expand Down