add vouchers display
BIN
public/cards/v_antimatter.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
public/cards/v_blank.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/cards/v_clearance_sale.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/cards/v_crystal_ball.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/cards/v_directors_cut.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/cards/v_glow_up.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/cards/v_grabber.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/cards/v_hieroglyph.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/cards/v_hone.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/cards/v_illusion.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/cards/v_liquidation.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/cards/v_locked.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/cards/v_magic_trick.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/cards/v_money_tree.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
public/cards/v_nacho_tong.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/cards/v_observatory.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
public/cards/v_omen_globe.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/cards/v_overstock_norm.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/cards/v_overstock_plus.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/cards/v_paint_brush.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/cards/v_palette.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/cards/v_petroglyph.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
public/cards/v_planet_merchant.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/cards/v_planet_tycoon.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/cards/v_recyclomancy.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/cards/v_reroll_glut.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/cards/v_reroll_surplus.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/cards/v_retcon.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/cards/v_seed_money.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/cards/v_tarot_merchant.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/cards/v_tarot_tycoon.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/cards/v_telescope.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/cards/v_undiscovered.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/cards/v_wasteful.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
@@ -34,6 +34,7 @@ import {
|
|||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip'
|
} from '@/components/ui/tooltip'
|
||||||
import { jokers } from '@/shared/jokers'
|
import { jokers } from '@/shared/jokers'
|
||||||
|
import { vouchers } from '@/shared/vouchers'
|
||||||
import { useFormatter } from 'next-intl'
|
import { useFormatter } from 'next-intl'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useSearchParams } from 'next/navigation'
|
||||||
@@ -107,7 +108,12 @@ type Game = {
|
|||||||
logOwnerFinalJokers: string[] // Log owner's final jokers
|
logOwnerFinalJokers: string[] // Log owner's final jokers
|
||||||
opponentFinalJokers: string[] // Opponent's final jokers
|
opponentFinalJokers: string[] // Opponent's final jokers
|
||||||
events: LogEvent[]
|
events: LogEvent[]
|
||||||
rerolls: number
|
rerolls: number // Log owner's reroll count
|
||||||
|
rerollCostTotal: number // Log owner's total reroll cost
|
||||||
|
logOwnerVouchers: string[] // Log owner's vouchers
|
||||||
|
opponentRerolls: number // Opponent's reroll count
|
||||||
|
opponentRerollCostTotal: number // Opponent's total reroll cost
|
||||||
|
opponentVouchers: string[] // Opponent's vouchers
|
||||||
winner: 'logOwner' | 'opponent' | null // Who won the game
|
winner: 'logOwner' | 'opponent' | null // Who won the game
|
||||||
pvpBlinds: PvpBlind[] // PVP blind data
|
pvpBlinds: PvpBlind[] // PVP blind data
|
||||||
currentPvpBlind: number | null // Current PVP blind number
|
currentPvpBlind: number | null // Current PVP blind number
|
||||||
@@ -140,6 +146,11 @@ const initGame = (id: number, startDate: Date): Game => ({
|
|||||||
opponentFinalJokers: [],
|
opponentFinalJokers: [],
|
||||||
events: [],
|
events: [],
|
||||||
rerolls: 0,
|
rerolls: 0,
|
||||||
|
rerollCostTotal: 0,
|
||||||
|
logOwnerVouchers: [],
|
||||||
|
opponentRerolls: 0,
|
||||||
|
opponentRerollCostTotal: 0,
|
||||||
|
opponentVouchers: [],
|
||||||
winner: null,
|
winner: null,
|
||||||
pvpBlinds: [],
|
pvpBlinds: [],
|
||||||
currentPvpBlind: null,
|
currentPvpBlind: null,
|
||||||
@@ -347,6 +358,34 @@ export default function LogParser() {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (line.includes('Client got nemesisEndGameStats message')) {
|
||||||
|
if (currentGame) {
|
||||||
|
// Extract Opponent Reroll Count
|
||||||
|
const rerollCountMatch = line.match(/\(reroll_count: (\d+)\)/)
|
||||||
|
if (rerollCountMatch?.[1]) {
|
||||||
|
currentGame.opponentRerolls = Number.parseInt(
|
||||||
|
rerollCountMatch[1],
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Opponent Reroll Cost Total
|
||||||
|
const rerollCostMatch = line.match(/\(reroll_cost_total: (\d+)\)/)
|
||||||
|
if (rerollCostMatch?.[1]) {
|
||||||
|
currentGame.opponentRerollCostTotal = Number.parseInt(
|
||||||
|
rerollCostMatch[1],
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Opponent Vouchers
|
||||||
|
const vouchersMatch = line.match(/\(vouchers: ([^)]+)\)/)
|
||||||
|
if (vouchersMatch?.[1]) {
|
||||||
|
currentGame.opponentVouchers = vouchersMatch[1].split('-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (line.includes('Client sent message: action:receiveEndGameJokers')) {
|
if (line.includes('Client sent message: action:receiveEndGameJokers')) {
|
||||||
if (currentGame) {
|
if (currentGame) {
|
||||||
// Mark end date if not already set (might happen slightly before 'got')
|
// Mark end date if not already set (might happen slightly before 'got')
|
||||||
@@ -362,6 +401,31 @@ export default function LogParser() {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (line.includes('Client sent message: action:nemesisEndGameStats')) {
|
||||||
|
if (currentGame) {
|
||||||
|
// Extract Log Owner Reroll Count
|
||||||
|
const rerollCountMatch = line.match(/reroll_count:(\d+)/)
|
||||||
|
if (rerollCountMatch?.[1]) {
|
||||||
|
currentGame.rerolls = Number.parseInt(rerollCountMatch[1], 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Log Owner Reroll Cost Total
|
||||||
|
const rerollCostMatch = line.match(/reroll_cost_total:(\d+)/)
|
||||||
|
if (rerollCostMatch?.[1]) {
|
||||||
|
currentGame.rerollCostTotal = Number.parseInt(
|
||||||
|
rerollCostMatch[1],
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract Log Owner Vouchers
|
||||||
|
const vouchersMatch = line.match(/vouchers:(.+)$/)
|
||||||
|
if (vouchersMatch?.[1]) {
|
||||||
|
currentGame.logOwnerVouchers = vouchersMatch[1].split('-')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (lineLower.includes('startgame message')) {
|
if (lineLower.includes('startgame message')) {
|
||||||
if (currentGame) {
|
if (currentGame) {
|
||||||
if (!currentGame.endDate) currentGame.endDate = timestamp
|
if (!currentGame.endDate) currentGame.endDate = timestamp
|
||||||
@@ -855,7 +919,8 @@ export default function LogParser() {
|
|||||||
? ((currentGame.endDate instanceof Date
|
? ((currentGame.endDate instanceof Date
|
||||||
? currentGame.endDate.getTime()
|
? currentGame.endDate.getTime()
|
||||||
: new Date(currentGame.endDate).getTime()) -
|
: new Date(currentGame.endDate).getTime()) -
|
||||||
currentGame.startDate.getTime()) / 1000
|
currentGame.startDate.getTime()) /
|
||||||
|
1000
|
||||||
: null
|
: null
|
||||||
games.push(currentGame)
|
games.push(currentGame)
|
||||||
}
|
}
|
||||||
@@ -1049,8 +1114,18 @@ export default function LogParser() {
|
|||||||
: opponentLabel}
|
: opponentLabel}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Rerolls:</strong>{' '}
|
<strong>{ownerLabel} Rerolls:</strong>{' '}
|
||||||
{game.rerolls || 'Unknown'}
|
{game.rerolls || 'Unknown'}
|
||||||
|
{game.rerollCostTotal
|
||||||
|
? ` (Cost: ${game.rerollCostTotal})`
|
||||||
|
: ''}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{opponentLabel} Rerolls:</strong>{' '}
|
||||||
|
{game.opponentRerolls || 'Unknown'}
|
||||||
|
{game.opponentRerollCostTotal
|
||||||
|
? ` (Cost: ${game.opponentRerollCostTotal})`
|
||||||
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Deck:</strong> {game.deck || 'Unknown'}
|
<strong>Deck:</strong> {game.deck || 'Unknown'}
|
||||||
@@ -1277,6 +1352,93 @@ export default function LogParser() {
|
|||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className='text-lg'>Vouchers</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className='space-y-3 text-sm'>
|
||||||
|
<div>
|
||||||
|
<strong>
|
||||||
|
{ownerLabel}
|
||||||
|
{game.winner === 'logOwner' ? ' 🏆' : ''}:
|
||||||
|
</strong>
|
||||||
|
{game.logOwnerVouchers.length > 0 ? (
|
||||||
|
<ul className='mt-3 ml-4 flex list-inside gap-3'>
|
||||||
|
{game.logOwnerVouchers.map((voucher, i) => {
|
||||||
|
if (!voucher) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const cleanName =
|
||||||
|
vouchers[voucher]?.name ??
|
||||||
|
cleanVoucherKey(voucher)
|
||||||
|
return (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Simple list
|
||||||
|
<li key={i} className={'list-none'}>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
'flex flex-col items-center justify-center gap-2'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={`/cards/${voucher}.png`}
|
||||||
|
alt={cleanName}
|
||||||
|
width={142}
|
||||||
|
height={190}
|
||||||
|
/>
|
||||||
|
<span>{cleanName}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p className='text-gray-500 italic'>
|
||||||
|
No data found.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>
|
||||||
|
{opponentLabel}
|
||||||
|
{game.winner === 'opponent' ? ' 🏆' : ''}:
|
||||||
|
</strong>
|
||||||
|
{game.opponentVouchers.length > 0 ? (
|
||||||
|
<ul className='mt-3 ml-4 flex list-inside gap-3'>
|
||||||
|
{game.opponentVouchers.map((voucher, i) => {
|
||||||
|
if (!voucher) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const cleanName =
|
||||||
|
vouchers[voucher]?.name ??
|
||||||
|
cleanVoucherKey(voucher)
|
||||||
|
return (
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: Simple list
|
||||||
|
<li key={i} className={'list-none'}>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
'flex flex-col items-center justify-center gap-2'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={`/cards/${voucher}.png`}
|
||||||
|
alt={cleanName}
|
||||||
|
width={142}
|
||||||
|
height={190}
|
||||||
|
/>
|
||||||
|
<span>{cleanName}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p className='text-gray-500 italic'>
|
||||||
|
No data found.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className='text-lg'>Mods</CardTitle>
|
<CardTitle className='text-lg'>Mods</CardTitle>
|
||||||
@@ -1637,6 +1799,18 @@ function cleanJokerKey(key: string): string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanVoucherKey(key: string): string {
|
||||||
|
if (!key) return ''
|
||||||
|
return key
|
||||||
|
.trim()
|
||||||
|
.replace(/^v_/, '') // Remove prefix v_
|
||||||
|
.replace(/_/g, ' ') // Replace underscores with spaces
|
||||||
|
.replace(
|
||||||
|
/\w\S*/g, // Capitalize each word (Title Case)
|
||||||
|
(txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type JsonValue =
|
type JsonValue =
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
|
|||||||
41
src/shared/vouchers.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export interface VoucherInfo {
|
||||||
|
name: string
|
||||||
|
file: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const vouchers: { [key: string]: VoucherInfo } = {
|
||||||
|
v_antimatter: { name: 'Antimatter', file: 'v_antimatter.png' },
|
||||||
|
v_blank: { name: 'Blank', file: 'v_blank.png' },
|
||||||
|
v_clearance_sale: { name: 'Clearance Sale', file: 'v_clearance_sale.png' },
|
||||||
|
v_crystal_ball: { name: 'Crystal Ball', file: 'v_crystal_ball.png' },
|
||||||
|
v_directors_cut: { name: "Director's Cut", file: 'v_directors_cut.png' },
|
||||||
|
v_glow_up: { name: 'Glow Up', file: 'v_glow_up.png' },
|
||||||
|
v_grabber: { name: 'Grabber', file: 'v_grabber.png' },
|
||||||
|
v_hieroglyph: { name: 'Hieroglyph', file: 'v_hieroglyph.png' },
|
||||||
|
v_hone: { name: 'Hone', file: 'v_hone.png' },
|
||||||
|
v_illusion: { name: 'Illusion', file: 'v_illusion.png' },
|
||||||
|
v_liquidation: { name: 'Liquidation', file: 'v_liquidation.png' },
|
||||||
|
v_locked: { name: 'Locked', file: 'v_locked.png' },
|
||||||
|
v_magic_trick: { name: 'Magic Trick', file: 'v_magic_trick.png' },
|
||||||
|
v_money_tree: { name: 'Money Tree', file: 'v_money_tree.png' },
|
||||||
|
v_nacho_tong: { name: 'Nacho Tong', file: 'v_nacho_tong.png' },
|
||||||
|
v_observatory: { name: 'Observatory', file: 'v_observatory.png' },
|
||||||
|
v_omen_globe: { name: 'Omen Globe', file: 'v_omen_globe.png' },
|
||||||
|
v_overstock_norm: { name: 'Overstock', file: 'v_overstock_norm.png' },
|
||||||
|
v_overstock_plus: { name: 'Overstock Plus', file: 'v_overstock_plus.png' },
|
||||||
|
v_paint_brush: { name: 'Paint Brush', file: 'v_paint_brush.png' },
|
||||||
|
v_palette: { name: 'Palette', file: 'v_palette.png' },
|
||||||
|
v_petroglyph: { name: 'Petroglyph', file: 'v_petroglyph.png' },
|
||||||
|
v_planet_merchant: { name: 'Planet Merchant', file: 'v_planet_merchant.png' },
|
||||||
|
v_planet_tycoon: { name: 'Planet Tycoon', file: 'v_planet_tycoon.png' },
|
||||||
|
v_recyclomancy: { name: 'Recyclomancy', file: 'v_recyclomancy.png' },
|
||||||
|
v_reroll_glut: { name: 'Reroll Glut', file: 'v_reroll_glut.png' },
|
||||||
|
v_reroll_surplus: { name: 'Reroll Surplus', file: 'v_reroll_surplus.png' },
|
||||||
|
v_retcon: { name: 'Retcon', file: 'v_retcon.png' },
|
||||||
|
v_seed_money: { name: 'Seed Money', file: 'v_seed_money.png' },
|
||||||
|
v_tarot_merchant: { name: 'Tarot Merchant', file: 'v_tarot_merchant.png' },
|
||||||
|
v_tarot_tycoon: { name: 'Tarot Tycoon', file: 'v_tarot_tycoon.png' },
|
||||||
|
v_telescope: { name: 'Telescope', file: 'v_telescope.png' },
|
||||||
|
v_undiscovered: { name: 'Locked', file: 'v_undiscovered.png' },
|
||||||
|
v_wasteful: { name: 'Wasteful', file: 'v_wasteful.png' },
|
||||||
|
}
|
||||||
118
test.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { db } from '@/server/db'
|
||||||
|
import { player_games } from '@/server/db/schema'
|
||||||
|
import { desc, eq } from 'drizzle-orm'
|
||||||
|
import { Redis } from 'ioredis'
|
||||||
|
const redisClient = new Redis(process.env.REDIS_URL as string)
|
||||||
|
|
||||||
|
type PlayerState = {
|
||||||
|
status: string
|
||||||
|
currentMatch?: {
|
||||||
|
opponentId: string
|
||||||
|
startTime: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusCount = {
|
||||||
|
[key: string]: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueueEntry = {
|
||||||
|
key: string
|
||||||
|
value: PlayerState
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findQueueingPlayers(redis: Redis): Promise<QueueEntry[]> {
|
||||||
|
try {
|
||||||
|
const queueingPlayers: QueueEntry[] = []
|
||||||
|
let cursor = '0'
|
||||||
|
const pattern = 'player:*:state'
|
||||||
|
|
||||||
|
do {
|
||||||
|
const [newCursor, keys] = await redis.scan(
|
||||||
|
cursor,
|
||||||
|
'MATCH',
|
||||||
|
pattern,
|
||||||
|
'COUNT',
|
||||||
|
'1000'
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor = newCursor
|
||||||
|
|
||||||
|
if (keys.length > 0) {
|
||||||
|
const values = await redis.mget(keys)
|
||||||
|
|
||||||
|
keys.forEach((key, index) => {
|
||||||
|
const value = values[index]
|
||||||
|
if (!value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const state = JSON.parse(value) as PlayerState
|
||||||
|
if (state.status === 'queuing') {
|
||||||
|
queueingPlayers.push({
|
||||||
|
key,
|
||||||
|
value: state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('Failed to parse player state:', parseError)
|
||||||
|
console.error('Problematic value:', value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} while (cursor !== '0')
|
||||||
|
|
||||||
|
return queueingPlayers
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to find queuing players:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redisClient.on('connect', () => {
|
||||||
|
console.log('Redis client connected')
|
||||||
|
})
|
||||||
|
|
||||||
|
redisClient.on('error', (err) => {
|
||||||
|
console.error('Redis client error:', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function logQueueingPlayers() {
|
||||||
|
try {
|
||||||
|
const queueingPlayers = await findQueueingPlayers(redisClient)
|
||||||
|
console.log('Found', queueingPlayers.length, 'queuing players:')
|
||||||
|
console.log(queueingPlayers)
|
||||||
|
|
||||||
|
return await Promise.all(
|
||||||
|
queueingPlayers.map(async (player) => {
|
||||||
|
const lastGame = await db
|
||||||
|
.select()
|
||||||
|
.from(player_games)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
player_games.playerId,
|
||||||
|
player.key.replace('player:', '').replace(':state', '')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderBy(desc(player_games.gameTime))
|
||||||
|
.limit(1)
|
||||||
|
return lastGame[0]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to log queuing players:', error)
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
await redisClient.quit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logQueueingPlayers()
|
||||||
|
.then((e) => {
|
||||||
|
for (const p of e)
|
||||||
|
console.log('--', p?.playerName, Math.round(p?.playerMmr ?? 0), 'MMR')
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Unhandled error in logQueueingPlayers:', error)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||