diff --git a/TODO.txt b/TODO.txt index a45a6945d..a9eb11c46 100644 --- a/TODO.txt +++ b/TODO.txt @@ -168,7 +168,7 @@ * implement private game DONE 10/15/2024 * private game can select map DONE 10/16/2024 * Test on android DONE 10/17/2024 -* add troop max +* add troop max DONE 10/31/2024 * use twitter emojis * create build menu * NPC has relations diff --git a/package-lock.json b/package-lock.json index 5d88ff12f..39729f3a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,17 +7,20 @@ "name": "warfront-client", "dependencies": { "@datastructures-js/priority-queue": "^6.3.1", + "@types/dompurify": "^3.0.5", "@types/express": "^4.17.21", "@types/google-protobuf": "^3.15.12", "@types/hammerjs": "^2.0.45", "@types/jimp": "^0.2.28", "@types/msgpack5": "^3.4.6", "@types/raphael": "^2.3.9", + "@types/twemoji": "^13.1.1", "binary-loader": "^0.0.1", "colord": "^2.9.3", "crypto": "^1.0.1", "d3": "^7.9.0", - "express": "^4.19.2", + "dompurify": "^3.1.7", + "express": "^4.21.1", "google-auth-library": "^9.14.0", "googleapis": "^143.0.0", "hammerjs": "^2.0.8", @@ -30,6 +33,7 @@ "protobufjs": "^7.3.2", "pureimage": "^0.4.13", "raphael": "^2.3.0", + "twemoji": "^14.0.2", "uuid": "^10.0.0", "wheelnav": "^1.7.1", "ws": "^8.18.0", @@ -4226,6 +4230,15 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4237,6 +4250,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -4576,6 +4590,15 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/twemoji": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@types/twemoji/-/twemoji-13.1.1.tgz", + "integrity": "sha512-0qnUqLhaSSGsvLXiwWnmcuOza9oGnGwXpxXauB6rEHsU30Dfmvizzwx2TzwUjlsY4ox+39tdG89CpJ/i3J/Cvw==", + "license": "MIT", + "dependencies": { + "twemoji": "*" + } + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", @@ -6034,9 +6057,10 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6881,6 +6905,12 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==", + "license": "(MPL-2.0 OR Apache-2.0)" + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -7314,9 +7344,9 @@ "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==" }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -7324,7 +7354,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -7584,6 +7614,29 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-extra/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -9785,6 +9838,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", + "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", + "license": "MIT", + "dependencies": { + "universalify": "^0.1.2" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/just-extend": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", @@ -13244,6 +13309,24 @@ "fsevents": "~2.3.3" } }, + "node_modules/twemoji": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.2.tgz", + "integrity": "sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==", + "license": "MIT", + "dependencies": { + "fs-extra": "^8.0.1", + "jsonfile": "^5.0.0", + "twemoji-parser": "14.0.0", + "universalify": "^0.1.2" + } + }, + "node_modules/twemoji-parser": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz", + "integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==", + "license": "MIT" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -13364,6 +13447,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index aa5fd40f2..016a4b8cf 100644 --- a/package.json +++ b/package.json @@ -54,17 +54,20 @@ }, "dependencies": { "@datastructures-js/priority-queue": "^6.3.1", + "@types/dompurify": "^3.0.5", "@types/express": "^4.17.21", "@types/google-protobuf": "^3.15.12", "@types/hammerjs": "^2.0.45", "@types/jimp": "^0.2.28", "@types/msgpack5": "^3.4.6", "@types/raphael": "^2.3.9", + "@types/twemoji": "^13.1.1", "binary-loader": "^0.0.1", "colord": "^2.9.3", "crypto": "^1.0.1", "d3": "^7.9.0", - "express": "^4.19.2", + "dompurify": "^3.1.7", + "express": "^4.21.1", "google-auth-library": "^9.14.0", "googleapis": "^143.0.0", "hammerjs": "^2.0.8", @@ -77,6 +80,7 @@ "protobufjs": "^7.3.2", "pureimage": "^0.4.13", "raphael": "^2.3.0", + "twemoji": "^14.0.2", "uuid": "^10.0.0", "wheelnav": "^1.7.1", "ws": "^8.18.0", diff --git a/resources/maps/Europe.json b/resources/maps/Europe.json index 89f52726c..6db7b6015 100644 --- a/resources/maps/Europe.json +++ b/resources/maps/Europe.json @@ -8,7 +8,7 @@ 171, 171 ], - "name": "Iceland", + "name": "Iceland 🇮🇸", "strength": 1 }, { @@ -16,7 +16,7 @@ 477, 473 ], - "name": "Ireland", + "name": "Ireland 🇮🇪", "strength": 1 }, { @@ -24,7 +24,7 @@ 650, 500 ], - "name": "England", + "name": "England 🏴", "strength": 3 }, { @@ -32,7 +32,7 @@ 560, 800 ], - "name": "Spain", + "name": "Spain 🇪🇸", "strength": 2 }, { @@ -40,7 +40,7 @@ 726, 616 ], - "name": "France", + "name": "France 🇫🇷", "strength": 2 }, { @@ -48,7 +48,7 @@ 1050, 745 ], - "name": "Italy", + "name": "Italy 🇮🇹", "strength": 1 }, { @@ -56,7 +56,7 @@ 872, 634 ], - "name": "Switzerland", + "name": "Switzerland 🇨🇭", "strength": 1 }, { @@ -64,7 +64,7 @@ 960, 271 ], - "name": "Norway", + "name": "Norway 🇳🇴", "strength": 1 }, { @@ -72,7 +72,7 @@ 1095, 336 ], - "name": "Sweden", + "name": "Sweden 🇸🇪", "strength": 1 }, { @@ -80,7 +80,7 @@ 1403, 235 ], - "name": "Finland", + "name": "Finland 🇫🇮", "strength": 1 }, { @@ -88,7 +88,7 @@ 775, 541 ], - "name": "Belgium", + "name": "Belgium 🇧🇪", "strength": 1 }, { @@ -96,7 +96,7 @@ 868, 487 ], - "name": "Netherlands", + "name": "Netherlands 🇳🇱", "strength": 1 }, { @@ -104,7 +104,7 @@ 949, 548 ], - "name": "Germany", + "name": "Germany 🇩🇪", "strength": 1 }, { @@ -112,7 +112,7 @@ 1017, 628 ], - "name": "Austria", + "name": "Austria 🇦🇹", "strength": 1 }, { @@ -120,7 +120,7 @@ 1120, 477 ], - "name": "Poland", + "name": "Poland 🇵🇱", "strength": 1 }, { @@ -128,7 +128,7 @@ 1030, 589 ], - "name": "Czechia", + "name": "Czechia 🇨🇿", "strength": 1 }, { @@ -136,7 +136,7 @@ 1540, 602 ], - "name": "Ukraine", + "name": "Ukraine 🇺🇦", "strength": 1 }, { @@ -144,7 +144,7 @@ 1500, 440 ], - "name": "Belarus", + "name": "Belarus 🇧🇾", "strength": 1 }, { @@ -152,7 +152,7 @@ 1424, 754 ], - "name": "Romania", + "name": "Romania 🇷🇴", "strength": 1 }, { @@ -160,7 +160,7 @@ 1580, 834 ], - "name": "Turkiye", + "name": "Turkiye 🇹🇷", "strength": 1 }, { @@ -168,7 +168,7 @@ 525, 955 ], - "name": "Morocco", + "name": "Morocco 🇲🇦", "strength": 1 }, { @@ -176,7 +176,7 @@ 1674, 449 ], - "name": "Russia", + "name": "Russia 🇷🇺", "strength": 3 } ] diff --git a/resources/maps/WorldMap.json b/resources/maps/WorldMap.json index 517cf09f2..dde4813e3 100644 --- a/resources/maps/WorldMap.json +++ b/resources/maps/WorldMap.json @@ -8,7 +8,7 @@ 375, 272 ], - "name": "USA", + "name": "USA 🇺🇸", "strength": 3 }, { @@ -16,7 +16,7 @@ 372, 136 ], - "name": "Canada", + "name": "Canada 🇨🇦", "strength": 2 }, { @@ -24,7 +24,7 @@ 375, 374 ], - "name": "Mexico", + "name": "Mexico 🇲🇽", "strength": 1 }, { @@ -32,7 +32,7 @@ 500, 378 ], - "name": "Cuba", + "name": "Cuba 🇨🇺", "strength": 1 }, { @@ -40,7 +40,7 @@ 524, 474 ], - "name": "Columbia", + "name": "Columbia 🇨🇴", "strength": 1 }, { @@ -48,7 +48,7 @@ 593, 473 ], - "name": "Venezuala", + "name": "Venezuala 🇻🇪", "strength": 1 }, { @@ -56,7 +56,7 @@ 596, 705 ], - "name": "Argentina", + "name": "Argentina 🇦🇷", "strength": 1 }, { @@ -64,7 +64,7 @@ 637, 567 ], - "name": "Brazil", + "name": "Brazil 🇧🇷", "strength": 1 }, { @@ -72,7 +72,7 @@ 1280, 975 ], - "name": "Antartica", + "name": "Antartica 🇦🇶", "strength": 3 }, { @@ -80,7 +80,7 @@ 709, 57 ], - "name": "Greenland", + "name": "Greenland 🇬🇱", "strength": 2 }, { @@ -88,7 +88,7 @@ 831, 112 ], - "name": "Iceland", + "name": "Iceland 🇮🇸", "strength": 1 }, { @@ -96,7 +96,7 @@ 925, 186 ], - "name": "England", + "name": "England 🏴", "strength": 3 }, { @@ -104,7 +104,7 @@ 887, 183 ], - "name": "Ireland", + "name": "Ireland 🇮🇪", "strength": 1 }, { @@ -112,7 +112,7 @@ 908, 264 ], - "name": "Spain", + "name": "Spain 🇪🇸", "strength": 1 }, { @@ -120,7 +120,7 @@ 1004, 250 ], - "name": "Italy", + "name": "Italy 🇮🇹", "strength": 1 }, { @@ -128,7 +128,7 @@ 958, 220 ], - "name": "France", + "name": "France 🇫🇷", "strength": 2 }, { @@ -136,7 +136,7 @@ 997, 205 ], - "name": "Germany", + "name": "Germany 🇩🇪", "strength": 1 }, { @@ -144,7 +144,7 @@ 1064, 101 ], - "name": "Sweden", + "name": "Sweden 🇸🇪", "strength": 1 }, { @@ -152,7 +152,7 @@ 1046, 193 ], - "name": "Poland", + "name": "Poland 🇵🇱", "strength": 1 }, { @@ -160,7 +160,7 @@ 1061, 188 ], - "name": "Belarus", + "name": "Belarus 🇧🇾", "strength": 1 }, { @@ -168,7 +168,7 @@ 1073, 243 ], - "name": "Romania", + "name": "Romania 🇷🇴", "strength": 1 }, { @@ -176,7 +176,7 @@ 1161, 274 ], - "name": "Turkiye", + "name": "Turkiye 🇹🇷", "strength": 1 }, { @@ -184,7 +184,7 @@ 969, 133 ], - "name": "Norway", + "name": "Norway 🇳🇴", "strength": 1 }, { @@ -192,7 +192,7 @@ 1062, 133 ], - "name": "Finland", + "name": "Finland 🇫🇮", "strength": 1 }, { @@ -200,7 +200,7 @@ 1099, 211 ], - "name": "Ukraine", + "name": "Ukraine 🇺🇦", "strength": 1 }, { @@ -208,7 +208,7 @@ 1344, 136 ], - "name": "Russia", + "name": "Russia 🇷🇺", "strength": 3 }, { @@ -216,7 +216,7 @@ 1537, 186 ], - "name": "Mongolia", + "name": "Mongolia 🇲🇳", "strength": 1 }, { @@ -224,7 +224,7 @@ 1524, 328 ], - "name": "China", + "name": "China 🇨🇳", "strength": 3 }, { @@ -232,7 +232,7 @@ 1368, 373 ], - "name": "India", + "name": "India 🇮🇳", "strength": 2 }, { @@ -240,7 +240,7 @@ 1276, 239 ], - "name": "Kazakhstan", + "name": "Kazakhstan 🇰🇿", "strength": 1 }, { @@ -248,7 +248,7 @@ 1238, 309 ], - "name": "Iran", + "name": "Iran 🇮🇷", "strength": 1 }, { @@ -256,7 +256,7 @@ 1178, 351 ], - "name": "Saudi Arabia", + "name": "Saudi Arabia 🇸🇦", "strength": 1 }, { @@ -264,7 +264,7 @@ 1679, 657 ], - "name": "Australia", + "name": "Australia 🇦🇺", "strength": 2 }, { @@ -272,7 +272,7 @@ 1890, 775 ], - "name": "New Zealand", + "name": "New Zealand 🇳🇿", "strength": 0.5 }, { @@ -280,7 +280,7 @@ 918, 342 ], - "name": "Algeria", + "name": "Algeria 🇩🇿", "strength": 1 }, { @@ -288,7 +288,7 @@ 1030, 332 ], - "name": "Libya", + "name": "Libya 🇱🇾", "strength": 1 }, { @@ -296,7 +296,7 @@ 1092, 335 ], - "name": "Egypt", + "name": "Egypt 🇪🇬", "strength": 1 }, { @@ -304,7 +304,7 @@ 963, 410 ], - "name": "Niger", + "name": "Niger 🇳🇪", "strength": 1 }, { @@ -312,7 +312,7 @@ 1112, 406 ], - "name": "Sudan", + "name": "Sudan 🇸🇩", "strength": 1 }, { @@ -320,7 +320,7 @@ 1074, 508 ], - "name": "DRC", + "name": "DRC 🇨🇩", "strength": 1 }, { @@ -328,7 +328,7 @@ 1154, 443 ], - "name": "Ethiopia", + "name": "Ethiopia 🇪🇹", "strength": 1 }, { @@ -336,7 +336,7 @@ 1075, 707 ], - "name": "South Africa", + "name": "South Africa 🇿🇦", "strength": 1 }, { @@ -344,7 +344,7 @@ 1194, 627 ], - "name": "Madagascar", + "name": "Madagascar 🇲🇬", "strength": 0.5 }, { @@ -352,7 +352,7 @@ 1052, 420 ], - "name": "Chad", + "name": "Chad 🇹🇩", "strength": 1 } ] diff --git a/src/client/graphics/Utils.ts b/src/client/graphics/Utils.ts index f22b7b72b..3e838b242 100644 --- a/src/client/graphics/Utils.ts +++ b/src/client/graphics/Utils.ts @@ -1,3 +1,6 @@ +import twemoji from 'twemoji'; +import DOMPurify from 'dompurify'; + export function renderTroops(troops: number): string { let troopsStr = '' @@ -28,4 +31,22 @@ export function createCanvas(): HTMLCanvasElement { canvas.style.touchAction = 'none'; return canvas +} + +export function processName(name: string): string { + const sanitized = Array.from(name).slice(0, 10).join('').replace(/[^\p{L}\p{N}\s\p{Emoji}\p{Emoji_Component}]/gu, ''); + + // First sanitize the raw input - strip everything except text and emojis + const withEmojis = twemoji.parse(sanitized, { + base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/', // Use jsDelivr CDN + folder: 'svg', // or 'png' if you prefer + ext: '.svg' // or '.png' if you prefer + }); + return DOMPurify.sanitize(withEmojis, { + ALLOWED_TAGS: ['img'], + ALLOWED_ATTR: ['src', 'alt', 'class'], + // Only allow twemoji CDN URLs + ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/ + }); + } \ No newline at end of file diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index d3a493b8d..727b15169 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -1,8 +1,12 @@ -import {LitElement, html, css} from 'lit'; -import {customElement, property, state} from 'lit/decorators.js'; -import {Layer} from './Layer'; -import {Game, Player} from '../../../core/game/Game'; -import {ClientID} from '../../../core/Schemas'; +import { LitElement, html, css } from 'lit'; +import { customElement, property, state } from 'lit/decorators.js'; +import { Layer } from './Layer'; +import { Game, Player } from '../../../core/game/Game'; +import { ClientID } from '../../../core/Schemas'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; +import { processName } from '../Utils'; + + interface Entry { name: string @@ -85,6 +89,10 @@ export class Leaderboard extends LitElement implements Layer { :host { display: block; } + img.emoji { + height: 1em; // Match text height + width: auto; // Maintain aspect ratio + } .leaderboard { position: fixed; top: 10px; @@ -158,7 +166,7 @@ export class Leaderboard extends LitElement implements Layer { .map((player, index) => html`