Compare commits

...
213 Commits
Author SHA1 Message Date
tresabhi 1a90e9312d feat: logs 2025-05-18 09:55:02 -05:00
tresabhi 2c7082fe04 feat: fast assets 2025-05-18 09:54:12 -05:00
tresabhi 518a83ee73 fix: bruh what 2025-05-16 23:03:56 -05:00
tresabhi 25c40bcea2 feat: logs 2025-05-16 22:33:31 -05:00
tresabhi f88ac8daf7 feat: more heuristic formats 2025-05-16 22:23:58 -05:00
tresabhi c4be6b78b6 feat: heuristics 2025-05-16 22:23:34 -05:00
tresabhi 2cfc65a990 feat: migrate tanks 2025-05-16 22:11:01 -05:00
tresabhi a8eb751048 feat: migrate tanks search page 2025-05-16 19:20:29 -05:00
tresabhi f030fd53b1 feat: slugs 2025-05-16 19:14:56 -05:00
tresabhi da506083c9 feat: migrate tier list 2025-05-16 18:15:27 -05:00
tresabhi 8bdc4d9b1b fix: sessio 2025-05-16 18:13:57 -05:00
tresabhi e0718d9ebb fix: migrate performance 2025-05-16 18:12:19 -05:00
tresabhi dbf1718d39 fix: migrate guess 2025-05-16 18:09:33 -05:00
tresabhi 84dd13c0ac feat: rename to path 2025-05-16 18:07:15 -05:00
tresabhi fdecef5f9c feat: migrate gallery 2025-05-16 18:06:04 -05:00
tresabhi 198e800958 feat: remove rybbit 2025-05-16 18:02:08 -05:00
tresabhi 29bb8ada06 feat: reduced navbar shadow 2025-05-16 18:01:10 -05:00
tresabhi 662f116afe fix: move guess components to src/components 2025-05-16 18:00:08 -05:00
tresabhi 02f0c078a5 fix: legacy redirect embed pages 2025-05-16 00:07:43 -05:00
tresabhi 4dd1113b6d feat: migrate embed and fix embed not rendering because of no context 2025-05-16 00:03:47 -05:00
tresabhi e656b83719 feat: migrate compare 2025-05-15 23:54:07 -05:00
tresabhi b90b355f8f feat: migrate urls 2025-05-15 23:44:20 -05:00
tresabhi 165a9cd719 feat: logs 2025-05-15 23:31:26 -05:00
tresabhi 5b8da36fb8 feat: effective and persistent angle 2025-05-15 23:21:01 -05:00
tresabhi bbe682fa73 feat: bigger icons 2025-05-15 23:03:10 -05:00
tresabhi af08df1526 feat: ricochet visualizer shell switcher 2025-05-15 23:02:15 -05:00
tresabhi 47a60adac7 feat: disable reload visualizer 2025-05-15 22:46:17 -05:00
tresabhi 10f3bc0026 fix: compare labels 2025-05-15 22:45:09 -05:00
tresabhi e7da1d582f fix: use pitch angle of transition of 15deg 2025-05-15 22:40:09 -05:00
tresabhi df70816f7e fix: use svh 2025-05-15 22:37:58 -05:00
tresabhi 091364d7f3 Merge branch 'main' into dev 2025-05-15 22:35:42 -05:00
tresabhi 753c06eee8 feat: logs 2025-05-07 20:54:24 -05:00
tresabhi 17713243b5 feat: final stuff 2025-05-07 20:51:52 -05:00
tresabhi 4e4f070368 feat: shinier 2025-05-07 19:43:40 -05:00
tresabhi 4064f163ec feat: remove plug 2025-05-07 19:42:49 -05:00
tresabhi b911f64847 feat: out animation 2025-05-07 19:41:47 -05:00
tresabhi b86b6e09d0 feat: font 2025-05-07 19:32:18 -05:00
tresabhi d2e7a477bb feat: fullscreen 2025-05-07 19:16:10 -05:00
tresabhi f9c182e68c feat: hide guess 2025-05-06 21:30:35 -05:00
tresabhi 7c497ab681 feat: more tintgs 2025-05-06 13:59:41 -05:00
tresabhi c840086c11 feat: tints 2025-05-06 13:56:56 -05:00
tresabhi 8649d09a94 feat: shadow 2025-05-06 13:39:07 -05:00
tresabhi c764564a96 feat: outline title 2025-05-06 13:29:40 -05:00
tresabhi ad8cbb96b7 feat: nerd time 2025-05-05 22:54:39 -05:00
tresabhi b409e26ca5 feat: reveal titles 2025-05-05 21:41:45 -05:00
tresabhi fbbfa5307c feat: skip 2025-05-05 21:22:05 -05:00
tresabhi 4165533838 feat: simplier hiding 2025-05-05 21:05:46 -05:00
tresabhi fb47ab3e64 feat: new auto rotate animation 2025-05-04 22:11:41 -05:00
tresabhi 57971453ad feat: stats 2025-05-04 21:45:38 -05:00
tresabhi c8cb490b48 feat: full logic 2025-05-04 21:39:09 -05:00
tresabhi 232f24181f feat: some guessing mechanics 2025-05-04 21:02:50 -05:00
tresabhi 46441d74d8 feat: some baswork 2025-05-04 13:08:28 -05:00
tresabhi a2274a6f0c idk man 2025-05-04 11:24:33 -05:00
tresabhi 0eb2827e7b Merge branch 'main' into dev 2025-05-02 21:28:54 -05:00
TrèsAbhi 41cb3f8511 Update Party3.astro 2025-05-02 09:32:22 -05:00
tresabhi 9405dccee9 feat: even more robust 2025-04-29 16:36:59 -05:00
tresabhi 83b0c6abc3 feat: persistent shells 2025-04-29 16:23:22 -05:00
tresabhi bf1621001b feat: more glow 2025-04-29 00:11:27 -05:00
tresabhi 8a5a4bf696 feat: glow and shells 2025-04-29 00:10:42 -05:00
tresabhi a99cce07dc feat: background 2025-04-28 17:43:29 -05:00
tresabhi d8c26ba24a feat: begennings of reload visuals 2025-04-28 17:21:42 -05:00
imgbot[bot]andImgBotApp f0b393c48f [ImgBot] Optimize images (#152)
*Total -- 1,028.23kb -> 900.50kb (12.42%)

/packages/website/public/assets/images/3d/grid.png -- 610.88kb -> 490.28kb (19.74%)
/packages/website/public/assets/images/icons/aim-caret.png -- 1.93kb -> 1.84kb (4.6%)
/packages/website/public/assets/images/icons/xp-multiplier.png -- 4.54kb -> 4.43kb (2.28%)
/packages/website/public/assets/images/third-party/wotb-news.png -- 404.33kb -> 397.41kb (1.71%)
/packages/website/public/assets/images/icons/custom-shell.png -- 6.55kb -> 6.54kb (0.18%)

Signed-off-by: ImgBotApp <[email protected]>
Co-authored-by: ImgBotApp <[email protected]>
2025-04-26 13:32:03 -05:00
TrèsAbhi d0d205bb93 New Crowdin updates (#150)
* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (French)
2025-04-26 13:31:52 -05:00
tresabhi aeaf52d3ac feat: darker view range 2025-04-24 22:06:28 -05:00
tresabhi e05358952a feat: stat search 2025-04-24 22:04:33 -05:00
tresabhi c1000e1608 feat: more responsive 2025-04-24 20:54:24 -05:00
tresabhi 46c86c5352 feat: more decimal places for dimensios 2025-04-24 20:37:31 -05:00
tresabhi 507b503560 feat: remove units and make ranks softer 2025-04-24 20:35:58 -05:00
tresabhi 37ca981d33 feat: more responsive 2025-04-24 20:24:12 -05:00
tresabhi efe6d2b1a5 feat: better navbar background 2025-04-24 20:07:17 -05:00
tresabhi 9f79f9c1e8 fix: no draggable images 2025-04-22 22:01:36 -05:00
tresabhi 38b4c62777 feat: new maps 2025-04-22 21:56:39 -05:00
tresabhi df1bc56a4c feat: isolate stat sections 2025-04-22 21:22:26 -05:00
tresabhi c3d2b063bc feat: new ricochet visualizer 2025-04-22 21:10:22 -05:00
tresabhi 1b94748668 feat: start of ricochet visual 2 2025-04-22 16:35:49 -05:00
tresabhi f4f804727f feat: slicker design 2025-04-22 14:42:04 -05:00
tresabhi 3729072594 feat: prettier 2025-04-22 14:36:31 -05:00
tresabhi c174f32e15 feat: view range visualizer 2025-04-22 14:35:34 -05:00
tresabhi 2443ecde2f feat: turretless support 2025-04-22 13:10:01 -05:00
tresabhi 9c027b3dde feat: puncture hole 2025-04-22 01:06:38 -05:00
tresabhi 22d794ac65 feat: animations 2025-04-22 00:50:14 -05:00
tresabhi 35073c184e test 2025-04-21 18:39:28 -05:00
tresabhi 4741406e4a fix: include prisma only in bot 2025-04-21 18:30:35 -05:00
tresabhi 24107433c6 fix: use built prisma 2025-04-21 18:24:19 -05:00
tresabhi 05c6fcfb34 fix: inline 2025-04-21 18:21:12 -05:00
tresabhi be7a16d399 feat: lower contrast 2025-04-21 18:19:05 -05:00
tresabhi c7f781dbb2 feat: ricochet visualizer 2025-04-21 18:09:57 -05:00
tresabhi a66c6ebb27 feat: classic styled components 2025-04-21 17:01:32 -05:00
tresabhi 83aeca2e5e Merge branch 'main' into dev 2025-04-21 16:39:49 -05:00
tresabhi 89a3db05d8 Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-04-21 16:39:08 -05:00
tresabhi 6050145b16 fix: home page hot tanks 2025-04-21 16:39:06 -05:00
tresabhi 77c78741b3 fix: remove unused type 2025-04-21 16:37:29 -05:00
TrèsAbhi f35670d6a8 Update Party3.astro 2025-04-21 14:36:24 -05:00
tresabhi 3392a57480 feat: rotation visualize 2025-04-20 23:00:08 -05:00
tresabhi ed64ee8228 feat: hull traverse visualizer 2025-04-20 22:51:53 -05:00
tresabhi 4f47d6ce85 Merge branch 'main' into dev 2025-04-20 21:35:06 -05:00
tresabhi 92d56ac086 fix: onPointerDown 2025-04-20 21:34:57 -05:00
tresabhi 671b8b60b9 feat: update prisma with custom output 2025-04-20 21:31:15 -05:00
tresabhi ba5a5245d9 feat: update prisma 2025-04-20 21:27:00 -05:00
tresabhi eee262add7 fix: submodules 2025-04-20 21:18:54 -05:00
tresabhi e5eaf3fd75 fix: submodules 2025-04-20 21:18:02 -05:00
tresabhi 5fdc9044ca fix: hero interactibility 2025-04-20 21:16:50 -05:00
tresabhi 0924a77776 fix: mas width for meta section 2025-04-20 21:13:40 -05:00
tresabhi 1a46e176f5 fix: tankopedia title 2025-04-20 21:11:16 -05:00
tresabhi efb8df9769 feat: logs 2025-04-20 21:00:57 -05:00
tresabhi 04e09b84ff feat: skeleton and proper file extensions 2025-04-20 20:49:54 -05:00
tresabhi b52c107af9 feat: parrelel api 2025-04-19 00:48:29 -05:00
tresabhi c5e6f5e2eb feat: glossary cron 2025-04-19 00:47:28 -05:00
tresabhi 365c68aa4d feat: move popular tanks to cron job 2025-04-18 23:57:04 -05:00
tresabhi ff9ec55da6 feat: increase title gap 2025-04-17 13:06:40 -05:00
tresabhi 2a2994ae69 feat: more responsive 2025-04-17 13:00:53 -05:00
tresabhi e3c6497d29 feat: better meta 2025-04-17 00:23:01 -05:00
tresabhi bdc013b4c8 fix: cleaner clear button 2025-04-16 23:34:30 -05:00
tresabhi e20d65d218 feat: darker 2025-04-16 23:22:12 -05:00
tresabhi 8b726c12d9 fix: invalidate frams on reveal 2025-04-16 23:20:44 -05:00
tresabhi da704e6181 feat: transition skeleton 2025-04-16 23:18:24 -05:00
tresabhi 64d346ccb6 feat: in-hero controls 2025-04-15 16:54:36 -05:00
tresabhi c090ad6492 feat: disturb on tab change and no title select 2025-04-15 16:37:10 -05:00
tresabhi 8b6610151e fix: keys 2025-04-15 16:29:14 -05:00
tresabhi 545b655d4e feat: slicker loading 2025-04-15 00:35:12 -05:00
tresabhi 0e6aa734cb feat: advanced jsxTree 2025-04-15 00:11:49 -05:00
tresabhi 2bef96475c feat: quick controls 2025-04-14 23:56:29 -05:00
tresabhi 45938c875f feat: skeleton search 2025-04-14 23:28:35 -05:00
tresabhi 9d916a588d feat: cooler header 2025-04-14 19:26:41 -05:00
tresabhi f0295079cc feat: quiz input angles animaiton 2025-04-14 18:56:02 -05:00
tresabhi 6d8387fa9d feat: foggy transition 2025-04-14 18:52:49 -05:00
tresabhi 7a18de6650 fix: don't reset camera on tab change 2025-04-14 17:30:51 -05:00
tresabhi cc6c4faa2d feat: migration 2025-04-06 22:57:35 -05:00
TrèsAbhi 4253cd3e1b fix: allow 2025-04-06 22:04:01 -05:00
TrèsAbhi 75cf827082 Update website.yml 2025-04-06 19:59:27 -05:00
TrèsAbhi 73c8807f14 no, no, no, NOW all hail pyogenics i messed up the last time 2025-04-06 16:03:04 -05:00
TrèsAbhi 9fac6dd40d all hail pyogenics 2025-04-06 15:53:23 -05:00
TrèsAbhi c06c338536 fix: add env and remove compressors 2025-04-06 10:48:08 -05:00
TrèsAbhi 9258623931 Update astro.config.mjs 2025-04-06 00:12:01 -05:00
tresabhi 3b02c25973 fix: disable playformCompress 2025-04-05 22:02:26 -05:00
tresabhi f90768d155 fix: env 2025-04-05 17:08:55 -05:00
tresabhi d77bd6a5f9 feat: logs 2025-04-05 16:15:55 -05:00
tresabhi 6836cb327f feat: more images 2025-04-05 16:07:42 -05:00
tresabhi 4373efd0cd feat: move images to blitzkit 2025-04-05 16:00:52 -05:00
tresabhi f13165e77d feat: better ui 2025-04-05 15:09:15 -05:00
tresabhi 8a5b0c1170 fix: gun-specific shell discrimination 2025-04-05 15:09:12 -05:00
tresabhi f16511a72e Merge branch 'main' into dev 2025-04-05 14:57:41 -05:00
tresabhi df0dc4f118 feat: shell filters 2025-04-03 16:33:48 -05:00
tresabhi 92399de5e2 fix: navbar interact 2025-04-03 13:00:23 -05:00
tresabhi 8d9dff8d73 feat: inline 2025-04-03 12:53:54 -05:00
tresabhi f91744f2f0 feat: compressor again 2025-04-03 12:51:24 -05:00
tresabhi e946dfdb69 feat: compressor 2025-04-03 12:48:17 -05:00
tresabhi 6283ddc57a feat: astro seo 2025-04-03 12:45:33 -05:00
tresabhi 2d66e0ab28 fix: missing padding backgrond 2025-04-03 12:34:02 -05:00
tresabhi c3d8f15a2a feat: better robots.txt 2025-04-03 12:23:53 -05:00
tresabhi 21a37ec6cc feat: new nav bar 2025-04-03 12:15:08 -05:00
tresabhi de471400ad feat: partytown 2025-04-03 11:34:57 -05:00
tresabhi 0b16295853 fix: keys 2025-04-03 11:31:31 -05:00
TrèsAbhi 63eea81856 Update stitches.config.ts 2025-04-02 21:55:08 -05:00
tresabhi ba57d61af5 feat: skeleton 2025-04-02 17:59:23 -05:00
tresabhi 3d940f4b47 feat: discord plug 2025-04-02 17:48:51 -05:00
tresabhi 8ef355c574 feat: we don't talk about it 2025-04-02 16:11:09 -05:00
tresabhi 71e1ab1362 aaaaaaaaaaaaaaaa 2025-04-01 19:38:58 -05:00
tresabhi 53dc108356 Merge branch 'main' of https://github.com/tresabhi/blitzkit 2025-04-01 19:38:01 -05:00
tresabhi 57b3218c5a undo cue4 2025-04-01 19:38:00 -05:00
tresabhi 7472e39faf fix: gifs 2025-04-01 19:34:03 -05:00
tresabhi 034417d19f fix: remove cue4 2025-03-31 17:49:23 -05:00
tresabhi 0368e9ceda feat: pink bot 2025-03-31 17:44:08 -05:00
tresabhi 28f365f762 fix: no class list 2025-03-31 17:39:42 -05:00
tresabhi 40a81f6b1b fix: class list 2025-03-31 17:39:01 -05:00
tresabhi 9475d22472 feat: uh hahaha you'll see ... again! 2025-03-31 17:36:40 -05:00
tresabhi 9538a03afb feat: uh hahaha you'll see 2025-03-31 16:54:32 -05:00
tresabhi d5d171bea6 4 2025-03-30 14:37:40 -05:00
tresabhi a9c9a90ac1 fix: disable /auction 2025-03-30 12:53:14 -05:00
tresabhi f7c562f8b6 fix: detect invalid 0-length translations 2025-03-30 12:51:34 -05:00
TrèsAbhi 836cf69f01 New Crowdin updates (#149)
* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Spanish)

* New translations en.json (Chinese Simplified)
2025-03-26 14:37:29 -05:00
tresabhi ba17aae706 feat: bump int size 2025-03-25 15:29:41 -05:00
TrèsAbhi 30ca5c053a Update tankNames.ts 2025-03-14 12:14:55 -05:00
tresabhi c36419f5b6 fix: don't include cue4 2025-03-11 10:40:02 -05:00
tresabhi 1879895db2 feat: add 124 support 2025-03-11 10:13:02 -05:00
tresabhi c964e3c8ec feat: google crawler 2025-03-03 17:49:45 -06:00
tresabhi 2ef6d18f82 lol why cue4??? 2025-03-03 16:19:43 -06:00
tresabhi 10b672572b feat: try appending dvpl? 2025-03-03 16:15:58 -06:00
TrèsAbhi 2a4efa8cd2 fix: check for changes for real 2025-03-03 12:29:25 -06:00
TrèsAbhi 5a8435da29 Update Footer.astro 2025-03-03 12:15:05 -06:00
TrèsAbhi 3461349a96 Update Footer.astro 2025-03-03 12:13:06 -06:00
TrèsAbhi 3791e9df12 New Crowdin updates (#147)
* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (French)

* New translations en.json (French)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Russian)
2025-03-03 11:10:15 -06:00
TrèsAbhi b3c36aac93 New Crowdin updates (#146)
* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Spanish)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Polish)

* New translations en.json (Polish)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Chinese Simplified)

