Skip to content

Commit

Permalink
matchmaking first version #2276
Browse files Browse the repository at this point in the history
  • Loading branch information
BastLast committed Jan 14, 2025
1 parent 763d43a commit 7902ecb
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 88 deletions.
188 changes: 113 additions & 75 deletions Core/src/commands/player/FightCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,101 +6,139 @@ import {ReactionCollectorFight} from "../../../../Lib/src/packets/interaction/Re
import {EndCallback, ReactionCollectorInstance} from "../../core/utils/ReactionsCollector";
import {ReactionCollectorAcceptReaction} from "../../../../Lib/src/packets/interaction/ReactionCollectorPacket";
import {
CommandFightPacketReq,
CommandFightRefusePacketRes
CommandFightPacketReq,
CommandFightRefusePacketRes
} from "../../../../Lib/src/packets/commands/CommandFightPacket";
import {BlockingConstants} from "../../../../Lib/src/constants/BlockingConstants";
import {InventorySlots} from "../../core/database/game/models/InventorySlot";
import {LogsReadRequests} from "../../core/database/logs/LogsReadRequests";

type PlayerStats = {
classId: number,
fightRanking: {
glory: number,
}
energy: {
value: number,
max: number
},
attack: number,
defense: number,
speed: number
breath: {
base: number,
max: number,
regen: number
}
classId: number,
fightRanking: {
glory: number,
}
energy: {
value: number,
max: number
},
attack: number,
defense: number,
speed: number
breath: {
base: number,
max: number,
regen: number
}
}

async function getPlayerStats(player: Player): Promise<PlayerStats> {
const playerActiveObjects = await InventorySlots.getMainSlotsItems(player.id);
return {
classId: player.class,
fightRanking: {
glory: player.getGloryPoints()
},
energy: {
value: player.getCumulativeFightPoint(),
max: player.getMaxCumulativeFightPoint()
},
attack: player.getCumulativeAttack(playerActiveObjects),
defense: player.getCumulativeDefense(playerActiveObjects),
speed: player.getCumulativeSpeed(playerActiveObjects),
breath: {
base: player.getBaseBreath(),
max: player.getMaxBreath(),
regen: player.getBreathRegen()
}
};
const playerActiveObjects = await InventorySlots.getMainSlotsItems(player.id);
return {
classId: player.class,
fightRanking: {
glory: player.getGloryPoints()
},
energy: {
value: player.getCumulativeFightPoint(),
max: player.getMaxCumulativeFightPoint()
},
attack: player.getCumulativeAttack(playerActiveObjects),
defense: player.getCumulativeDefense(playerActiveObjects),
speed: player.getCumulativeSpeed(playerActiveObjects),
breath: {
base: player.getBaseBreath(),
max: player.getMaxBreath(),
regen: player.getBreathRegen()
}
};
}

