mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: real popular tanks list
This commit is contained in:
1 parent
31a61c8a6b
commit
4b8a154142
8 files changed
+100
-45
No files matched your search
Vendored
+2
@@ -7,6 +7,8 @@
|
|||||||
"adsense",
|
"adsense",
|
||||||
"aeson",
|
"aeson",
|
||||||
"alphatest",
|
"alphatest",
|
||||||
|
"analyticsdata",
|
||||||
|
"analyticsreporting",
|
||||||
"APAC",
|
"APAC",
|
||||||
"apcr",
|
"apcr",
|
||||||
"applypatches",
|
"applypatches",
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"csv-parse": "^5.5.6",
|
"csv-parse": "^5.5.6",
|
||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
"fast-xml-parser": "^4.4.1",
|
"fast-xml-parser": "^4.4.1",
|
||||||
"googleapis": "^134.0.0",
|
|
||||||
"progress": "^2.0.3"
|
"progress": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"@react-three/fiber": "^8.17.10",
|
"@react-three/fiber": "^8.17.10",
|
||||||
"compare-versions": "^6.1.1",
|
"compare-versions": "^6.1.1",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
|
"googleapis": "^144.0.0",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"markdown-to-jsx": "^7.5.0",
|
"markdown-to-jsx": "^7.5.0",
|
||||||
|
|||||||
@@ -1,30 +1,67 @@
|
|||||||
import { Box, Flex, Heading } from '@radix-ui/themes';
|
import { assertSecret } from '@blitzkit/core';
|
||||||
import { awaitableAverageDefinitions } from '../core/awaitables/averageDefinitions';
|
import { EyeOpenIcon } from '@radix-ui/react-icons';
|
||||||
|
import { Box, Flex, Heading, Text } from '@radix-ui/themes';
|
||||||
|
import { google } from 'googleapis';
|
||||||
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions';
|
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions';
|
||||||
import { TankopediaPersistent } from '../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../stores/tankopediaPersistent';
|
||||||
import { TankCard } from './TankCard';
|
import { TankCard } from './TankCard';
|
||||||
|
|
||||||
const [tankDefinitions, averageDefinitions] = await Promise.all([
|
const [tankDefinitions] = await Promise.all([awaitableTankDefinitions, ,]);
|
||||||
awaitableTankDefinitions,
|
|
||||||
awaitableAverageDefinitions,
|
|
||||||
]);
|
|
||||||
const tanks = Object.values(tankDefinitions.tanks);
|
|
||||||
const tankAverages = Object.entries(averageDefinitions.averages).map(
|
|
||||||
([id, average]) => ({
|
|
||||||
id: Number(id),
|
|
||||||
...average,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// lol don't ask questions
|
const auth = await google.auth.getClient({
|
||||||
const testTanks = tanks
|
keyFile: import.meta.env.GOOGLE_APPLICATION_CREDENTIALS,
|
||||||
.filter((tank) => tank.testing)
|
scopes: ['https://www.googleapis.com/auth/analytics.readonly'],
|
||||||
.sort((a, b) => b.tier - a.tier);
|
});
|
||||||
const mostPlayedTanks = tankAverages
|
const analytics = google.analyticsdata({ version: 'v1beta', auth });
|
||||||
.sort((a, b) => b.samples.d_1 - a.samples.d_1)
|
const report = await analytics.properties.runReport({
|
||||||
.map(({ id }) => tankDefinitions.tanks[id])
|
property: `properties/${assertSecret(import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID)}`,
|
||||||
.filter((tank) => tank !== undefined && !tank.testing);
|
requestBody: {
|
||||||
const popularTanks = [...testTanks, ...mostPlayedTanks].slice(0, 8);
|
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: '/tools/tankopedia/' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!report.data.rows) {
|
||||||
|
throw new Error('No rows in report');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hotTanks = report.data.rows
|
||||||
|
.filter(
|
||||||
|
(row) =>
|
||||||
|
row.dimensionValues &&
|
||||||
|
row.dimensionValues[0].value &&
|
||||||
|
row.dimensionValues[0].value !== '/tools/tankopedia/' &&
|
||||||
|
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.log('Unknown tank', row.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return row.id in tankDefinitions.tanks;
|
||||||
|
})
|
||||||
|
.slice(0, 16)
|
||||||
|
.map(({ id, views }) => ({
|
||||||
|
tank: tankDefinitions.tanks[id],
|
||||||
|
views,
|
||||||
|
}));
|
||||||
|
|
||||||
export function HomePageHotTanks() {
|
export function HomePageHotTanks() {
|
||||||
return (
|
return (
|
||||||
@@ -36,15 +73,26 @@ export function HomePageHotTanks() {
|
|||||||
|
|
||||||
function Content() {
|
function Content() {
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="5" pt="4" pb="8">
|
<Flex direction="column" pt="4" pb="8">
|
||||||
<Heading align="center" size="5">
|
<Heading align="center" size="5">
|
||||||
Popular tanks
|
Popular tanks
|
||||||
</Heading>
|
</Heading>
|
||||||
|
<Text color="gray" align="center" mb="5" size="2">
|
||||||
|
Last 7 days
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Flex justify="center" gap="4" wrap="wrap">
|
<Flex justify="center" gap="4" wrap="wrap">
|
||||||
{popularTanks.map((tank) => (
|
{hotTanks.map((row) => (
|
||||||
<Box width="7rem" key={tank.id}>
|
<Box width="7rem" key={row.tank.id}>
|
||||||
<TankCard tank={tank} />
|
<TankCard
|
||||||
|
tank={row.tank}
|
||||||
|
discriminator={
|
||||||
|
<Flex align="center" gap="1" justify="center">
|
||||||
|
<EyeOpenIcon />
|
||||||
|
{Math.round(row.views * (30 / 7)).toLocaleString('en-US')}
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
---
|
||||||
|
import { assertSecret } from '@blitzkit/core';
|
||||||
|
---
|
||||||
|
|
||||||
{/* google analytics */}
|
{/* google analytics */}
|
||||||
<script
|
<script
|
||||||
is:inline
|
is:inline
|
||||||
async
|
async
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=G-GL2JVHCGPQ"></script>
|
src={`https://www.googletagmanager.com/gtag/js?id=${assertSecret(
|
||||||
|
import.meta.env.PUBLIC_GOOGLE_TAG_MANAGER_ID,
|
||||||
|
)}`}></script>
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
|
||||||
@@ -10,7 +16,7 @@
|
|||||||
dataLayer.push(arguments);
|
dataLayer.push(arguments);
|
||||||
}
|
}
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
gtag('config', 'G-GL2JVHCGPQ');
|
gtag('config', assertSecret(import.meta.env.PUBLIC_GOOGLE_TAG_MANAGER_ID));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{/* nitro ads */}
|
{/* nitro ads */}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { asset, tankIcon, TankType, type TankDefinition } from '@blitzkit/core';
|
import { asset, tankIcon, TankType, type TankDefinition } from '@blitzkit/core';
|
||||||
import { Flex, Link, Text, type TextProps } from '@radix-ui/themes';
|
import { Flex, Link, Text, type TextProps } from '@radix-ui/themes';
|
||||||
import { uniq } from 'lodash-es';
|
import { uniq } from 'lodash-es';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef, type ReactNode } from 'react';
|
||||||
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
|
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
|
||||||
import { classIcons } from '../ClassIcon';
|
import { classIcons } from '../ClassIcon';
|
||||||
import { MAX_RECENTLY_VIEWED } from '../TankSearch/constants';
|
import { MAX_RECENTLY_VIEWED } from '../TankSearch/constants';
|
||||||
@@ -10,7 +10,7 @@ import './index.css';
|
|||||||
type TankCardProps = TextProps & {
|
type TankCardProps = TextProps & {
|
||||||
tank: TankDefinition;
|
tank: TankDefinition;
|
||||||
onTankSelect?: (tank: TankDefinition) => void;
|
onTankSelect?: (tank: TankDefinition) => void;
|
||||||
discriminator?: string;
|
discriminator: ReactNode;
|
||||||
noLink?: boolean;
|
noLink?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+13
-14
@@ -108,7 +108,6 @@ export function SceneProps() {
|
|||||||
let { dispersion } = characteristics;
|
let { dispersion } = characteristics;
|
||||||
const turretRotationSpeed = degToRad(characteristics.turretTraverseSpeed);
|
const turretRotationSpeed = degToRad(characteristics.turretTraverseSpeed);
|
||||||
const gunRotationSpeed = degToRad(characteristics.gunTraverseSpeed);
|
const gunRotationSpeed = degToRad(characteristics.gunTraverseSpeed);
|
||||||
|
|
||||||
const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/12305.glb`));
|
const mockTank = useLoader(GLTFLoader, asset(`3d/tanks/models/12305.glb`));
|
||||||
const texture = useLoader(TextureLoader, imgur('C28Z8nU'));
|
const texture = useLoader(TextureLoader, imgur('C28Z8nU'));
|
||||||
texture.anisotropy = 2;
|
texture.anisotropy = 2;
|
||||||
@@ -119,6 +118,7 @@ export function SceneProps() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
|
let lastPenalty = 0;
|
||||||
let DEBUG_disable = false;
|
let DEBUG_disable = false;
|
||||||
|
|
||||||
useFrame(({ raycaster, camera, clock, gl }) => {
|
useFrame(({ raycaster, camera, clock, gl }) => {
|
||||||
@@ -160,22 +160,22 @@ export function SceneProps() {
|
|||||||
const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
const desiredYaw = Math.atan2(-direction.x, -direction.z);
|
||||||
const deltaPitch = desiredPitch - lastPitch;
|
const deltaPitch = desiredPitch - lastPitch;
|
||||||
const deltaYaw = desiredYaw - lastYaw;
|
const deltaYaw = desiredYaw - lastYaw;
|
||||||
const maxDeltaPitch =
|
const deltaAngle = Math.sqrt(deltaPitch ** 2 + deltaYaw ** 2);
|
||||||
Math.sign(deltaPitch) *
|
const clampedDeltaAngle = Math.min(gunRotationSpeed * deltaT, deltaAngle);
|
||||||
Math.min(gunRotationSpeed * deltaT, Math.abs(deltaPitch));
|
const clampedDeltaPitch = deltaPitch * (deltaAngle / clampedDeltaAngle);
|
||||||
const maxDeltaYaw =
|
const clampedDeltaYaw = deltaYaw * (deltaAngle / clampedDeltaAngle);
|
||||||
Math.sign(deltaYaw) *
|
|
||||||
Math.min(turretRotationSpeed * deltaT, Math.abs(deltaYaw));
|
|
||||||
|
|
||||||
const [pitch, yaw] = applyPitchYawLimits(
|
const [pitch, yaw] = applyPitchYawLimits(
|
||||||
lastPitch + maxDeltaPitch,
|
lastPitch + clampedDeltaPitch,
|
||||||
lastYaw + maxDeltaYaw,
|
lastYaw + clampedDeltaYaw,
|
||||||
gunModelDefinition.pitch,
|
gunModelDefinition.pitch,
|
||||||
turretModelDefinition.yaw,
|
turretModelDefinition.yaw,
|
||||||
);
|
);
|
||||||
|
|
||||||
const deltaRotation = Math.abs(maxDeltaPitch) + Math.abs(maxDeltaYaw);
|
const actualDeltaPitch = pitch - lastPitch;
|
||||||
const rotationSpeed = deltaRotation;
|
const actualDeltaYaw = yaw - lastYaw;
|
||||||
|
const deltaRotation = Math.abs(actualDeltaPitch) + Math.abs(actualDeltaYaw);
|
||||||
|
const rotationSpeed = deltaRotation / deltaT;
|
||||||
const turretTraversePenalty =
|
const turretTraversePenalty =
|
||||||
characteristics.dispersionTurretTraversing * radToDeg(rotationSpeed);
|
characteristics.dispersionTurretTraversing * radToDeg(rotationSpeed);
|
||||||
|
|
||||||
@@ -186,9 +186,8 @@ export function SceneProps() {
|
|||||||
dispersionMod * Math.exp(-deltaT / characteristics.aimTime) -
|
dispersionMod * Math.exp(-deltaT / characteristics.aimTime) -
|
||||||
dispersionMod;
|
dispersionMod;
|
||||||
|
|
||||||
dispersion += penalty + dispersionModDischarged;
|
dispersion += penalty - lastPenalty + dispersionModDischarged;
|
||||||
|
lastPenalty = penalty;
|
||||||
document.title = `${dispersion.toFixed(3)} (${penalty.toFixed(3)})`;
|
|
||||||
|
|
||||||
modelTransformEvent.emit({ pitch, yaw });
|
modelTransformEvent.emit({ pitch, yaw });
|
||||||
lastPitch = pitch;
|
lastPitch = pitch;
|
||||||
|
|||||||
Reference in new issue
Block a user