From 4f9fdb911eaa06ff614bb295e75c08768e4cfd30 Mon Sep 17 00:00:00 2001 From: erik Date: Thu, 1 May 2025 19:21:34 +0000 Subject: [PATCH 1/4] Websocket test --- main.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 0892dcd8..fdb48928 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ import json import sqlite3 from typing import Dict -from fastapi import FastAPI, Header, HTTPException, Query +from fastapi import FastAPI, Header, HTTPException, Query, WebSocket, WebSocketDisconnect from fastapi.responses import JSONResponse from fastapi.routing import APIRoute from fastapi.staticfiles import StaticFiles @@ -11,6 +11,8 @@ from pydantic import BaseModel from typing import Optional from db import init_db, save_snapshot, DB_FILE +import asyncio +from starlette.concurrency import run_in_threadpool # ------------------------------------------------------------------ app = FastAPI() @@ -194,6 +196,42 @@ def get_trails( ] return JSONResponse(content={"trails": trails}) +# -------------------- WebSocket endpoints ----------------------- +browser_conns: set[WebSocket] = set() + +async def _broadcast_to_browser_clients(snapshot: dict): + for ws in list(browser_conns): + try: + await ws.send_json(snapshot) + except WebSocketDisconnect: + browser_conns.remove(ws) + +@app.websocket("/ws/position") +async def ws_receive_snapshots(websocket: WebSocket, secret: str = Query(...)): + await websocket.accept() + if secret != SHARED_SECRET: + await websocket.close(code=1008) + return + try: + while True: + data = await websocket.receive_json() + snap = TelemetrySnapshot.parse_obj(data) + live_snapshots[snap.character_name] = snap.dict() + await run_in_threadpool(save_snapshot, snap.dict()) + await _broadcast_to_browser_clients(snap.dict()) + except WebSocketDisconnect: + pass + +@app.websocket("/ws/live") +async def ws_live_updates(websocket: WebSocket): + await websocket.accept() + browser_conns.add(websocket) + try: + while True: + await asyncio.sleep(3600) + except WebSocketDisconnect: + browser_conns.remove(websocket) + # -------------------- static frontend --------------------------- app.mount("/", StaticFiles(directory="static", html=True), name="static") From a00cfb688c5de58afca0df8c3a5eec2cfd4f1566 Mon Sep 17 00:00:00 2001 From: Johan Lundberg Date: Fri, 2 May 2025 02:16:29 +0200 Subject: [PATCH 2/4] script to generate some player data --- .gitignore | 2 ++ __init__.py | 0 generate_data.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 .gitignore create mode 100644 __init__.py create mode 100644 generate_data.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..033df5fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +__pycache__ diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/generate_data.py b/generate_data.py new file mode 100644 index 00000000..de21bde7 --- /dev/null +++ b/generate_data.py @@ -0,0 +1,45 @@ +import httpx +from datetime import datetime, timedelta, timezone +from time import sleep +from main import TelemetrySnapshot + + +def main() -> None: + wait = 10 + online_time = -10 + ew = 0 + ns = 0 + while True: + snapshot = TelemetrySnapshot( + character_name="Test name", + char_tag="test_tag", + session_id="test_session_id", + timestamp=datetime.now(tz=timezone.utc), + ew=ew, + ns=ns, + z=0, + kills=0, + kills_per_hour="kph_str", + onlinetime=str(timedelta(seconds=online_time)), + deaths=0, + rares_found=0, + prismatic_taper_count=0, + vt_state="test state", + ) + resp = httpx.post( + "http://localhost:8000/position/", + data=snapshot.model_dump_json(), + headers={ + "Content-Type": "application/json", + "X-PLUGIN-SECRET": "your_shared_secret", + }, + ) + print(resp) + sleep(wait) + ew += 0.1 + ns += 0.1 + online_time += wait + + +if __name__ == "__main__": + main() From 4a826e5ac20d646e0cc9ca1b5aecc166ef9c83ff Mon Sep 17 00:00:00 2001 From: Johan Lundberg Date: Fri, 2 May 2025 20:24:18 +0200 Subject: [PATCH 3/4] remove unneeded __init__.py --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29b..00000000 From b19568b1abd312347bfac4122f67aeeb73382eed Mon Sep 17 00:00:00 2001 From: Johan Lundberg Date: Fri, 2 May 2025 20:55:54 +0200 Subject: [PATCH 4/4] add online time and deaths to player card --- generate_data.py | 2 +- static/script.js | 2 ++ static/style.css | 9 +++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/generate_data.py b/generate_data.py index de21bde7..fdbbfd1a 100644 --- a/generate_data.py +++ b/generate_data.py @@ -6,7 +6,7 @@ from main import TelemetrySnapshot def main() -> None: wait = 10 - online_time = -10 + online_time = 24 * 3600 # start at 1 day ew = 0 ns = 0 while True: diff --git a/static/script.js b/static/script.js index 0b6b9aff..bfe94b72 100644 --- a/static/script.js +++ b/static/script.js @@ -209,6 +209,8 @@ function render(players) { ${p.kills_per_hour} ${p.rares_found} ${p.vt_state} + ${p.onlinetime} + ${p.deaths} `; li.addEventListener('click', () => selectPlayer(p, x, y)); diff --git a/static/style.css b/static/style.css index 031a41be..b6a7286d 100644 --- a/static/style.css +++ b/static/style.css @@ -156,11 +156,12 @@ body { #playerList li { display: grid; grid-template-columns: 1fr auto; - grid-template-rows: auto auto auto; + grid-template-rows: auto auto auto auto; grid-template-areas: "name loc" "kills kph" - "rares meta"; + "rares meta" + "onlinetime deaths"; gap: 4px 8px; margin: 6px 0; padding: 8px 10px; @@ -178,6 +179,8 @@ body { .stat.kph { grid-area: kph; } .stat.rares { grid-area: rares; } .stat.meta { grid-area: meta; } +.stat.onlinetime { grid-area: onlinetime; } +.stat.deaths { grid-area: deaths; } /* pill styling */ #playerList li .stat { @@ -198,6 +201,8 @@ body { background: var(--accent); color: #111; } +.stat.onlinetime::before { content: "🕑 "} +.stat.deaths::before { content: "💀 "} /* hover & selected states */ #playerList li:hover { background: var(--card-hov); }