Skip to content

Commit

Permalink
add regional ranks
Browse files Browse the repository at this point in the history
  • Loading branch information
TsFreddie committed Jan 18, 2025
1 parent 21c9f90 commit 5a45463
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 33 deletions.
57 changes: 43 additions & 14 deletions src/lib/ddnet/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,55 @@ export const mapType = (type: string) => {

const FLAG_DEFAULT: string = '/assets/flags/default.png';
const FLAG_MAP: { [key: string]: typeof FLAG_DEFAULT } = {
GER: '/assets/flags/DE.png',
CHN: '/assets/flags/CN.png',
JPN: '/assets/flags/JP.png',
KOR: '/assets/flags/KR.png',
RUS: '/assets/flags/RU.png',
SGP: '/assets/flags/SG.png',
IND: '/assets/flags/IN.png',
AUS: '/assets/flags/AU.png',
ZAF: '/assets/flags/ZA.png',
USA: '/assets/flags/US.png',
BRA: '/assets/flags/BR.png',
CHL: '/assets/flags/CL.png',
NLD: '/assets/flags/NL.png',
FRA: '/assets/flags/FR.png',
GER: '/assets/flags/DE.png',
POL: '/assets/flags/PL.png',
IRN: '/assets/flags/IR.png',
BHR: '/assets/flags/BH.png',
FIN: '/assets/flags/FI.png',
UKR: '/assets/flags/UA.png',
RUS: '/assets/flags/RU.png',
TUR: '/assets/flags/TR.png',
IRN: '/assets/flags/IR.png',
BHR: '/assets/flags/BH.png',
CHL: '/assets/flags/CL.png',
BRA: '/assets/flags/BR.png',
ARG: '/assets/flags/AR.png',
EUR: '/assets/flags/EU.png'
PER: '/assets/flags/PE.png',
USA: '/assets/flags/US.png',
KOR: '/assets/flags/KR.png',
SGP: '/assets/flags/SG.png',
ZAF: '/assets/flags/ZA.png',
IND: '/assets/flags/IN.png',
AUS: '/assets/flags/AU.png',
EUR: '/assets/flags/EU.png',
JPN: '/assets/flags/JP.png'
};

export const KNOWN_REGIONS: Record<string, string> = {
CHN: '中国服',
NLD: '荷兰服',
FRA: '法国服',
GER: '德国服',
POL: '波兰服',
FIN: '芬兰服',
UKR: '乌克兰服',
RUS: '俄罗斯服',
TUR: '土耳其服',
IRN: '伊朗服',
BHR: '巴林服',
CHL: '智利服',
BRA: '巴西服',
ARG: '阿根廷服',
PER: '秘鲁服',
USA: '美国服',
KOR: '韩国服',
TWN: '台服',
SGP: '新加坡服',
ZAF: '南非服',
IND: '印度服',
AUS: '澳大利亚服',
OLD: '历史遗留服'
};

export const flagAsset = (flag: string) => {
Expand Down
57 changes: 56 additions & 1 deletion src/lib/server/fetches/ranks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface PointInfo {

interface FinishInfo {
timestamp: number;
region: string;
region?: string;
type: string;
map: string;
name: string;
Expand Down Expand Up @@ -149,3 +149,58 @@ export const ranks = new FetchCache<RankInfo>('https://ddnet.org/ranks/', async

return ranks;
});

// fetch caches for regional ranks
const regionCache = new Map<string, FetchCache<RankInfo>>();
export const regionalRanks = async (region: string) => {
if (!region) return null;
const existing = regionCache.get(region);
if (existing) return existing;

// create a new fetch cache for the region
// check whether the region exists first
const response = await fetch(`https://ddnet.org/ranks/${region}/`, { method: 'HEAD' });
if (!response.ok) {
return null;
}

const fetchCache = new FetchCache<RankInfo>(
`https://ddnet.org/ranks/${region}/`,
async (response) => {
const ranks = {
ranks: {
points: [],
team: [],
rank: [],
yearly: [],
monthly: [],
weekly: []
},
last_finishes: [],
total_points: 0,
update_time: 0
} satisfies RankInfo;

const html = await response.text();

// we actually want all 500 ranks, so people can check whether they hit the top 500
// there is no other way to check this
new HTMLRewriter()
.on(
[
'div[class="block2 ladder"] > h3',
'div[class="block2 ladder"] > table > tr',
'div[class="block2 ladder"] > table > tr > td',
'div[class="block2 ladder"] > table > tr > td > img'
].join(','),
new LadderHandler(ranks)
)
.on('p[class="toggle"] > span[data-type="date"]', new UpdateTimeHandler(ranks))
.transform(html);

return ranks;
}
);
regionCache.set(region, fetchCache);
return fetchCache;
};
18 changes: 15 additions & 3 deletions src/routes/ddnet/players/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { ranks } from '$lib/server/fetches/ranks';
import { ranks, regionalRanks } from '$lib/server/fetches/ranks';

export const load = (async ({ parent, url }) => {
const region = url.searchParams.get('server');

export const load = (async ({ parent }) => {
try {
// fetch regional ranks if region is provided
if (region) {
const fetch = await regionalRanks(region);
if (!fetch) return error(404);

const result = await fetch.fetch();
return { ...result, region: region.toUpperCase(), ...(await parent()) };
}

// otherwise just fetch global ranks
const result = await ranks.fetch();
return { ...result, ...(await parent()) };
return { ...result, region: 'GLOBAL', ...(await parent()) };
} catch {
return error(500);
}
Expand Down
83 changes: 68 additions & 15 deletions src/routes/ddnet/players/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
import FlagSpan from '$lib/components/FlagSpan.svelte';
import Modal from '$lib/components/Modal.svelte';
import PointCalculation from '$lib/components/PointCalculation.svelte';
import { KNOWN_REGIONS } from '$lib/ddnet/helpers.js';
import { encodeAsciiURIComponent } from '$lib/link';
import type { RankInfo } from '$lib/server/fetches/ranks.js';
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import Fa from 'svelte-fa';
let { data } = $props();
let searchName = $state('');
let showModal = $state(false);
// slicing top 20 ranks for each ladder
const sliceRanks = () => {
const result: RankInfo['ranks'] = {
points: [],
Expand All @@ -27,6 +32,9 @@
return result;
};
let ranks: RankInfo['ranks'] = $state(sliceRanks());
// queried global ranks
const queryRanks = () => {
const result: RankInfo['ranks'] = {
points: [],
Expand All @@ -49,10 +57,21 @@
return result;
};
let ranks: RankInfo['ranks'] = $state(sliceRanks());
let searchName = $state('');
let showModal = $state(false);
// find top 500 ranks with search name
const searchRanksTop500 = () => {
const result: RankInfo['ranks'] = {
points: [],
team: [],
rank: [],
yearly: [],
monthly: [],
weekly: []
};
for (const ladder of Object.keys(data.ranks) as (keyof RankInfo['ranks'])[]) {
result[ladder] = data.ranks[ladder].filter((rank) => rank.name == searchName);
}
return result;
};
const MIN_QUERY_INTERVAL = 200;
let queryingName: string | null = null;
Expand Down Expand Up @@ -93,8 +112,10 @@
}
$effect(() => {
if (queryListName) {
if (data.region == 'GLOBAL' && queryListName) {
ranks = queryRanks();
} else if (searchName) {
ranks = searchRanksTop500();
} else {
ranks = sliceRanks();
}
Expand All @@ -105,13 +126,13 @@
query();
});
const LADDER_NAMES = {
points: '🌎 总通过分',
yearly: '📅 获得通过分 (近365天)',
monthly: '📅 获得通过分 (近30天)',
weekly: '📅 获得通过分 (近7天)',
team: '👥 团队排位分',
rank: '👤 个人排位分'
const LADDER_NAMES: Record<string, [string, string]> = {
points: ['总通过分', '🌎'],
yearly: ['获得通过分 (近365天)', '📅'],
monthly: ['获得通过分 (近30天)', '📅'],
weekly: ['获得通过分 (近7天)', '📅'],
team: ['团队排位分', '👥'],
rank: ['个人排位分', '👤']
};
</script>

Expand Down Expand Up @@ -148,6 +169,20 @@
showModal = !showModal;
}}><Fa class="inline" icon={faQuestionCircle}></Fa> 分数说明</button
>
<select
class="rounded bg-slate-700 px-4 py-2 text-slate-300"
value={data.region}
onchange={(ev: Event) => {
const value = (ev.currentTarget as HTMLSelectElement).value;
if (value == 'GLOBAL') goto(`/ddnet/players`);
else goto(`/ddnet/players?server=${value.toLowerCase()}`);
}}
>
<option value="GLOBAL">全球</option>
{#each Object.keys(KNOWN_REGIONS) as key}
<option value={key}>{KNOWN_REGIONS[key]}</option>
{/each}
</select>
</div>

<!-- horizontally scrollable list of cards -->
Expand All @@ -171,6 +206,8 @@

<div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
{#each Object.keys(ranks) as ladder}
{@const ladderName = LADDER_NAMES[ladder]}
{@const regionName = KNOWN_REGIONS[data.region] ?? '全球'}
<div
class="mt-4 rounded-lg bg-slate-700 p-4 shadow-md {ranks[
ladder as any as keyof RankInfo['ranks']
Expand All @@ -179,14 +216,30 @@
: ''}"
>
{#if ladder == 'points'}
<h2 class="text-xl font-bold">{LADDER_NAMES[ladder]}(共 {data.total_points}pts)</h2>
{#if data.region == 'GLOBAL'}
<h2 class="text-xl font-bold">
{ladderName[1]}{regionName}{ladderName[0]}(共 {data.total_points}pts)
</h2>
{:else}
<h2 class="text-xl font-bold">
<FlagSpan flag={data.region} />{regionName}{ladderName[0]}(共 {data.total_points}pts)
</h2>
{/if}
{:else}
<h2 class="text-xl font-bold">{LADDER_NAMES[ladder as any as keyof RankInfo['ranks']]}</h2>
<h2 class="text-xl font-bold">
{ladderName[1]}{regionName}{ladderName[0]}
</h2>
{/if}
<ul class="mt-2">
{#if ranks[ladder as any as keyof RankInfo['ranks']].length == 0}
<li>
<span class="text-center">未获得记录</span>
{#if data.region != 'GLOBAL' && !searchName}
<span class="text-center">无记录,可能已停服</span>
{:else if data.region != 'GLOBAL' && searchName}
<span class="text-center">未进区服前 500 名</span>
{:else}
<span class="text-center">未获得记录</span>
{/if}
</li>
{:else}
{#each ranks[ladder as any as keyof RankInfo['ranks']] as rank}
Expand Down

0 comments on commit 5a45463

Please sign in to comment.