/**
* Find another player to fight the player that started the command
* @param player
* @param player - player that wants to fight
* @param offset - offset to start the search in case the first try did not work
* @returns player opponent
*/
function findOpponent(player: Player): Player {
const closestPlayers = Players.findByDefenseGlory(player.attackGloryPoints,5,0)
//shuffle l'array
//loop dessus pour check les autres conditions
return
async function findOpponent(player: Player, offset: number): Promise<Player | null> {
const closestPlayers = await Players.findByDefenseGlory(player.attackGloryPoints, FightConstants.PLAYER_PER_OPPONENT_SEARCH, offset)

Check failure on line 64 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Missing semicolon
//shuffle array

Check warning on line 65 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Expected space or tab after '//' in comment

Check failure on line 65 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
closestPlayers.sort(() => Math.random() - 0.5);
let selectedPlayer: Player = null;
for (const closestPlayer of closestPlayers) {
if (
closestPlayer.id === player.id || // cannot fight itself

Check failure on line 70 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
closestPlayer.level < FightConstants.REQUIRED_LEVEL || // level too low

Check failure on line 71 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
Math.abs(player.defenseGloryPoints - closestPlayer.attackGloryPoints) > FightConstants.ELO.MAX_ELO_GAP // ELO gap too large
) {
continue;
}
if (
await LogsReadRequests.hasBeenADefenderInRankedFightSinceMinute(
closestPlayer.keycloakId,
FightConstants.DEFENDER_COOLDOWN_MINUTES
)
) {
continue; // defender on cooldown

Check failure on line 82 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
}
const bo3 = await LogsReadRequests.getRankedFightsThisWeek(player.keycloakId, closestPlayer.keycloakId);
if (
bo3.won > 1 ||
bo3.lost > 1 ||
bo3.draw + bo3.won + bo3.lost >= 3
) {
continue; // max fights already played

Check failure on line 90 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
}
selectedPlayer = closestPlayer;
}
if (selectedPlayer || offset > FightConstants.MAX_OFFSET_FOR_OPPONENT_SEARCH) {
return selectedPlayer;
}
else {

Check failure on line 97 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Unnecessary 'else' after 'return'
return findOpponent(player, offset + 1);
}
}

export default class FightCommand {
@commandRequires(CommandFightPacketReq, {
notBlocked: true,
disallowedEffects: CommandUtils.DISALLOWED_EFFECTS.NOT_STARTED_OR_DEAD,
level: FightConstants.REQUIRED_LEVEL
})
async execute(response: DraftBotPacket[], player: Player, packet: CommandFightPacketReq, context: PacketContext): Promise<void> {
const toCheckPlayer = await Players.getAskedPlayer({keycloakId: packet.playerKeycloakId}, player);
@commandRequires(CommandFightPacketReq, {
notBlocked: true,
disallowedEffects: CommandUtils.DISALLOWED_EFFECTS.NOT_STARTED_OR_DEAD,
level: FightConstants.REQUIRED_LEVEL
})
async execute(response: DraftBotPacket[], player: Player, packet: CommandFightPacketReq, context: PacketContext): Promise<void> {
const toCheckPlayer = await Players.getAskedPlayer({keycloakId: packet.playerKeycloakId}, player);

const collector = new ReactionCollectorFight(
await getPlayerStats(toCheckPlayer)
);
const collector = new ReactionCollectorFight(
await getPlayerStats(toCheckPlayer)
);

const endCallback: EndCallback = async (collector: ReactionCollectorInstance, response: DraftBotPacket[]): Promise<void> => {
const reaction = collector.getFirstReaction();
if (reaction && reaction.reaction.type === ReactionCollectorAcceptReaction.name) {
const endCallback: EndCallback = async (collector: ReactionCollectorInstance, response: DraftBotPacket[]): Promise<void> => {
const reaction = collector.getFirstReaction();
if (reaction && reaction.reaction.type === ReactionCollectorAcceptReaction.name) {
const opponent = await findOpponent(player, 0);
if (!opponent) {
// error message if no opponent found

Check failure on line 120 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
}
// start fight

Check failure on line 122 in Core/src/commands/player/FightCommand.ts

View workflow job for this annotation

GitHub Actions / eslint-core-module

Comments should not begin with a lowercase character
}
else {
response.push(makePacket(CommandFightRefusePacketRes, {}));
}
};

// Acceptation du fight/matchmaking
} else {
response.push(makePacket(CommandFightRefusePacketRes, {}));
}
};
const collectorPacket = new ReactionCollectorInstance(
collector,
context,
{
allowedPlayerKeycloakIds: [player.keycloakId],
reactionLimit: 1
},
endCallback
)
.block(player.id, BlockingConstants.REASONS.FIGHT_CONFIRMATION)
.build();

const collectorPacket = new ReactionCollectorInstance(
collector,
context,
{
allowedPlayerKeycloakIds: [player.keycloakId],
reactionLimit: 1
},
endCallback
)
.block(player.id, BlockingConstants.REASONS.FIGHT_CONFIRMATION)
.build();

response.push(collectorPacket);
}
response.push(collectorPacket);
}
}

46 changes: 34 additions & 12 deletions Core/src/core/database/logs/LogsReadRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {LogsPlayersPossibilities} from "./models/LogsPlayersPossibilities";
import {LogsPossibilities} from "./models/LogsPossibilities";
import {LogsPlayers} from "./models/LogsPlayers";
import {LogsPlayersTravels} from "./models/LogsPlayersTravels";
import {getNextSaturdayMidnight, getNextSundayMidnight, minutesToMilliseconds} from "../../../../../Lib/src/utils/TimeUtils";
import {
getNextSaturdayMidnight,
getNextSundayMidnight,
minutesToMilliseconds
} from "../../../../../Lib/src/utils/TimeUtils";
import {LogsMapLinks} from "./models/LogsMapLinks";
import {MapConstants} from "../../../../../Lib/src/constants/MapConstants";
import {LogsFightsResults} from "./models/LogsFightsResults";
Expand Down Expand Up @@ -223,24 +227,42 @@ export class LogsReadRequests {
return await this.travelsOnPveIslandsCountThisWeekRequest(keycloakId);
}

/**
* Check if the player has been a defender in a ranked fight since the last minute
* @param playerKeycloakId - The keycloak id of the player to check
* @param minutes - The number of minutes to check
*/
static async hasBeenADefenderInRankedFightSinceMinute(playerKeycloakId: string, minutes: number): Promise<boolean> {
const hasBeenDefender = await LogsFightsResults.findOne({
where: {
"$LogsPlayer2.keycloakId": playerKeycloakId,
date: {
[Op.gt]: Math.floor((Date.now() - minutesToMilliseconds(minutes)) / 1000)
},
friendly: false
},
include: [{
model: LogsPlayers,
association: new HasOne(LogsFightsResults, LogsPlayers, {
sourceKey: "player2Id",
foreignKey: "id",
as: "LogsPlayer2"
})
}]
});
return !!hasBeenDefender;
}

/*
* Get the fights of a player against another this week
* @param playerKeycloakId
* @param opponentKeycloakId
*/
static async getRankedFightsThisWeek(playerKeycloakId: string, opponentKeycloakId: string): Promise<RankedFightResult> {
static async getRankedFightsThisWeek(attackerKeycloakId: string, defenderKeycloakId: string): Promise<RankedFightResult> {
const fights = await LogsFightsResults.findAll({
where: {
[Op.or]: [
{
"$LogsPlayer1.keycloakId$": playerKeycloakId,
"$LogsPlayer2.keycloakId$": opponentKeycloakId
},
{
"$LogsPlayer1.keycloakId$": opponentKeycloakId,
"$LogsPlayer2.keycloakId$": playerKeycloakId
}
],
"$LogsPlayer1.keycloakId$": attackerKeycloakId,
"$LogsPlayer2.keycloakId$": defenderKeycloakId,
date: {
[Op.gt]: Math.floor((getNextSaturdayMidnight() - 7 * 24 * 60 * 60 * 1000) / 1000)
},
Expand Down
10 changes: 9 additions & 1 deletion Lib/src/constants/FightConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export abstract class FightConstants {
OCCUPIED: "error.occupied",
NO_FIGHT_POINTS: "error.noFightPoints",
ELO_GAP: "error.eloGap",
BEST_OF_3: "error.bestOf3",
PVE_ISLAND: "error.onPveIsland"
};

Expand Down Expand Up @@ -128,4 +127,13 @@ export abstract class FightConstants {
static readonly HANDSHAKE_EMOTE = "\uD83E\uDD1D";

static readonly DEFAULT_ACTION_WEIGHT = 1;

// time needed to wait before being able to fight again after a ranked fight as a defender

Check failure on line 131 in Lib/src/constants/FightConstants.ts

View workflow job for this annotation

GitHub Actions / eslint-lib-module

Comments should not begin with a lowercase character
static DEFENDER_COOLDOWN_MINUTES = 30;

// Maximum offset for opponent search
static MAX_OFFSET_FOR_OPPONENT_SEARCH = 5;

// Number of players to search for when looking for an opponent
static PLAYER_PER_OPPONENT_SEARCH = 5;
}

0 comments on commit 7902ecb

Please sign in to comment.