Files
dodep-simulator/websocket_manager.py
T

130 lines
5.0 KiB
Python

import json
import asyncio
import random
import math
from typing import Set, Dict, Optional
from fastapi import WebSocket
from datetime import datetime
class ConnectionManager:
def __init__(self):
self.active_connections: Dict[int, Set[WebSocket]] = {}
self._lock = asyncio.Lock()
async def connect(self, websocket: WebSocket, user_id: int):
await websocket.accept()
async with self._lock:
if user_id not in self.active_connections:
self.active_connections[user_id] = set()
self.active_connections[user_id].add(websocket)
async def disconnect(self, websocket: WebSocket, user_id: int):
async with self._lock:
if user_id in self.active_connections:
self.active_connections[user_id].discard(websocket)
if not self.active_connections[user_id]:
del self.active_connections[user_id]
async def send_personal(self, user_id: int, message: dict):
async with self._lock:
if user_id in self.active_connections:
dead = set()
for ws in self.active_connections[user_id]:
try:
await ws.send_json(message)
except Exception:
dead.add(ws)
self.active_connections[user_id] -= dead
if not self.active_connections[user_id]:
del self.active_connections[user_id]
async def broadcast(self, message: dict):
async with self._lock:
dead: Dict[int, Set[WebSocket]] = {}
for uid, sockets in self.active_connections.items():
for ws in sockets:
try:
await ws.send_json(message)
except Exception:
dead.setdefault(uid, set()).add(ws)
for uid, sockets in dead.items():
self.active_connections[uid] -= sockets
if not self.active_connections[uid]:
del self.active_connections[uid]
async def broadcast_except(self, exclude_user_id: int, message: dict):
async with self._lock:
for uid, sockets in self.active_connections.items():
if uid == exclude_user_id:
continue
for ws in sockets:
try:
await ws.send_json(message)
except Exception:
pass
@property
def online_count(self) -> int:
"""Количество уникальных пользователей онлайн"""
return len(self.active_connections)
manager = ConnectionManager()
# ─── Crash Game State ────────────────────────────────────────────────────────
class CrashGameState:
def __init__(self):
self.running = False
self.current_multiplier = 1.0
self.crash_point = 1.0
self.bets: Dict[int, float] = {}
self.cashed_out: Dict[int, float] = {}
self.phase = "waiting"
self.timer = 0
self.round = 0
self.history: list = []
self._start_time = 0.0
self._elapsed = 0.0
def generate_crash_point(self) -> float:
"""True CS2-style provably fair crash point using exponential distribution"""
# Используем распределение: чем выше множитель, тем меньше шанс
r = random.random()
# Формула: crash_point = 1 / (1 - r) с ограничением
# Это даёт экспоненциальное распределение
crash_point = 1.0 / (1.0 - r * 0.99)
# В редких случаях может быть очень высоким (>1000), ограничиваем
crash_point = min(crash_point, 1000.0)
return round(crash_point, 2)
def get_multiplier_at_time(self, elapsed_seconds: float) -> float:
"""Экспоненциальный рост множителя: e^(t * speed)"""
speed = 0.08 # скорость роста
return round(math.exp(elapsed_seconds * speed), 2)
def get_time_for_multiplier(self, mult: float) -> float:
"""Сколько времени нужно для достижения множителя"""
return math.log(mult) / 0.08
def calculate_payout(self, bet_amount: float, cashout_at: float) -> float:
return round(bet_amount * cashout_at, 2)
def to_dict(self):
return {
"phase": self.phase,
"round": self.round,
"current_multiplier": self.current_multiplier,
"crash_point": self.crash_point,
"timer": self.timer,
"bet_count": len(self.bets),
"total_bets": sum(self.bets.values()),
"cashed_out": {str(k): v for k, v in self.cashed_out.items()},
"history": self.history[-20:],
}
crash_game = CrashGameState()