Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
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: 1 addition & 5 deletions src/connector/websocketOutgoing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,9 @@ export class WebsocketOutgoing {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
eventNumber,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
timeoutEndTimeout,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
timeoutRemainingLoop,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
playercamUrl,
...formattedData
} = data;
} = data;

const deepMod: any = structuredClone(formattedData);
if (
Expand Down
93 changes: 75 additions & 18 deletions src/model/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ export class Match {
};
private timeoutEndTimeout: any = undefined;
private timeoutRemainingLoop: any = undefined;
private timeoutGracePeriodPassed: boolean = false;

private hasEnteredOvertime: boolean = false;

private tools: ToolsData;

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

this.checkAndGrantOvertimeTimeout();

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

case "game_end":
this.isRunning = false;

// Clean up timeout-related timers
clearTimeout(this.timeoutEndTimeout);
clearInterval(this.timeoutRemainingLoop);
this.timeoutEndTimeout = undefined;
this.timeoutRemainingLoop = undefined;

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

Expand Down Expand Up @@ -297,27 +309,11 @@ export class Match {
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 All @@ -334,8 +330,18 @@ export class Match {

clearInterval(this.timeoutRemainingLoop);
this.timeoutRemainingLoop = null;

this.timeoutGracePeriodPassed = false;

this.timeoutEndTimeout = setTimeout(() => {
if (this.timeoutGracePeriodPassed) {
if (this.timeoutState.leftTeam) {
this.tools.timeoutCounter.left = Math.max(0, this.tools.timeoutCounter.left - 1);
} else if (this.timeoutState.rightTeam) {
this.tools.timeoutCounter.right = Math.max(0, this.tools.timeoutCounter.right - 1);
}
}

this.timeoutState.leftTeam = false;
this.timeoutState.rightTeam = false;
clearInterval(this.timeoutRemainingLoop);
Expand All @@ -345,6 +351,13 @@ export class Match {
this.timeoutRemainingLoop = setInterval(() => {
if (this.timeoutState.timeRemaining > 0) {
this.timeoutState.timeRemaining--;

// track if grace period has passed
const gracePeriodRemaining = this.tools.timeoutDuration - this.tools.timeoutCancellationGracePeriod;
if (!this.timeoutGracePeriodPassed && this.timeoutState.timeRemaining <= gracePeriodRemaining) {
this.timeoutGracePeriodPassed = true;
}

this.eventNumber++;
} else {
clearInterval(this.timeoutRemainingLoop);
Expand Down Expand Up @@ -439,6 +452,50 @@ 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;

if (currentState) {
const gracePeriodRemaining = this.tools.timeoutDuration - this.tools.timeoutCancellationGracePeriod;
const stillInGracePeriod = this.timeoutState.timeRemaining > gracePeriodRemaining;

if (stillInGracePeriod && !this.timeoutGracePeriodPassed) {
}

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();
}
}

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