Skip to content

Commit

Permalink
Add analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
justin-together committed Oct 15, 2024
1 parent c2b8444 commit 5e65f02
Show file tree
Hide file tree
Showing 17 changed files with 817 additions and 416 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# 🥧📡 Anti/PieRay Helper
# 🏰 Empire Utils

![Logo](images/icon.png?raw=true "Logo")

Minecraft analytics and anti/PieRay helper.

## 🥧📡 Anti/PieRay Helper

- Press F3+C in vanilla, or setup bind for [Lunar Client](https://www.lunarclient.com/) mod [Coordinates](https://lunarclient.dev/apollo/developers/mods/coordinates) to "Copy Coords to Clipboard".
- Move to a "corner" showing the entity in PieChart, but where the entity is missing from PieChart in the neighboring chunks in the X and Z directions. Look towards the entity.
Expand Down
2 changes: 1 addition & 1 deletion forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const config: ForgeConfig = {
config: {
repository: {
owner: 'GreenAppers',
name: 'PieRayHelper',
name: 'EmpireUtils',
},
prerelease: true,
},
Expand Down
Binary file modified images/1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/icon.icns
Binary file not shown.
Binary file modified images/icon.ico
Binary file not shown.
Binary file modified images/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "PieRayHelper",
"productName": "PieRayHelper",
"name": "EmpireUtils",
"productName": "EmpireUtils",
"version": "1.0.0",
"description": "Anti/PieRay Helper",
"description": "Minecraft analytics and anti/PieRay helper",
"keywords": [
"minecraft",
"piechart",
Expand Down Expand Up @@ -32,7 +32,8 @@
"framer-motion": "^11.5.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"recharts": "^2.13.0"
"recharts": "^2.13.0",
"tail": "^2.2.6"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand All @@ -49,6 +50,7 @@
"@electron/fuses": "^1.8.0",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",
"@types/tail": "^2.2.3",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"@vercel/webpack-asset-relocator-loader": "1.7.3",
Expand Down
187 changes: 187 additions & 0 deletions src/components/Analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import React, { useEffect, useState } from 'react'
import {
Area,
AreaChart,
CartesianGrid,
Tooltip as RechartsTooltip,
XAxis,
YAxis,
} from 'recharts'
import type { GameLog, TimeSeries } from '../types'
import { addSampleToTimeseries } from '../utils/timeseries'
import { Heading, Link, Spacer } from '@chakra-ui/react'

const vanillaTimestamp = /^\[(\d\d:\d\d:\d\d)\]/
const lunarTimestamp = /^\[(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d.\d\d\d)\]/

const connectingTo = /Connecting to (\W),/
const soldContainer = /Successfully sold a container worth: \$([,\d]+.\d+)!/

const contentDelimiter = ': '

export default function TimeseriesChart(props: {
color?: string
timeseries: TimeSeries
}) {
const color = props.color || '#38A169'
return (
<>
<AreaChart
accessibilityLayer
data={props.timeseries.buckets.map((x) => ({
time: new Date(x.time),
value: x.value,
}))}
width={730}
height={250}
margin={{
left: 10,
right: 40,
top: 40,
bottom: 20,
}}
>
<CartesianGrid vertical={false} />
<RechartsTooltip cursor={{ strokeDasharray: '3 3' }} />

<XAxis
dataKey="time"
tickLine={true}
axisLine={true}
tickMargin={8}
minTickGap={32}
tickFormatter={(value) => new Date(value).toLocaleTimeString()}
/>

<YAxis
tickLine={true}
axisLine={true}
tickMargin={8}
tickFormatter={(value) => `${value}`}
/>
<defs>
<linearGradient id={`fill${color}`} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color} stopOpacity={0.8} />
<stop offset="95%" stopColor={color} stopOpacity={0.1} />
</linearGradient>
</defs>
<Area
dataKey="value"
fill={`url(#fill${color})`}
type="monotone"
stroke={color}
strokeWidth={1.5}
/>
</AreaChart>
</>
)
}

export function Analytics() {
const [gameLogPath, setGameLogPath] = useState('')
const [gameLogPaths, setGameLogPaths] = useState([] as GameLog[])
const [serverName, setServerName] = useState('')
const [soldTimeseries, setSoldTimeseries] = useState<TimeSeries>({
buckets: [],
duration: 60 * 1000,
samples: 60,
})

useEffect(() => {
window.api.findGameLog().then((gameLogPaths) => {
setGameLogPath(
(currentLog) => currentLog || gameLogPaths?.[0]?.path || currentLog
)
setGameLogPaths(gameLogPaths)
})
}, [setGameLogPath, setGameLogPaths])

useEffect(() => {
const handle = setInterval(
() =>
setSoldTimeseries((timeseries) =>
addSampleToTimeseries(0, new Date(), timeseries)
),
30 * 1000
)
return () => clearInterval(handle)
}, [setSoldTimeseries])

useEffect(() => {
if (!gameLogPath) return
let total = 0
let firstTimestamp: Date | undefined

const handle = window.api.onTailGameLog(gameLogPath, (line: string) => {
let timestamp: Date | undefined

const now = new Date()
const vanillaTimestampMatch = line.match(vanillaTimestamp)
if (vanillaTimestampMatch)
timestamp = new Date(
new Date().toLocaleDateString() + ' ' + vanillaTimestampMatch[1]
)
const lunarTimestampMatch = line.match(lunarTimestamp)
if (lunarTimestampMatch) timestamp = new Date(lunarTimestampMatch[1])
if (!timestamp) timestamp = now
if (!firstTimestamp) firstTimestamp = timestamp

const contentDelimiterIndex = line.indexOf(contentDelimiter)
if (contentDelimiterIndex < 0) return
const content = line.slice(
contentDelimiterIndex + contentDelimiter.length
)

const connectingToMatch = content.match(connectingTo)
if (connectingToMatch) {
setServerName(connectingToMatch[1])
}

const soldContainerMatch = content.match(soldContainer)
if (soldContainerMatch) {
const soldContainerValue = parseFloat(
soldContainerMatch[1].replace(/,/g, '')
)
total += soldContainerValue
setSoldTimeseries((timeseries) =>
addSampleToTimeseries(soldContainerValue, timestamp, timeseries)
)

const totalSeconds =
(timestamp.getTime() - firstTimestamp.getTime()) / 1000
const ratePerMinute = (total * 60) / totalSeconds
console.log(
'Sold container value',
soldContainerValue,
'Total',
total,
'Rate',
ratePerMinute.toFixed(2),
'per minute',
(ratePerMinute * 60).toFixed(2),
'per hour'
)
}
})
return () => window.api.removeListener(handle)
}, [gameLogPath, setServerName, setSoldTimeseries])

return (
<>
<Heading>{serverName || 'Analytics'}</Heading>
<Heading as="h6" size="xs">
Game log&nbsp;
<Link
onClick={() => window.api.openBrowserWindow(`file://${gameLogPath}`)}
>
{gameLogPath}
</Link>
</Heading>

<Heading as="h5" size="sm" marginTop="2rem">
Successfully sold a container worth
</Heading>
<TimeseriesChart timeseries={soldTimeseries} />
</>
)
}
Loading

0 comments on commit 5e65f02

Please sign in to comment.