* New translations en.json (Japanese)

* New translations en.json (Japanese)

* New translations en.json (Ukrainian)

* New translations en.json (Polish)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (French)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations en.json (Ukrainian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Chinese Simplified)
2025-02-03 11:07:41 -06:00
TrèsAbhi c0895409bc Update index.ts 2025-01-24 11:27:45 -06:00
TrèsAbhi 438b565cbb New Crowdin updates (#145) 2025-01-19 13:00:15 -06:00
tresabhi cf1a82e89b fix: remove default 2025-01-19 12:51:26 -06:00
tresabhi 0506e361b2 feat: more prune 2025-01-19 12:46:57 -06:00
tresabhi d6a1357a82 Merge branch 'main' into dev 2025-01-19 12:41:06 -06:00
tresabhi a5a1dbd51b fix: [Oo] for /debug 2025-01-18 14:16:49 -06:00
tresabhi 5201350e44 fix: [Oo] for /stats 2025-01-18 14:16:26 -06:00
tresabhi 6a84b9d5ae fix: stricter pattern 2025-01-18 14:07:47 -06:00
tresabhi cf04fe21b3 fix: logs 2025-01-18 13:59:14 -06:00
tresabhi 84eb3c2da3 fix: version 2025-01-18 13:49:37 -06:00
tresabhi 22bf2dc1d8 feat: logs 2025-01-18 13:49:18 -06:00
tresabhi b9c339daa7 fix: search keys 2025-01-18 13:47:31 -06:00
tresabhi ef3006cac1 fix: double full stops 2025-01-18 13:42:52 -06:00
TrèsAbhi e482fd40c1 Update applyPatches.ts 2025-01-18 11:41:45 -06:00
TrèsAbhi cc4569966e Update applyPatches.ts 2025-01-18 09:19:38 -06:00
tresabhi 3708a3ced7 fix: handle deleted tanks 2025-01-17 20:29:33 -06:00
tresabhi 0ae59c2fd7 fix: check dir exists 2025-01-17 20:15:09 -06:00
tresabhi 7027f63236 feat: prune 2025-01-17 19:56:46 -06:00
TrèsAbhi 6725e79021 New Crowdin updates (#140) 2025-01-17 19:36:39 -06:00
tresabhi 5ca3cee9da feat: verbose 2025-01-17 19:22:02 -06:00
tresabhi 602a5a1e99 fix: move env 2025-01-17 19:19:45 -06:00
tresabhi bfbf3bdc39 fix: no double dvpl 2025-01-17 19:17:39 -06:00
tresabhi 644686582e fix: icon api 2025-01-17 19:06:07 -06:00
TrèsAbhi 1c00bc81f8 New Crowdin updates (#139)
* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Japanese)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Portuguese)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Russian)

