Skip to content

Commit

Permalink
Increase penalties for intentional leaves (#2594)
Browse files Browse the repository at this point in the history
* no longer elo protect before stage 6 if leave is consented

* add 5 min timeout when leaving a game before the end
  • Loading branch information
sylvainpolletvillard authored Dec 18, 2024
1 parent 59a5790 commit 59b978a
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 35 deletions.
1 change: 1 addition & 0 deletions app/public/dist/client/changelog/patch-5.9.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
- Master ball: 1500 ELO
- Beast ball: 1700 ELO
- ELO, XP and titles after game are now distributed immediately after the player is eliminated and leaves the game, instead of waiting for the game to end
- Due to an increase of cases of intentional leaves at the start of a game, we have to increase the penalties for early disconnections. Intentional disconnections before stage 6 are no longer protected against Elo loss. Unintentional disconnections without reconnection within the next 3 minutes before stage 6 remain protected against Elo loss, but players won't be able to join a new game within the next 5 minutes.

# UI

Expand Down
3 changes: 2 additions & 1 deletion app/public/dist/client/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2143,7 +2143,7 @@
"GOOD_ROD": "After each PvP round, fish a Pokémon that is then added to a free slot on your bench. Can catch rare WATER Pokémons and Feebas.",
"SUPER_ROD": "After each PvP round, fish a Pokémon that is then added to a free slot on your bench. Can catch epic WATER Pokémons and Wishiwashi.",
"DYNAMAX_BAND": "At the start of the fight, raise max HP by 250%.",
"SHINY_STONE": "Gives +2 to LIGHT synergy and spawn a second light spot on the holder's cell",
"SHINY_STONE": "Gives LIGHT synergy and +1 to LIGHT synergy level. Spawn a second light spot on the holder's cell.",
"RARE_CANDY": "Immediately make a Pokémon evolve. Can only be given to non-fully evolved Pokémon, and reduce its sell price to its previous tier.",
"EVIOLITE": "Holder gets a bunch of stats, but can no longer evolve. Can only be given to non-fully evolved Pokémon.",
"WHITE_FLUTE": "Spawn 3 WILD Pokémons around the holder at the start of the fight. Rarity and tier increase with stage level.",
Expand Down Expand Up @@ -3354,6 +3354,7 @@
"USER_BANNED": "You account has been banned",
"USER_RANK_TOO_LOW": "Your player rank is not high enough to participate in this lobby",
"USER_NOT_AUTHENTICATED": "Please authenticate again",
"USER_TIMEOUT": "You recently left a game before its end, so you have to wait 5 minutes before joining another one",
"ROOM_FULL": "This room is full",
"ROOM_EMPTY": "This room has been removed due to lack of players",
"ROOM_DELETED": "This room has been deleted",
Expand Down
7 changes: 6 additions & 1 deletion app/public/src/game/lobby-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ export async function joinExistingPreparationRoom(
navigate("/preparation")
}
} catch (error) {
logger.error(error)
if (error.code && error.code in CloseCodesMessages) {
const errorMessage = CloseCodesMessages[error.code]
dispatch(setErrorAlertMessage(t(`errors.${errorMessage}`)))
} else {
logger.error(error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ export default function AvailableRoomMenu() {
)
const uid: string = useAppSelector((state) => state.network.uid)
const user = useAppSelector((state) => state.network.profile)
const [isJoining, setJoining] = useState<boolean>(false)
const [showRoomSelectionMenu, setShowRoomSelectionMenu] = useState<boolean>(false)

const requestRoom = throttle(async function (gameMode: GameMode) {
if (lobby && !isJoining) {
setJoining(true)
if (lobby) {
lobby.send(Transfer.REQUEST_ROOM, gameMode)
setShowRoomSelectionMenu(false)
}
}, 1000)

Expand All @@ -58,13 +57,11 @@ export default function AvailableRoomMenu() {
return
}

if (lobby && !isJoining) {
if (password) {
if (user && user.role === Role.BASIC) {
const password = prompt(`This room is private. Enter password`)
if (selectedRoom.metadata?.password != password)
return alert(`Wrong password !`)
}
if (lobby) {
if (password && user && user.role === Role.BASIC) {
const password = prompt(`This room is private. Enter password`)
if (selectedRoom.metadata?.password != password)
return alert(`Wrong password !`)
}

await joinExistingPreparationRoom(selectedRoom.roomId, client, lobby, dispatch, navigate)
Expand Down
11 changes: 10 additions & 1 deletion app/public/src/pages/preparation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,16 @@ export default function Preparation() {
})

r.onLeave((code) => {
const shouldGoToLobby = (code === CloseCodes.USER_KICKED || code === CloseCodes.ROOM_DELETED || code === CloseCodes.ROOM_FULL || code === CloseCodes.ROOM_EMPTY || code === CloseCodes.USER_BANNED || code === CloseCodes.USER_RANK_TOO_LOW)
const shouldGoToLobby = [
CloseCodes.USER_KICKED,
CloseCodes.ROOM_DELETED,
CloseCodes.ROOM_FULL,
CloseCodes.ROOM_EMPTY,
CloseCodes.USER_BANNED,
CloseCodes.USER_RANK_TOO_LOW,
CloseCodes.USER_TIMEOUT
].includes(code)

const shouldReconnect = code === CloseCodes.ABNORMAL_CLOSURE || code === CloseCodes.TIMEOUT
logger.info(`left preparation room with code ${code}`, { shouldGoToLobby, shouldReconnect })

Expand Down
12 changes: 12 additions & 0 deletions app/rooms/commands/preparation-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ export class OnJoinCommand extends Command<
> {
async execute({ client, options, auth }) {
try {
const timeoutDateStr = await this.room.presence.hget(
client.auth.uid,
"user_timeout"
)
if (timeoutDateStr) {
const timeout = new Date(timeoutDateStr).getTime()
if (timeout > Date.now()) {
client.leave(CloseCodes.USER_TIMEOUT)
return
}
}

const numberOfHumanPlayers = values(this.state.users).filter(
(u) => !u.isBot
).length
Expand Down
15 changes: 14 additions & 1 deletion app/rooms/game-room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,19 @@ export default class GameRoom extends Room<GameState> {
if (client && client.auth && client.auth.displayName) {
//logger.info(`${client.auth.displayName} left game`)
const player = this.state.players.get(client.auth.uid)
if (player && this.state.stageLevel <= 5) {
const hasLeftGameBeforeTheEnd =
player && player.life > 0 && !this.state.gameFinished
if (hasLeftGameBeforeTheEnd) {
/* if a user leaves a game before the end,
they cannot join another in the next 5 minutes */
this.presence.hset(
client.auth.uid,
"user_timeout",
new Date(Date.now() + 1000 * 60 * 5).toISOString()
)
}

if (player && this.state.stageLevel <= 5 && !consented) {
/*
if player left game during the loading screen or before stage 6,
we consider they didn't play the game and presume a technical issue
Expand All @@ -594,6 +606,7 @@ export default class GameRoom extends Room<GameState> {
this.setMetadata({
playerIds: removeInArray(this.metadata.playerIds, client.auth.uid)
})

/*logger.info(
`${client.auth.displayName} has been removed from players list`
)*/
Expand Down
12 changes: 10 additions & 2 deletions app/rooms/preparation-room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,12 +377,20 @@ export default class PreparationRoom extends Room<PreparationState> {
}
}

async onJoin(client: Client<undefined, UserRecord>, options: any, auth: UserRecord | undefined) {
async onJoin(
client: Client<undefined, UserRecord>,
options: any,
auth: UserRecord | undefined
) {
if (auth) {
/*logger.info(
`${auth.displayName} ${client.id} join preparation room`
)*/
await this.dispatcher.dispatch(new OnJoinCommand(), { client, options, auth })
await this.dispatcher.dispatch(new OnJoinCommand(), {
client,
options,
auth
})
}
}

Expand Down
2 changes: 2 additions & 0 deletions app/types/enum/CloseCodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum CloseCodes {
USER_NOT_AUTHENTICATED = 4004,
USER_RANK_TOO_LOW = 4005,
USER_RANK_TOO_HIGH = 4006,
USER_TIMEOUT = 4007,
ROOM_FULL = 4010,
ROOM_EMPTY = 4011,
ROOM_DELETED = 4012
Expand All @@ -21,6 +22,7 @@ export const CloseCodesMessages: { [code in CloseCodes]?: string } = {
[CloseCodes.USER_BANNED]: "USER_BANNED",
[CloseCodes.USER_RANK_TOO_LOW]: "USER_RANK_TOO_LOW",
[CloseCodes.USER_NOT_AUTHENTICATED]: "USER_NOT_AUTHENTICATED",
[CloseCodes.USER_TIMEOUT]: "USER_TIMEOUT",
[CloseCodes.ROOM_FULL]: "ROOM_FULL",
[CloseCodes.ROOM_EMPTY]: "ROOM_EMPTY",
[CloseCodes.ROOM_DELETED]: "ROOM_DELETED"
Expand Down
10 changes: 2 additions & 8 deletions app/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,14 +558,8 @@ export interface IPokemonEntity {
apBoost: number,
crit: boolean
): void
addItem(
item: Item,
permanent?: boolean
): void
removeItem(
item: Item,
permenent?: boolean
): void
addItem(item: Item, permanent?: boolean): void
removeItem(item: Item, permanent?: boolean): void
update(
dt: number,
board: Board,
Expand Down
18 changes: 7 additions & 11 deletions app/utils/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,15 @@ export function throttle<T extends (...args: any) => any>(

// prevent concurrent execution of async fn
export function block<T extends (...args: any) => Promise<any>>(fn: T) {
let executing: boolean
let lastResult: ReturnType<T>

let existingPromise: Promise<any> | null = null
return async function (this: any, ...args: any[]) {
const context = this

if (!executing) {
executing = true
lastResult = await fn.apply(context, args)
executing = false
if (existingPromise) {
return existingPromise
}

return lastResult
existingPromise = fn.apply(this, args)
const result = await existingPromise
existingPromise = null
return result
}
}

Expand Down

0 comments on commit 59b978a

Please sign in to comment.