* New translations en.json (Chinese Simplified)
2025-01-17 12:46:05 -06:00
TrèsAbhi ac3b69bec5 Update assets.yml 2025-01-17 12:45:12 -06:00
TrèsAbhi 3e14c943b6 Update assets.yml 2025-01-16 23:05:13 -06:00
TrèsAbhi d33633be2c Update assets.yml 2025-01-16 22:15:24 -06:00
TrèsAbhi 4bf70d02d0 Update Crowdin configuration file 2025-01-16 20:48:29 -06:00
tresabhi 377bcf0e38 fix: env 2025-01-16 20:44:55 -06:00
tresabhi 884b6d4318 fix: run tests even on dev 2025-01-16 20:36:49 -06:00
303 changed files with 16461 additions and 6224 deletions

No files matched your search

+16 -1
View File
@@ -39,6 +39,18 @@ env:
PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }}
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ github.event.inputs.cdn_branch || secrets.PUBLIC_ASSET_BRANCH }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: true
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
PUBLIC_PATREON_CLIENT_ID: ${{ secrets.PUBLIC_PATREON_CLIENT_ID }}
PUBLIC_PATREON_REDIRECT_URI: ${{ secrets.PUBLIC_PATREON_REDIRECT_URI }}
PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }}
PUBLIC_DEBUG_MISSING_I18N: false
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
jobs:
download_client:
@@ -106,6 +118,9 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_TOKEN }}
- name: Setup bun
uses: oven-sh/setup-bun@v1
@@ -127,7 +142,7 @@ jobs:
- name: Start server and build assets
run: |
bun dev:website &
bun dev:website --verbosepa &
SERVER_PID=$!
bun run build:assets --all-targets --depot --production
+5 -2
View File
@@ -2,7 +2,6 @@ name: Website
on:
push:
branches: main
workflow_dispatch:
schedule:
@@ -22,11 +21,14 @@ env:
PUBLIC_ASSET_REPO: ${{ secrets.PUBLIC_ASSET_REPO }}
PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }}
PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }}
ALLOW_ROBOTS: true
ALLOW_ROBOTS: "true"
WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }}
PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }}
PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }}
GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }}
WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }}
PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }}
jobs:
build:
@@ -53,6 +55,7 @@ jobs:
path: dist/website
deploy-github-pages:
if: ${{ github.ref == 'refs/heads/main' }}
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
needs: build
+2
View File
@@ -27,3 +27,5 @@ target
/.vercel
/.astro
.vercel
/src
/packages/bot/src/prisma
+6
View File
@@ -1,3 +1,9 @@
[submodule "submodules/blitzkit-closed"]
path = submodules/blitzkit-closed
url = https://github.com/tresabhi/blitzkit-closed
[submodule "submodules/CUE4Parse"]
path = submodules/CUE4Parse
url = https://github.com/FabianFG/CUE4Parse
[submodule "submodules/UAssetAPI"]
path = submodules/UAssetAPI
url = https://github.com/atenfyr/UAssetAPI
+8
View File
@@ -14,6 +14,7 @@
"applypatches",
"artefacts",
"astrojs",
"astrolib",
"atgm",
"Autoloader",
"Autoreloader",
@@ -35,6 +36,7 @@
"cacheable",
"Callouts",
"Camo",
"Carro",
"CCPAOPTOUT",
"CDON",
"classname",
@@ -42,6 +44,7 @@
"closedkrieg",
"cockroackdb",
"Coef",
"Combattimento",
"cooldown",
"corelations",
"corsproxy",
@@ -53,6 +56,7 @@
"dbid",
"Ddpf",
"Ddsd",
"deadrail",
"debugflags",
"deburr",
"decalmask",
@@ -130,9 +134,12 @@
"outdir",
"outfile",
"ownedtanks",
"padder",
"partytown",
"PIXELFORMAT",
"playerachievements",
"playerinfo",
"playform",
"pngjs",
"POSITIVEX",
"POSITIVEY",
@@ -181,6 +188,7 @@
"TEXCOORD",
"tresabhi",
"tungstentip",
"Turretless",
"TYPELESS",
"ubjson",
"Unicum",
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,3 +1,3 @@
files:
- source: /packages/core/lang/en.json
- source: /packages/i18n/strings/en.json
translation: /%original_path%/%locale%.json
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.1
## Fixes
- Fixed a crash that occurs if tanks are deleted in Blitz and were once available in BlitzKit
- All tank icons are now back
- The tank search once again returns results
+23
View File
@@ -0,0 +1,23 @@
# BlitzKit 1.27.10
This update changes the URLs across the whole website, removing the "tools" prefix and simplifying other aspects too.
## Changes
- Removed "full" tank names
- For example, tanks like the "Carro 45t" had full names like "Carro Combattimento 45t" which remained pretty much unused, so they have been removed
- Reduced the shadow below the navigation bar
- Removed the "tools" prefix from the URLs; here are the paths:
- `/tools/tankopedia` -> `/tanks`
- `/tools/tankopedia/12345` -> `/tanks/tank-name`
- `/tools/compare` -> `/compare`
- `/tools/embed` -> `/embed`
- `/tools/embed/embed-name` -> `/embed/embed-name`
- `/tools/gallery` -> `/gallery`
- `/tools/performance` -> `/performance`
- `/tools/session` -> `/session`
- `/tools/tier-list` -> `/tier-list`
## Technical Features
- The asset mangler now uses http headers and even more heuristics to minimize re-creating unchanged assets
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.11
This update affects both the bot and the website.
## Technical Changes
- Added a proper CDN domain to avoid error 429s and any slowdowns
+7
View File
@@ -0,0 +1,7 @@
# BlitzKit 1.27.2
This update only affects the website.
## Fixes
- The new Improved Vertical Stabilizer equipment which only affects the gun depression found on the Type 63 now works
+9
View File
@@ -0,0 +1,9 @@
# BlitzKit 1.27.3
This update only affects the bot.
## Fixes
- Added support for 64-bit IDs allowing users with new accounts to link with BlitzKit
- This comes at the added cost of resetting the links of all other accounts
- Just re-link and you're good to go
+3
View File
@@ -0,0 +1,3 @@
# FreakKit 1.27.4
It has come to our attention that tanks are very harsh objects. To even it out, we made the website a pretty pretty princess <3
+11
View File
@@ -0,0 +1,11 @@
# BlitzKit 1.27.5
This update only affects the bot.
## Changes
- Removed `/auction`
## Fixes
- Fixed a bug that caused `/rating` to disappear
+23
View File
@@ -0,0 +1,23 @@
# BlitzKit 1.27.6
This update only affects the website.
## Features
- Added a promotional card for [the official Discord server](https://discord.gg/nDt7AjGJQH) to the homepage
- Added a better navbar
- The added spaces and padding helps add more room for error when clicking, especially on mobile
- The dropdown menu is now a lost smaller, disposing of the need to scroll at all
- Added shell filters to the tank search
- Reworked the filter buttons and their positions to allow for more space
## Fixes
- Moved most images from imgur to BlitzKit allowing for users in certain countries to load BlitzKit without getting blocked
## Technical Features
- Added Partytown to offload some workload off the main thread, allowing for a more responsive experience
- Reworked `robots.txt` to disallow indexing of BlitzKit variants
- Improved the SEO further for most pages
- Added a few compressor library to speed up load times
+25
View File
@@ -0,0 +1,25 @@
# BlitzKit 1.27.7
![](https://i.imgur.com/qFepOaT.png)
This update only affects the website.
## Features
- Added a new foggy-wireframe transition between tabs to smooth out the jarring jumps
- Added animations pop-in and out animations on a few elements
- The tank name title now shrinks in size as soon as a disturbance is caused
- The shell selector, antagonist switcher, and thicknesses range now ease into frame
- The Tankopedia search page now loads as a skeleton by default before populating with tanks, allowing for the recently viewed tanks to load without cumulative shift
- Something similar was done to the Gallery tool
- Gave the meta section of the website some zest with nation-based backdrops
- The compare tabs in the Tankopedia are now centered
## Fixes
- The camera no longer snaps to the initial position when switching between tabs
## Technical Changes
- Backported some technologies from BlitzKit Reforged; you will not notice any differences in usage
- The popular tanks and gallery indexings have been moved to the asynchronous cron job
+12
View File
@@ -0,0 +1,12 @@
# BlitzKit 1.27.8
![](https://i.imgur.com/WO5MS94.png)
## Features
- The website now uses Inter as the font
- Added a cool new non-disturbed state for the Tankopedia because why not
## Changes
- Removed the Discord plug and language changer from the homepage
+27
View File
@@ -0,0 +1,27 @@
# BlitzKit 1.27.9
BlitzKit's great, but there's far too many numbers. This update adds a few visualizers to help new players understand how things relate numerically.
![](https://i.imgur.com/OV5ak4T.png)
## Features
- Added the turret and hull traverse speed visualizer
- Added the shell ricochet and normalization angles visualizer
- Added the view-range visualizer
- Added a search bar to the Tankopedia characteristics
- Bumped the decimal places for vehicle dimensions and volume
- Made tank ranks softer (the bars beneath the tank characteristics)
- Gave the navigation bar better distinction from the rest of the page
- Increased the number of characteristics rows to 3 on select devices
- Switched to a more contrasting classic style of components across the website (it's subtle, but beautiful)
## Changes
- Removed units
## Fixes
- Clarified a few confusing row labels in the Compare tool
- Made the Tankopedia hero section dynamically smaller on mobile phones to allow scrolling
- Tank ranks no longer reach 0 or go above the number of vehicles in the comparison
+11 -8
View File
@@ -1,7 +1,7 @@
{
"name": "blitzkit",
"private": true,
"version": "1.27.0",
"version": "1.27.11",
"description": "🎉 Tools for everything World of Tanks Blitz",
"type": "module",
"types": "./types.d.ts",
@@ -35,19 +35,22 @@
"build:protos": "bun packages/scripts/src/protos.ts",
"preview:website": "cd packages/website && bun run preview",
"build:reviews": "cd packages/scripts && bun build:reviews",
"build:ids": "cd packages/scripts && bun build:ids"
"build:ids": "cd packages/scripts && bun build:ids",
"prunestrings": "cd packages/scripts && bun prunestrings"
},
"license": "MIT",
"dependencies": {
"sharp": "^0.33.5",
"lodash-es": "^4.17.21"
"googleapis": "^144.0.0",
"lodash-es": "^4.17.21",
"quicklime": "^1.2.0",
"sharp": "^0.33.5"
},
"devDependencies": {
"@types/bun": "latest",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.4",
"prettier": "^3.3.3",
"prisma": "^6.0.0",
"typescript": "^5.5.4"
"@types/node": "^22.14.1",
"prettier": "^3.5.3",
"prisma": "^6.6.0",
"typescript": "^5.8.3"
}
}
+2 -2
View File
@@ -107,8 +107,8 @@ export const auctionCommand = new Promise<CommandRegistry>((resolve) => {
if (!tank) return '';
const name = `[${tank.name}](<https://blitzkit.app/tools/tankopedia/${
tank.id
const name = `[${tank.name}](<https://blitzkit.app/tanks/${
tank.slug
}>)`;
const next =
data.next_price === null
+1 -1
View File
@@ -17,7 +17,7 @@ export const debugCommand = new Promise<CommandRegistry>((resolve) => {
const list = [
[strings.bot.commands.debug.body.version, packageJSON.version],
[
strings.bot.commands.debug.body.shard,
strings.bot.commands.debug.body.shard.label,
client.shard?.ids[0] ?? strings.bot.commands.debug.body.shard.default,
],
[
+2 -5
View File
@@ -1,11 +1,8 @@
import {
getAccountInfo,
getClanAccountInfo,
linkBlitzAndDiscord,
} from '@blitzkit/core';
import { getAccountInfo, getClanAccountInfo } from '@blitzkit/core';
import { teal } from '@radix-ui/colors';
import { Glow } from '../components/AllStatsOverview/components/HeroStat/components/Glow';
import { CommandWrapper } from '../components/CommandWrapper';
import { linkBlitzAndDiscord } from '../core/db/linkBlitzAndDiscord';
import { addUsernameChoices } from '../core/discord/addUsernameChoices';
import { autocompleteUsername } from '../core/discord/autocompleteUsername';
import { createLocalizedCommand } from '../core/discord/createLocalizedCommand';
+1 -1
View File
@@ -78,7 +78,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
)
.map(
(tank) =>
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tools/tankopedia/${tank.id}>)`,
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
);
const lines = [title, ...tankList];
@@ -16,8 +16,8 @@ export function CommandWrapper({ children, fat }: CommandWrapperProps) {
gap: 16,
width: '100%',
color: theme.colors.textHighContrast,
backgroundColor: theme.colors.appBackground1,
position: 'relative',
background: theme.colors.appBackground1,
}}
>
{children}
+2 -4
View File
@@ -1,3 +1,4 @@
import { SEARCH_KEYS } from '@blitzkit/core';
import { literals } from '@blitzkit/i18n';
import { Locale } from 'discord.js';
import { go } from 'fuzzysort';
@@ -19,10 +20,7 @@ export async function resolveTankId(
const searchResult = go(
`${tank}`,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: ['searchableName', 'searchableNameDeburr', 'camouflages'],
limit: 1,
},
{ keys: SEARCH_KEYS, limit: 1 },
);
if (searchResult.length === 0) {
@@ -8,12 +8,12 @@ export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale,
) {
const { strings } = translator(locale);
const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions();
const info: string[] = [];
// TODO: somehow localize tank names?
if (tank) return awaitedTankDefinitions.tanks[tank].name;
if (tank) return awaitedTankDefinitions.tanks[tank].name.locales[bkLocale];
if (nation)
info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) {
@@ -1,5 +1,6 @@
import { idToRegion, usersDatabase } from '@blitzkit/core';
import { flagUserActivity } from '@blitzkit/core/src/blitzkit/flagUserActivity';
import { idToRegion } from '@blitzkit/core';
import { flagUserActivity } from '../db/flagUserActivity';
import { usersDatabase } from '../db/users';
export async function getBlitzFromDiscord(discordId: bigint) {
const unique = await usersDatabase.discordUser.findUnique({
@@ -8,6 +9,8 @@ export async function getBlitzFromDiscord(discordId: bigint) {
if (unique === null) return null;
await flagUserActivity(unique.blitz_id);
return { id: unique.blitz_id, region: idToRegion(unique.blitz_id) };
const id = Number(unique.blitz_id);
await flagUserActivity(id);
return { id, region: idToRegion(id) };
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { usersDatabase } from '@blitzkit/core';
import { usersDatabase } from '../db/users';
import { client } from '../discord/client';
const EXIT_EVENTS = [
@@ -1,4 +1,4 @@
import { usersDatabase } from '@blitzkit/core';
import { usersDatabase } from './users';
export async function flagUserActivity(blitzId: number) {
await usersDatabase.user.upsert({
@@ -1,5 +1,5 @@
import { usersDatabase } from '@blitzkit/core';
import { flagUserActivity } from './flagUserActivity';
import { usersDatabase } from './users';
export async function linkBlitzAndDiscord(discordId: bigint, blitzId: number) {
await usersDatabase.discordUser.upsert({
@@ -1,3 +1,3 @@
import { PrismaClient } from '@prisma/client';
import { PrismaClient } from '../../prisma';
export const usersDatabase = new PrismaClient();
@@ -1,5 +1,4 @@
import { fetchTankNames, TankType } from '@blitzkit/core';
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
import { fetchTankNames, SEARCH_KEYS, TankType } from '@blitzkit/core';
import {
ApplicationCommandOptionChoiceData,
AutocompleteInteraction,
@@ -32,16 +31,7 @@ export async function autocompleteTanks(
go(
focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{
keys: [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.{locale}`,
`searchableNameDeburr.{locale}`,
]).flat(),
'camouflages',
],
limit: 10,
},
{ keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => {
let name = unwrap(item.obj.searchableName);
@@ -3,7 +3,8 @@ import { Locale } from 'discord.js';
import { SUPPORTED_LOCALES_DISCORD } from '../localization/strings/constants';
import { translator } from '../localization/translator';
const validNameRegex = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
const validNameRegex =
/^[\p{Ll}\p{Lm}\p{Lo}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u;
export function localizationObject(
path: (strings: BlitzKitStrings) => string,
@@ -14,7 +15,8 @@ export function localizationObject(
(localizations, locale) => {
const { strings } = translator(locale);
const translation = literalsFunc(path(strings), literals);
const isValid = !strict || validNameRegex.test(translation);
const isValid =
(!strict || validNameRegex.test(translation)) && translation.length > 0;
if (!isValid) {
console.warn(
@@ -12,8 +12,9 @@ export function translator(localeRaw: Locale) {
SUPPORTED_LOCALES_DISCORD.includes(localeRaw)
? localeRaw
: DEFAULT_LOCALE_DISCORD;
const strings = STRINGS[SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]];
const unwrap = unwrapper(SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale]);
const bkLocale = SUPPORTED_LOCALES_DISCORD_MAP_INVERSE[locale];
const strings = STRINGS[bkLocale];
const unwrap = unwrapper(bkLocale);
return { locale, strings, unwrap };
return { locale, strings, unwrap, bkLocale };
}
@@ -19,11 +19,11 @@ import { Writeable } from '../../../types/writable';
export async function handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
await interaction.deferReply();
const awaitedCommands = await commands;
const registry = awaitedCommands[interaction.commandName];
await interaction.deferReply();
try {
const returnable = await registry.handler(interaction);
@@ -15,7 +15,6 @@ import {
SlashCommandSubcommandsOnlyBuilder,
} from 'discord.js';
import { aboutCommand } from '../../commands/about';
import { auctionCommand } from '../../commands/auction';
import { breakdownCommand } from '../../commands/breakdown';
import { debugCommand } from '../../commands/debug';
import { evolutionCommand } from '../../commands/evolution';
@@ -80,7 +79,7 @@ interface CommandRegistryExplicit extends CommandRegistryBase {
export type CommandRegistry = CommandRegistryImplicit | CommandRegistryExplicit;
export const COMMANDS_RAW: Promise<CommandRegistry>[] = [
auctionCommand,
// auctionCommand,
permissionsCommand,
debugCommand,
aboutCommand,
+8 -1
View File
@@ -45,7 +45,7 @@ const interval = setInterval(async () => {
const usernames = awaitedDiscoveredIdsDefinitions.count;
manager.broadcastEval(
(client, { servers, users, channels, tanks, usernames }) => {
async (client, { servers, users, channels, tanks, usernames }) => {
const bios = [
`Living in ${servers.toLocaleString()} servers`,
`Serving ${users.toLocaleString()} users`,
@@ -62,6 +62,13 @@ const interval = setInterval(async () => {
},
],
});
for (const guild of client.guilds.cache.values()) {
try {
const botMember = await guild.members.fetch(client.user.id);
await botMember.setNickname('BlitzKit');
} catch {}
}
},
{ context: { servers, channels, users, tanks, usernames } },
);
+1 -1
View File
@@ -23,7 +23,7 @@ export const theme = {
spaces: createSpaces(),
colors: {
...createColors(slateDark),
...createColors(mauveDark),
...createColors(slateDarkA, 'alpha'),
...createColors(slate, 'light'),
+1 -1
View File
@@ -6,7 +6,7 @@
"dependencies": {
"@octokit/plugin-throttling": "^8.2.0",
"@octokit/rest": "^20.1.1",
"@prisma/client": "^5.19.1",
"@prisma/client": "^6.6.0",
"adm-zip": "^0.5.16",
"crc": "^4.3.2",
"dotenv": "^16.4.5",
@@ -1,4 +1,5 @@
import { assertSecret, type BlitzGlossary } from '@blitzkit/core';
import { assertSecret } from '../blitzkit';
import { BlitzGlossary } from '../types';
export async function fetchGlossary(locale: string) {
const response = await fetch(
+1
View File
@@ -4,6 +4,7 @@ export * from './classes';
export * from './constants';
export * from './equipmentPrice';
export * from './fetchBlitz';
export * from './fetchGlossary';
export * from './generateStats';
export * from './getAccountAchievements';
export * from './getAccountInfo';
+8 -3
View File
@@ -1,7 +1,12 @@
import { assertSecret } from './assertSecret';
const repo = assertSecret(import.meta.env.PUBLIC_ASSET_REPO);
const branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
const fastUser = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_USER);
const fastRepo = assertSecret(import.meta.env.PUBLIC_ASSET_FAST_REPO);
export function asset(path: string) {
return `https://raw.githubusercontent.com/${assertSecret(
import.meta.env.PUBLIC_ASSET_REPO,
)}/${assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH)}/${path}`;
return branch === 'main'
? `https://${fastUser}.github.io/${fastRepo}/${path}`
: `https://raw.githubusercontent.com/${repo}/${branch}/${path}`;
}
+7
View File
@@ -0,0 +1,7 @@
import { fetchPB } from '../protobuf';
import { Gallery } from '../protos';
import { asset } from './asset';
export function fetchGallery() {
return fetchPB(asset('definitions/gallery.pb'), Gallery);
}
+2 -2
View File
@@ -13,7 +13,6 @@ export * from './discoveredIdDefinitions';
export * from './emblemURL';
export * from './equipmentDefinitions';
export * from './eventManager';
// export * from './flagUserActivity';
export * from './gameDefinitions';
export * from './getArchivedLatestSeasonNumber';
// export * from './getArchivedRatingInfo';
@@ -21,7 +20,6 @@ export * from './getArchivedRatingLeaderboard';
// export * from './getArchivedRatingMidnightLeaderboard';
export * from './getTimeDaysAgo';
export * from './isExplosive';
export * from './linkBlitzAndDiscord';
export * from './mapDefinitions';
export * from './modelDefinitions';
export * from './normalizeBoundingBox';
@@ -44,6 +42,8 @@ export * from './unwrapBlitzkitResponse';
export * from './votes';
// export * from './withCORSProxy';
export * from './createDefaultProvisions';
export * from './gallery';
export * from './metaSortTank';
export * from './popularTanks';
export * from './romanize';
export * from './youtubers';
@@ -0,0 +1,7 @@
import { fetchPB } from '../protobuf';
import { PopularTanks } from '../protos';
import { asset } from './asset';
export function fetchPopularTanks() {
return fetchPB(asset('definitions/popular-tanks.pb'), PopularTanks);
}
+19 -4
View File
@@ -1,4 +1,5 @@
import { deburr } from 'lodash-es';
import { SUPPORTED_LOCALES } from '../../../i18n/src/strings';
import { I18nString } from '../protos';
import { fetchCamouflageDefinitions } from './camouflageDefinitions';
import { fetchTankDefinitions } from './tankDefinitions';
@@ -14,22 +15,36 @@ export async function fetchTankNames() {
tankDefinitionsArray.map(async (tank) => {
const searchableNameDeburr: I18nString = { locales: {} };
Object.entries(tank.name_full ?? tank.name).forEach(([key, value]) => {
Object.entries(tank.name).forEach(([key, value]) => {
searchableNameDeburr.locales[key] = deburr(value);
});
return {
id: tank.id,
name: tank.name,
nameFull: tank.name_full,
searchableName: tank.name_full ?? tank.name,
searchableName: tank.name,
searchableNameDeburr,
camouflages: tank.camouflages
?.map((id) => camouflageDefinitions.camouflages[id]?.name)
?.map((id) =>
SUPPORTED_LOCALES.map(
(locale) =>
camouflageDefinitions.camouflages[id]?.name.locales[locale],
),
)
.flat()
.filter(Boolean)
.map(deburr)
.join(' '),
treeType: tank.type,
};
}),
);
}
export const SEARCH_KEYS = [
...SUPPORTED_LOCALES.map((locale) => [
`searchableName.locales.${locale}`,
`searchableNameDeburr.locales.${locale}`,
]).flat(),
'camouflages',
];
-1
View File
@@ -1 +0,0 @@
export * from './users';
-1
View File
@@ -1,6 +1,5 @@
export * from './blitz';
export * from './blitzkit';
export * from './databases';
export * from './imgur';
export * from './math';
export * from './patreon';
+15
View File
@@ -0,0 +1,15 @@
syntax = "proto2";
package blitzkit;
import "packages/core/src/protos/i18n.proto";
message Gallery {
repeated Avatar avatars = 1;
}
message Avatar {
required string id = 1;
required I18nString name = 2;
required string extension = 3;
}
+237
View File
@@ -0,0 +1,237 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.2.2
// protoc v5.28.2
// source: packages/core/src/protos/gallery.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire';
import { I18nString, createBaseI18nString } from './i18n';
export const protobufPackage = 'blitzkit';
export interface Gallery {
avatars: Avatar[];
}
export interface Avatar {
id: string;
name: I18nString;
extension: string;
}
function createBaseGallery(): Gallery {
return { avatars: [] };
}
export const Gallery: MessageFns<Gallery> = {
encode(
message: Gallery,
writer: BinaryWriter = new BinaryWriter(),
): BinaryWriter {
for (const v of message.avatars) {
Avatar.encode(v!, writer.uint32(10).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Gallery {
const reader =
input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGallery();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.avatars.push(Avatar.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Gallery {
return {
avatars: globalThis.Array.isArray(object?.avatars)
? object.avatars.map((e: any) => Avatar.fromJSON(e))
: [],
};
},
toJSON(message: Gallery): unknown {
const obj: any = {};
if (message.avatars?.length) {
obj.avatars = message.avatars.map((e) => Avatar.toJSON(e));
}
return obj;
},
create<I extends Exact<DeepPartial<Gallery>, I>>(base?: I): Gallery {
return Gallery.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<Gallery>, I>>(object: I): Gallery {
const message = createBaseGallery();
message.avatars = object.avatars?.map((e) => Avatar.fromPartial(e)) || [];
return message;
},
};
function createBaseAvatar(): Avatar {
return { id: '', name: createBaseI18nString(), extension: '' };
}
export const Avatar: MessageFns<Avatar> = {
encode(
message: Avatar,
writer: BinaryWriter = new BinaryWriter(),
): BinaryWriter {
if (message.id !== '') {
writer.uint32(10).string(message.id);
}
if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join();
}
if (message.extension !== '') {
writer.uint32(26).string(message.extension);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Avatar {
const reader =
input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAvatar();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.id = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.name = I18nString.decode(reader, reader.uint32());
continue;
}
case 3: {
if (tag !== 26) {
break;
}
message.extension = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Avatar {
return {
id: globalThis.String(assertSet('Avatar.id', object.id)),
name: I18nString.fromJSON(assertSet('Avatar.name', object.name)),
extension: globalThis.String(
assertSet('Avatar.extension', object.extension),
),
};
},
toJSON(message: Avatar): unknown {
const obj: any = {};
if (message.id !== '') {
obj.id = message.id;
}
if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name);
}
if (message.extension !== '') {
obj.extension = message.extension;
}
return obj;
},
create<I extends Exact<DeepPartial<Avatar>, I>>(base?: I): Avatar {
return Avatar.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<Avatar>, I>>(object: I): Avatar {
const message = createBaseAvatar();
message.id = object.id ?? '';
message.name =
object.name !== undefined && object.name !== null
? I18nString.fromPartial(object.name)
: createBaseI18nString();
message.extension = object.extension ?? '';
return message;
},
};
type Builtin =
| Date
| Function
| Uint8Array
| string
| number
| boolean
| undefined;
export type DeepPartial<T> = T extends Builtin
? T
: T extends globalThis.Array<infer U>
? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: T extends { $case: string; value: unknown }
? { $case: T['$case']; value?: DeepPartial<T['value']> }
: T extends {}
? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin
? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
[K in Exclude<keyof I, KeysOfUnion<P>>]: never;
};
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}
+2
View File
@@ -3,10 +3,12 @@ export * from './average_definitions.ts';
export * from './camouflage_definitions.ts';
export * from './consumable_definitions.ts';
export * from './equipment_definitions.ts';
export * from './gallery.ts';
export * from './game_definitions.ts';
export * from './i18n.ts';
export * from './map_definitions.ts';
export * from './model_definitions.ts';
export * from './popular_tanks.ts';
export * from './provision_definitions.ts';
export * from './rating_leaderboard.ts';
export * from './reviews.ts';
@@ -0,0 +1,12 @@
syntax = "proto2";
package blitzkit;
message PopularTanks {
repeated PopularTank tanks = 1;
}
message PopularTank {
required uint32 id = 1;
required uint32 views = 2; // views in the last 7 days
}
+190
View File
@@ -0,0 +1,190 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.2.2
// protoc v5.28.2
// source: packages/core/src/protos/popular_tanks.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "blitzkit";
export interface PopularTanks {
tanks: PopularTank[];
}
export interface PopularTank {
id: number;
/** views in the last 7 days */
views: number;
}
function createBasePopularTanks(): PopularTanks {
return { tanks: [] };
}
export const PopularTanks: MessageFns<PopularTanks> = {
encode(message: PopularTanks, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
for (const v of message.tanks) {
PopularTank.encode(v!, writer.uint32(10).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): PopularTanks {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePopularTanks();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.tanks.push(PopularTank.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): PopularTanks {
return {
tanks: globalThis.Array.isArray(object?.tanks) ? object.tanks.map((e: any) => PopularTank.fromJSON(e)) : [],
};
},
toJSON(message: PopularTanks): unknown {
const obj: any = {};
if (message.tanks?.length) {
obj.tanks = message.tanks.map((e) => PopularTank.toJSON(e));
}
return obj;
},
create<I extends Exact<DeepPartial<PopularTanks>, I>>(base?: I): PopularTanks {
return PopularTanks.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<PopularTanks>, I>>(object: I): PopularTanks {
const message = createBasePopularTanks();
message.tanks = object.tanks?.map((e) => PopularTank.fromPartial(e)) || [];
return message;
},
};
function createBasePopularTank(): PopularTank {
return { id: 0, views: 0 };
}
export const PopularTank: MessageFns<PopularTank> = {
encode(message: PopularTank, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.id !== 0) {
writer.uint32(8).uint32(message.id);
}
if (message.views !== 0) {
writer.uint32(16).uint32(message.views);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): PopularTank {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePopularTank();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 8) {
break;
}
message.id = reader.uint32();
continue;
}
case 2: {
if (tag !== 16) {
break;
}
message.views = reader.uint32();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): PopularTank {
return {
id: globalThis.Number(assertSet("PopularTank.id", object.id)),
views: globalThis.Number(assertSet("PopularTank.views", object.views)),
};
},
toJSON(message: PopularTank): unknown {
const obj: any = {};
if (message.id !== 0) {
obj.id = Math.round(message.id);
}
if (message.views !== 0) {
obj.views = Math.round(message.views);
}
return obj;
},
create<I extends Exact<DeepPartial<PopularTank>, I>>(base?: I): PopularTank {
return PopularTank.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<PopularTank>, I>>(object: I): PopularTank {
const message = createBasePopularTank();
message.id = object.id ?? 0;
message.views = object.views ?? 0;
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial<T["value"]> }
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin ? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}
+10 -10
View File
@@ -10,16 +10,16 @@ message TankDefinitions {
message TankDefinition {
required uint32 id = 1;
map<uint32, uint32> roles = 2;
required bool fixed_camouflage = 4;
repeated int32 camouflages = 5;
repeated int32 ancestors = 6;
repeated int32 successors = 7;
repeated Crew crew = 8;
required uint32 health = 9;
required string nation = 10;
required I18nString name = 11;
optional I18nString name_full = 12;
required string slug = 2;
map<uint32, uint32> roles = 3;
required bool fixed_camouflage = 5;
repeated int32 camouflages = 6;
repeated int32 ancestors = 7;
repeated int32 successors = 8;
repeated Crew crew = 9;
required uint32 health = 10;
required string nation = 11;
required I18nString name = 12;
required TankType type = 13;
required uint32 max_consumables = 14;
required uint32 max_provisions = 15;
File diff suppressed because it is too large. Load diff
+14
View File
@@ -0,0 +1,14 @@
{
"default": "en",
"supported": [
{ "locale": "en" },
{ "locale": "es" },
{ "locale": "fr" },
{ "locale": "ja" },
{ "locale": "pl" },
{ "locale": "pt" },
{ "locale": "ru" },
{ "locale": "uk" },
{ "locale": "zh", "blitz": "zh_Hans_SG" }
]
}
+73 -9
View File
@@ -11,7 +11,9 @@
"mm": "%s1mm",
"deg": "%s1°",
"hp": "%s1hp",
"x": "x%s1"
"x": "x%s1",
"percentage": "%s1%",
"deg_s": "%s1°/s"
},
"gun_types": {
@@ -739,6 +741,11 @@
},
"website": {
"redirect": {
"title": "301: moved permanently",
"body": "This page has moved. Redirecting..."
},
"settings": {
"dev_mode": "Development mode",
"language": "Language"
@@ -756,6 +763,7 @@
},
"tank_search": {
"clear_filters": "Clear filters",
"search_bar_hint": "Search tanks...",
"recent": "Recently viewed",
"select_all": "Select all",
@@ -824,6 +832,12 @@
},
"home": {
"discord": {
"join_short": "Join cool people!",
"join_long": "Join super duper cool people!",
"action": "Discord"
},
"hero": {
"credit": "By %s1 ❤️"
},
@@ -838,8 +852,32 @@
"seo_description": "Tools for everything World of Tanks Blitz"
},
"docs": {
"patches": {
"name": "BlitzKit Changelogs",
"seo_description": "Changelogs for BlitzKit Discord bot and website"
}
},
"tools": {
"guess": {
"name": "Guess the tank",
"button": "Guess away",
"description": "Silhouette mini-game",
"seo_description": "Play a tank guessing game with a silhouette in World of Tanks Blitz",
"search": {
"placeholder": "Type to guess...",
"skip": "Skip",
"guess": "Guess",
"next": "Next",
"no_results": "No results"
},
"stats": "%s1 / %s2 guessed correctly"
},
"tankopedia": {
"search": "Search a stat...",
"name": "Tankopedia",
"button": "Find tanks",
"description": "Blitz tank encyclopedia",
@@ -847,6 +885,32 @@
"tank_seo_title": "%s1 - Tier %s2 %s3 %s4 | %s5 Tankopedia | World of Tanks Blitz (WoTB)",
"tank_seo_description": "%s1 armor, statistics, equipment, provisions, and more. %s2 DPM, %s3hp alpha, %s4mm penetration, %s5kph top speed, %s6hp health.",
"visualizers": {
"rotator": {
"hull": "Hull",
"turret": "Turret",
"hard": "Hard",
"medium": "Medium",
"soft": "Soft",
"effective": "Effective"
},
"ricochet": {
"angle": "Angle",
"normalization": "Normalization",
"effective_angle": "Effective angle",
"armor": "Armor",
"no_ricochet": "Explosive shells don't ricochet"
},
"view_range": {
"camo": "Camo",
"percentage": "%s1%",
"ghost_factory": "Ghost Factory",
"dead_rail": "Dead Rail"
}
},
"sandbox": {
"poses": {
"hull_down": "Hull down",
@@ -996,7 +1060,7 @@
"characteristics": {
"title": "Characteristics",
"compare": {
"label": "Compare against",
"label": "Compare",
"class": "Tier %s1 %s2",
"tier": "All of tier %s1",
"all": "All tanks"
@@ -1033,7 +1097,7 @@
"explosionRadius": "Explosion radius",
"shellVelocity": "Shell velocity",
"aimTime": "Aim time",
"dispersion": "Still",
"dispersion": "Dispersion still",
"dispersionMoving": "Moving",
"dispersionHullTraversing": "Hull traversing",
"dispersionTurretTraversing": "Turret traversing",
@@ -1047,28 +1111,28 @@
"gunRearElevation": "Rear elevation",
"azimuthLeft": "Azimuth left",
"azimuthRight": "Azimuth right",
"speedForwards": "Forwards",
"speedForwards": "Speed forwards",
"speedBackwards": "Backwards",
"hardTerrainCoefficient": "Hard",
"hardTerrainCoefficientRaw": "Hard",
"hardTerrainCoefficient": "Terrain coefficient hard",
"hardTerrainCoefficientRaw": "Raw terrain coefficient hard",
"mediumTerrainCoefficient": "Medium",
"mediumTerrainCoefficientRaw": "Medium",
"softTerrainCoefficient": "Soft",
"softTerrainCoefficientRaw": "Soft",
"enginePower": "Engine power",
"powerToWeightRatioHardTerrain": "Hard",
"powerToWeightRatioHardTerrain": "Power to weight ratio hard",
"powerToWeightRatioMediumTerrain": "Medium",
"powerToWeightRatioSoftTerrain": "Soft",
"weight": "Weight",
"turretTraverseSpeed": "Turret traverse speed",
"gunTraverseSpeed": "Gun traverse speed",
"hullTraverseHardTerrain": "Hard",
"hullTraverseHardTerrain": "Hull traverse hard",
"hullTraverseMediumTerrain": "Medium",
"hullTraverseSoftTerrain": "Soft",
"health": "Health",
"fireChance": "Fire chance",
"viewRange": "View range",
"camouflageStill": "Still",
"camouflageStill": "Camouflage still",
"camouflageMoving": "Moving",
"camouflageShootingStill": "Still",
"camouflageShootingMoving": "Moving",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -10,7 +10,8 @@
"build:reviews": "bun src/reviews.ts",
"build:ids": "bun src/discoverIds.ts",
"build:bot": "bun src/buildBot.ts",
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts"
"build:ratingleaderboard": "bun src/buildRatingLeaderboard.ts",
"prunestrings": "bun src/pruneStrings.ts"
},
"dependencies": {
"@blitzkit/core": "workspace:*",
+18 -10
View File
@@ -45,19 +45,27 @@ while (true) {
files.length,
);
await Promise.all(
files.map(async ({ path, data }) => {
const { dir } = parsePath(path);
for (const { path, data } of files) {
console.log(`Patching "${path}"...`);
const { dir } = parsePath(path);
try {
await mkdir(`${DATA}/${dir}`, { recursive: true });
await writeFile(
`${DATA}/${path}`,
new Uint8Array(writeDVPL(Buffer.from(data))),
);
} catch (error) {
console.warn(`Failed to make directory "${dir}"`);
}
bar.tick();
}),
);
const isDvpl = path.endsWith('.dvpl');
const buffer = Buffer.from(data);
await writeFile(
`${DATA}/${path}${isDvpl ? '' : '.dvpl'}`,
new Uint8Array(isDvpl ? buffer : writeDVPL(buffer)),
);
bar.tick();
}
if ('dynamicContentLocalizationsDir' in data) {
console.log('Found dynamic content localizations; patching...');
+114 -9
View File
@@ -37,6 +37,7 @@ import {
SUPPORTED_LOCALES,
} from '@blitzkit/i18n';
import { readdir } from 'fs/promises';
import { deburr } from 'lodash-es';
import { parse as parsePath } from 'path';
import { Vector3Tuple } from 'three';
import { parse as parseYaml } from 'yaml';
@@ -48,6 +49,23 @@ import { DATA } from './constants';
import { Avatar } from './skillIcons';
import { TankParameters } from './tankIcons';
const nonAlphanumericRegex = /[^a-z0-9]/g;
const multipleDashesRegex = /--+/g;
const trailingDashRegex = /-$/g;
const leadingDashRegex = /^-/g;
const nationSlugDiscriminators = {
china: 'cn',
european: 'eu',
france: 'fr',
germany: 'de',
japan: 'jp',
other: 'ot',
uk: 'gb',
usa: 'us',
ussr: 'ru',
};
function parseResearchCost(raw: number | string) {
if (typeof raw === 'number') {
return {
@@ -502,7 +520,7 @@ const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
HIGH_EXPLOSIVE: ShellType.HE,
HOLLOW_CHARGE: ShellType.HEAT,
};
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU(R?)))$/;
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/;
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
chassis: ModuleType.TRACKS,
engine: ModuleType.ENGINE,
@@ -703,6 +721,93 @@ export async function definitions() {
};
});
const slugRequesters = new Map<string, { id: number; key: string }[]>();
const idToNation: Record<number, string> = {};
await Promise.all(
nations.map(async (nation) => {
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tankList.root) {
if (botPattern.test(tankKey)) continue;
const tank = tankList.root[tankKey];
const tankId = toUniqueId(nation, tank.id);
const name = (
(tank.shortUserString
? getString(tank.shortUserString)
: undefined) ?? getString(tank.userString)
).locales.en;
let slug = deburr(name).toLowerCase();
slug = slug.replaceAll(nonAlphanumericRegex, '-');
slug = slug.replaceAll(multipleDashesRegex, '-');
slug = slug.replaceAll(trailingDashRegex, '');
slug = slug.replaceAll(leadingDashRegex, '');
idToNation[tankId] = nation;
if (slugRequesters.has(slug)) {
slugRequesters.get(slug)!.push({ id: tankId, key: tankKey });
} else {
slugRequesters.set(slug, [{ id: tankId, key: tankKey }]);
}
}
}),
);
const slugs = new Map<number, string>();
slugRequesters.forEach((requesters, slug) => {
if (requesters.length === 1) {
slugs.set(requesters[0].id, slug);
return;
}
console.warn(
`Multiple tanks share slug ${slug}: ${requesters.map(({ key }) => key).join(', ')}`,
);
if (requesters.length !== 2) {
throw new Error('Unresolvable number of duplicates :(');
}
const nonCanonical = requesters.find(({ key }) => key.endsWith('TUR'));
if (nonCanonical === undefined) {
console.log('Using nations to discriminate');
// both are non-tutorial tanks, will have to discriminate using nation
requesters.forEach(({ id, key }) => {
const nation = idToNation[id];
const discriminator =
nationSlugDiscriminators[
nation as keyof typeof nationSlugDiscriminators
];
console.log(`Solution: ${key} -> ${slug}-${discriminator}`);
slugs.set(id, `${slug}-${discriminator}`);
});
} else {
console.log('Using tutorial bot suffix to discriminate');
const canonical = requesters.find(({ key }) => !key.endsWith('TUR'));
if (canonical === undefined) {
throw new Error(
'Two tutorial bots share the same slug? The world is truly broken.',
);
}
console.log(`Solution: ${canonical.key} -> ${slug}`);
slugs.set(canonical.id, slug);
console.log(`Solution: ${nonCanonical.key} -> ${slug}-tur`);
slugs.set(nonCanonical.id, `${slug}-tur`);
}
});
await Promise.all(
nations.map(async (nation) => {
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
@@ -787,6 +892,13 @@ export async function definitions() {
const hullArmor: Armor = { spaced: [], thickness: {} };
const equipment = tankDefinition.root.optDevicePreset;
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
const slug = slugs.get(tankId);
if (slug === undefined) {
throw new Error(
`Could not find slug for ${nation}/${tankKey} (${tankId})`,
);
}
if (tank.sellPrice) {
tankPrice = {
@@ -903,7 +1015,7 @@ export async function definitions() {
(tank.shortUserString
? getString(tank.shortUserString)
: undefined) ?? getString(tank.userString),
name_full: getString(tank.userString),
slug,
nation,
type: tankTags.includes('collectible')
? TankType.COLLECTOR
@@ -939,13 +1051,6 @@ export async function definitions() {
});
}
if (
tankDefinitions.tanks[tankId].name ===
tankDefinitions.tanks[tankId].name_full
) {
delete tankDefinitions.tanks[tankId].name_full;
}
modelDefinitions.models[tankId] = {
armor: hullArmor,
turret_origin: vector3TupleToBlitzkit(turretOrigin),
@@ -0,0 +1,90 @@
import { Avatar, encodePBBuffer, fetchGlossary, Gallery } from '@blitzkit/core';
import locales from '@blitzkit/i18n/locales.json';
import { extname } from 'path';
import ProgressBar from 'progress';
import { commitAssets } from '../core/github/commitAssets';
import { FileChange } from '../core/github/commitMultipleFiles';
export async function glossary() {
console.log('Building glossary...');
const avatars: Record<string, { avatar: Avatar; url: string }> = {};
await Promise.all(
locales.supported.map(async (supported) => {
const glossary = await fetchGlossary(supported.blitz ?? supported.locale);
console.log(
`Found ${Object.keys(glossary).length} things for ${supported.locale}`,
);
for (const key in glossary) {
const glossaryEntry = glossary[key];
if (
!key.startsWith('avatar') ||
key.endsWith('_part') ||
glossaryEntry.image_url === null
) {
continue;
}
if (key in avatars) {
avatars[key].avatar.name.locales[supported.locale] =
glossaryEntry.title;
} else {
const extension = extname(glossaryEntry.image_url);
avatars[key] = {
url: glossaryEntry.image_url,
avatar: {
id: key,
name: { locales: { [supported.locale]: glossaryEntry.title } },
extension,
},
};
}
}
}),
);
const changes: FileChange[] = [];
const gallery: Gallery = { avatars: [] };
const totalCount = Object.keys(avatars).length;
const bar = new ProgressBar(
`Fetching avatars of ${totalCount} things :bar`,
totalCount,
);
await Promise.all(
Object.entries(avatars).map(async ([key, { url, avatar }]) => {
if (!(locales.default in avatars[key].avatar.name.locales)) {
console.warn(`Avatar ${key} has no ${locales.default} name`);
bar.tick();
return;
}
gallery.avatars.push(avatar);
const content = await fetch(url).then((response) =>
response.arrayBuffer(),
);
changes.push({
path: `gallery/avatars/${key}${avatar.extension}`,
content: Buffer.from(content),
});
bar.tick();
}),
);
changes.push({
path: 'definitions/gallery.pb',
content: encodePBBuffer(Gallery, gallery),
});
await commitAssets('glossary', changes);
}
@@ -18,7 +18,10 @@ import { definitions } from './definitions';
import { equipmentIcons } from './equipmentIcons';
import { flags } from './flags';
import { gameModeBanners } from './gameModeBanners';
import { glossary } from './glossary';
import { migration } from './migration';
import { moduleIcons } from './moduleIcons';
import { popularTanks } from './popularTanks';
import { shellIcons } from './shellIcons';
import { skillIcons } from './skillIcons';
import { tankArmors } from './tankArmors';
@@ -28,6 +31,7 @@ import { tankModels } from './tankModels';
if (!targets && !allTargets) throw new Error('No target(s) specified');
const methods = [
migration,
definitions,
tankModels,
tankArmors,
@@ -43,6 +47,8 @@ const methods = [
boosterIcons,
tankIcons,
blitzkitTankIcons,
popularTanks,
glossary,
];
for (const method of methods) {
@@ -0,0 +1,38 @@
import { toUniqueId } from '@blitzkit/core';
import { readdir } from 'fs/promises';
import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
import { commitAssets } from '../core/github/commitAssets';
import { DATA } from './constants';
import { VehicleDefinitionList } from './definitions';
export async function migration() {
console.log('Building migration map...');
const map: Record<string, number> = {};
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
await Promise.all(
nations.map(async (nation) => {
const tankList = await readXMLDVPL<{ root: VehicleDefinitionList }>(
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tankList.root) {
const tank = tankList.root[tankKey];
const tankId = toUniqueId(nation, tank.id);
const davaKey = `${nation}:${tankKey}`;
map[davaKey] = tankId;
}
}),
);
await commitAssets('migration', [
{
path: 'definitions/migration.json',
content: Buffer.from(JSON.stringify(map)),
},
]);
}
@@ -0,0 +1,76 @@
import {
assertSecret,
encodePBBuffer,
fetchTankDefinitions,
PopularTanks,
} from '@blitzkit/core';
import { google } from 'googleapis';
import { commitAssets } from '../core/github/commitAssets';
export async function popularTanks() {
const tankDefinitions = await fetchTankDefinitions();
const auth = await google.auth.getClient({
keyFile: import.meta.env.GOOGLE_APPLICATION_CREDENTIALS,
scopes: ['https://www.googleapis.com/auth/analytics.readonly'],
});
const analytics = google.analyticsdata({ version: 'v1beta', auth });
const report = await analytics.properties.runReport({
property: `properties/${assertSecret(import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID)}`,
requestBody: {
dateRanges: [{ startDate: '7daysAgo', endDate: 'today' }],
dimensions: [{ name: 'pagePath' }],
metrics: [{ name: 'screenPageViews' }],
orderBys: [{ metric: { metricName: 'screenPageViews' }, desc: true }],
dimensionFilter: {
filter: {
fieldName: 'pagePath',
stringFilter: {
matchType: 'BEGINS_WITH',
value: '/tanks/',
},
},
},
},
});
if (!report.data.rows) {
throw new Error('No rows in report');
}
const tanks = report.data.rows
.filter(
(row) =>
row.dimensionValues &&
row.dimensionValues[0].value &&
row.dimensionValues[0].value !== '/tanks/' &&
row.metricValues &&
row.metricValues[0].value,
)
.map((row) => ({
id: Number(
row.dimensionValues![0].value!.match(
/\/tools\/tankopedia\/(\d+)\/?/,
)?.[1],
),
views: Number(row.metricValues![0].value!),
}))
.filter((row) => {
if (!(row.id in tankDefinitions.tanks)) {
console.warn('Skipping unknown popular tank', row.id);
return false;
}
return row.id in tankDefinitions.tanks;
})
.slice(0, 8)
.map(({ id, views }) => ({ id, views }));
const popularTanks: PopularTanks = { tanks };
await commitAssets('popular tanks', [
{
path: 'definitions/popular-tanks.pb',
content: encodePBBuffer(PopularTanks, popularTanks),
},
]);
}
@@ -166,28 +166,9 @@ export async function extractModel(data: string, path: string) {
break;
case 'TransformComponent': {
const translation = new Vector3();
const rotation = new Quaternion();
const scale = new Vector3();
new Matrix4()
.multiplyMatrices(
new Matrix4().compose(
new Vector3().fromArray(component['tc.worldTranslation']),
new Quaternion().fromArray(component['tc.worldRotation']),
new Vector3().fromArray(component['tc.worldScale']),
),
new Matrix4().compose(
new Vector3().fromArray(component['tc.localTranslation']),
new Quaternion().fromArray(component['tc.localRotation']),
new Vector3().fromArray(component['tc.localScale']),
),
)
.decompose(translation, rotation, scale);
node.setTranslation(translation.toArray());
node.setRotation(rotation.toArray() as Vector4Tuple);
node.setScale(scale.toArray());
node.setTranslation(component['tc.localTranslation']);
node.setRotation(component['tc.localRotation']);
node.setScale(component['tc.localScale']);
break;
}
@@ -3,7 +3,7 @@ import { DATA_COMPRESSED } from '../../buildAssets/constants';
import { readDVPL } from './readDVPL';
export async function readDVPLFile(file: string) {
if (DATA_COMPRESSED) {
if (DATA_COMPRESSED && !file.endsWith('.dvpl')) {
return readDVPL(await readFile(`${file}.dvpl`));
}
@@ -6,7 +6,7 @@ import { TextureMutation } from './constants';
export async function readTexture(path: string, mutation?: TextureMutation) {
const ddsTexturePath = path.replace('.tex', '.dx11.dds');
const isDds = existsSync(ddsTexturePath);
const isDds = existsSync(`${ddsTexturePath}.dvpl`);
const resolvedTexturePath = isDds
? ddsTexturePath
: ddsTexturePath.replace('.dds', '.pvr');
@@ -1,4 +1,5 @@
import { assertSecret } from '@blitzkit/core';
import { chunk } from 'lodash-es';
import { commitMultipleFiles, FileChange } from './commitMultipleFiles';
export async function commitAssets(message: string, changes: FileChange[]) {
@@ -6,10 +7,19 @@ export async function commitAssets(message: string, changes: FileChange[]) {
if (changes.length === 0) return;
await commitMultipleFiles(
assertSecret(import.meta.env.PUBLIC_ASSET_REPO),
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
`${message} - ${new Date().toDateString()}`,
changes,
);
const chunks = chunk(changes, 64);
let chunkIndex = 0;
for (const chunk of chunks) {
console.log(`Committing chunk ${chunkIndex + 1}/${chunks.length}`);
await commitMultipleFiles(
assertSecret(import.meta.env.PUBLIC_ASSET_REPO),
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH),
`${message} - ${new Date().toDateString()} (${chunkIndex + 1}/${chunks.length})`,
chunk,
);
chunkIndex++;
}
}
@@ -7,6 +7,8 @@ export interface FileChange {
content: Buffer;
}
const heuristicFormats = ['glb', 'webp', 'jpg', 'png'];
export async function commitMultipleFiles(
repoRaw: string,
branch: string,
@@ -21,32 +23,69 @@ export async function commitMultipleFiles(
const response = await fetch(
`https://raw.githubusercontent.com/${blobPath}`,
);
let isNew: boolean;
let isDifferent: boolean;
let diff: number;
if (response.status === 404) {
console.log(
`🟢 (+${change.content.length.toLocaleString()}B) ${blobPath}`,
);
changes.push(change);
isNew = true;
diff = change.content.length;
isDifferent = true;
} else if (
response.headers.has('Content-Length') &&
parseInt(response.headers.get('Content-Length')!) !==
change.content.length
) {
isNew = false;
diff =
change.content.length -
parseInt(response.headers.get('Content-Length')!);
isDifferent = true;
} else if (response.status === 200) {
isNew = false;
const buffer = Buffer.from(await response.arrayBuffer());
// we discard blob if they're the same size; it's unlikely their
// contents will be different; I love playing russian roulette!
const equal = buffer.length === change.content.length || buffer.equals(change.content);
if (!equal) {
const diff = change.content.length - buffer.length;
console.log(
`🟡 (${diff > 0 ? '+' : ''}${diff.toLocaleString()}B) ${blobPath}`,
);
changes.push(change);
if (buffer.equals(change.content)) {
diff = 0;
isDifferent = false;
} else {
diff = change.content.length - buffer.length;
isDifferent = true;
}
} else {
throw new Error(
`Unexpected status code ${response.status} for ${blobPath}`,
);
}
/**
* Heuristic: if it's a large blob like a .glb, a diff = 0 might end up
* being a false positive with isDifferent = true. So, it's okay to
* assume it's unchanged.
*/
if (
diff === 0 &&
heuristicFormats.some((format) => change.path.endsWith(`.${format}`))
) {
isDifferent = false;
isNew = false;
}
if (isNew) {
console.log(
`🟢 (+${change.content.length.toLocaleString()}B) ${blobPath}`,
);
changes.push(change);
} else if (isDifferent) {
console.log(
`🟡 (${diff > 0 ? '+' : ''}${diff.toLocaleString()}B) ${blobPath}`,
);
changes.push(change);
} else {
console.log(
`🔵 (${change.content.length.toLocaleString()}B) ${blobPath}`,
);
}
}),
);
+53
View File
@@ -0,0 +1,53 @@
import {
DEFAULT_LOCALE,
SUPPORTED_LOCALES,
TranslationTree,
} from '@blitzkit/i18n';
import { writeFile } from 'fs/promises';
const ROOT = '@blitzkit/i18n/strings';
const WORKING_ROOT = '../../packages/i18n/strings';
const defaultStrings = require(`${ROOT}/${DEFAULT_LOCALE}.json`).default;
const nonDefaultLocales = SUPPORTED_LOCALES.filter(
(locale) => locale !== DEFAULT_LOCALE,
);
function prune(defaultStrings: TranslationTree, strings: TranslationTree) {
for (const key in strings) {
if (key in defaultStrings) {
if (typeof defaultStrings[key] === typeof strings[key]) {
if (typeof strings[key] === 'string') {
if (
// are equivalent regardless of %s flavor
(defaultStrings[key] as string).replaceAll(/%s\d/g, '%s') ===
(strings[key] as string).replaceAll(/%s\d/g, '%s') ||
// string is empty
strings[key] === '' ||
// string has old flavor of %s
strings[key].match(/%s([^\d]|$)/g) !== null
) {
delete strings[key];
}
} else {
prune(defaultStrings[key] as TranslationTree, strings[key]);
}
} else {
delete strings[key];
}
} else {
delete strings[key];
}
}
return strings;
}
for (const locale of nonDefaultLocales) {
console.log(`Pruning ${locale}...`);
const strings = require(`${ROOT}/${locale}.json`).default;
const pruned = prune(defaultStrings, strings);
writeFile(`${WORKING_ROOT}/${locale}.json`, JSON.stringify(pruned, null, 2));
}
+2 -1
View File
@@ -1,5 +1,6 @@
// @ts-check
import partytown from '@astrojs/partytown';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
import { SUPPORTED_LOCALES } from '@blitzkit/i18n';
@@ -20,7 +21,7 @@ export default defineConfig({
outDir: '../../dist/website',
prefetch: true,
integrations: [react(), sitemap()],
integrations: [react(), sitemap(), partytown()],
vite: {
resolve: {
alias: {
+8 -2
View File
@@ -10,8 +10,11 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/partytown": "^2.1.4",
"@astrolib/seo": "^1.0.0-beta.8",
"@blitzkit/core": "workspace:*",
"@blitzkit/i18n": "workspace:*",
"@fontsource/inter": "^5.2.5",
"@nanostores/persistent": "^0.10.2",
"@nanostores/react": "^0.7.3",
"@nivo/bar": "^0.87.0",
@@ -23,11 +26,11 @@
"@react-three/fiber": "^8.17.10",
"compare-versions": "^6.1.1",
"echarts": "^5.5.1",
"googleapis": "^144.0.0",
"immer": "^10.1.1",
"js-cookie": "^3.0.5",
"markdown-to-jsx": "^7.5.0",
"nanostores": "^0.11.3",
"quicklime": "^1.2.0",
"react": "^18.3.1",
"react-device-detect": "^2.2.3",
"react-dom": "^18.3.1",
@@ -40,10 +43,13 @@
"@astrojs/check": "^0.9.4",
"@astrojs/react": "^3.6.2",
"@astrojs/sitemap": "^3.2.1",
"@playform/compress": "^0.1.9",
"@playform/inline": "^0.1.2",
"@types/js-cookie": "^3.0.6",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/three": "^0.160.0",
"astro": "^4.16.7"
"astro": "^4.16.7",
"astro-compressor": "^1.0.0"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Loaded 100 of 303 files, more files were not shown because too many files have changed in this diff. Show more