nothing happens when i upload a report /deepsite/api/me/projects/new/unknown/update:1 Failed to load resource: the server responded with a status of 500 ()Understand this error
4abf197 verified | // Main application logic | |
| let currentTelemetryData = null; | |
| let currentPlaybackSequence = null; | |
| let playbackInterval = null; | |
| let currentTick = 0; | |
| let canvas, ctx; | |
| // Initialize upload form | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const fileInput = document.getElementById('report-file'); | |
| const fileInfo = document.getElementById('file-info'); | |
| const fileName = document.getElementById('file-name'); | |
| const fileSize = document.getElementById('file-size'); | |
| const analyzeBtn = document.getElementById('analyze-btn'); | |
| const uploadForm = document.getElementById('upload-form'); | |
| // Handle file selection | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length > 0) { | |
| const file = e.target.files[0]; | |
| fileName.textContent = file.name; | |
| fileSize.textContent = `(${(file.size / 1024).toFixed(1)} KB)`; | |
| fileInfo.classList.remove('hidden'); | |
| analyzeBtn.classList.remove('hidden'); | |
| } else { | |
| fileInfo.classList.add('hidden'); | |
| analyzeBtn.classList.add('hidden'); | |
| } | |
| } | |
| function updateStatsOverview() { | |
| if (!currentTelemetryData?.sequences) return; | |
| const sequences = currentTelemetryData.sequences; | |
| let totalShots = 0; | |
| let totalHits = 0; | |
| let totalSuspicion = 0; | |
| sequences.forEach(seq => { | |
| const stats = seq.sequence_stats || {}; | |
| totalShots += stats.total_shots || 0; | |
| totalHits += stats.hits || 0; | |
| totalSuspicion += calculateSuspicionScore(stats); | |
| }); | |
| const avgAccuracy = totalShots > 0 ? (totalHits / totalShots) : 0; | |
| const avgSuspicion = sequences.length > 0 ? (totalSuspicion / sequences.length) : 0; | |
| document.getElementById('total-sequences').textContent = sequences.length; | |
| document.getElementById('avg-accuracy').textContent = Math.round(avgAccuracy * 100) + '%'; | |
| document.getElementById('suspicion-score').textContent = Math.round(avgSuspicion); | |
| document.getElementById('total-shots').textContent = totalShots; | |
| } | |
| function initializeCharts() { | |
| if (!currentTelemetryData?.sequences) return; | |
| const sequences = currentTelemetryData.sequences; | |
| const accuracyData = sequences.map(seq => Math.round((seq.sequence_stats?.accuracy || 0) * 100)); | |
| const suspicionData = sequences.map((seq, index) => ({ | |
| x: index, | |
| y: calculateSuspicionScore(seq.sequence_stats || {}) | |
| })); | |
| // Accuracy distribution chart | |
| const accuracyCtx = document.getElementById('accuracy-chart').getContext('2d'); | |
| new Chart(accuracyCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: accuracyData.map((_, i) => `Seq ${i + 1}`), | |
| datasets: [{ | |
| label: 'Accuracy %', | |
| data: accuracyData, | |
| backgroundColor: 'rgba(59, 130, 246, 0.5)', | |
| borderColor: 'rgba(59, 130, 246, 1)', | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 100, | |
| ticks: { | |
| color: '#9ca3af' | |
| }, | |
| grid: { | |
| color: '#374151' | |
| } | |
| }, | |
| x: { | |
| ticks: { | |
| color: '#9ca3af' | |
| }, | |
| grid: { | |
| color: '#374151' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Suspicion timeline chart | |
| const suspicionCtx = document.getElementById('suspicion-chart').getContext('2d'); | |
| new Chart(suspicionCtx, { | |
| type: 'line', | |
| data: { | |
| datasets: [{ | |
| label: 'Suspicion Score', | |
| data: suspicionData, | |
| backgroundColor: 'rgba(239, 68, 68, 0.1)', | |
| borderColor: 'rgba(239, 68, 68, 1)', | |
| borderWidth: 2, | |
| tension: 0.4, | |
| fill: true | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 100, | |
| ticks: { | |
| color: '#9ca3af' | |
| }, | |
| grid: { | |
| color: '#374151' | |
| } | |
| }, | |
| x: { | |
| ticks: { | |
| color: '#9ca3af' | |
| }, | |
| grid: { | |
| color: '#374151' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function sortSequences(sortBy) { | |
| if (!currentTelemetryData?.sequences) return; | |
| const container = document.getElementById('sequences-container'); | |
| const sequences = [...currentTelemetryData.sequences]; | |
| if (sortBy === 'accuracy') { | |
| sequences.sort((a, b) => (b.sequence_stats?.accuracy || 0) - (a.sequence_stats?.accuracy || 0)); | |
| } else if (sortBy === 'suspicion') { | |
| sequences.sort((a, b) => calculateSuspicionScore(b.sequence_stats || {}) - calculateSuspicionScore(a.sequence_stats || {})); | |
| } | |
| currentTelemetryData.sequences = sequences; | |
| renderSequences(); | |
| } | |
| function exportReport() { | |
| if (!currentTelemetryData) { | |
| alert('No data to export'); | |
| return; | |
| } | |
| const reportData = { | |
| player_name: currentTelemetryData.player_name, | |
| steam_id: currentTelemetryData.steam_id, | |
| timestamp: currentTelemetryData.timestamp, | |
| summary: { | |
| total_sequences: currentTelemetryData.sequences.length, | |
| avg_accuracy: document.getElementById('avg-accuracy').textContent, | |
| suspicion_score: document.getElementById('suspicion-score').textContent, | |
| total_shots: document.getElementById('total-shots').textContent | |
| }, | |
| sequences: currentTelemetryData.sequences.map(seq => ({ | |
| sequence_id: seq.sequence_id, | |
| accuracy: seq.sequence_stats?.accuracy || 0, | |
| hits: seq.sequence_stats?.hits || 0, | |
| total_shots: seq.sequence_stats?.total_shots || 0, | |
| suspicion_score: calculateSuspicionScore(seq.sequence_stats || {}) | |
| })) | |
| }; | |
| const blob = new Blob([JSON.stringify(reportData, null, 2)], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `aimbot-report-${Date.now()}.json`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function resetAnalysis() { | |
| currentTelemetryData = null; | |
| currentPlaybackSequence = null; | |
| stopPlayback(); | |
| document.getElementById('upload-section').classList.remove('hidden'); | |
| document.getElementById('dashboard').classList.add('hidden'); | |
| document.getElementById('report-file').value = ''; | |
| document.getElementById('file-info').classList.add('hidden'); | |
| // Clear charts | |
| Chart.getChart('accuracy-chart')?.destroy(); | |
| Chart.getChart('suspicion-chart')?.destroy(); | |
| } | |
| function showHelp() { | |
| document.getElementById('help-modal').classList.remove('hidden'); | |
| } | |
| function closeHelp() { | |
| document.getElementById('help-modal').classList.add('hidden'); | |
| } | |
| function showSettings() { | |
| alert('Settings panel coming soon!'); | |
| } | |
| function toggleFullscreen() { | |
| const canvas = document.getElementById('playback-canvas'); | |
| if (!document.fullscreenElement) { | |
| canvas.requestFullscreen(); | |
| } else { | |
| document.exitFullscreen(); | |
| } | |
| } | |
| ); | |
| // Handle form submission | |
| uploadForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| if (fileInput.files.length === 0) return; | |
| const file = fileInput.files[0]; | |
| const reader = new FileReader(); | |
| analyzeBtn.disabled = true; | |
| analyzeBtn.innerHTML = 'Analyzing... <i data-feather="loader" class="inline ml-1 w-4 h-4 animate-spin"></i>'; | |
| window.feather.replace(); | |
| reader.onload = (e) => { | |
| try { | |
| const telemetryData = JSON.parse(e.target.result); | |
| initializeApp(telemetryData); | |
| document.getElementById('upload-section').classList.add('hidden'); | |
| document.getElementById('dashboard').classList.remove('hidden'); | |
| window.scrollTo({ | |
| top: 0, | |
| behavior: 'smooth' | |
| }); | |
| } catch (error) { | |
| alert('Error parsing report file. Please ensure it is valid JSON.'); | |
| console.error(error); | |
| } finally { | |
| analyzeBtn.disabled = false; | |
| analyzeBtn.innerHTML = 'Analyze Report <i data-feather="chevron-right" class="inline ml-1 w-4 h-4"></i>'; | |
| window.feather.replace(); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| }); | |
| function initializeApp(telemetryData) { | |
| try { | |
| if (!telemetryData || typeof telemetryData !== 'object') { | |
| throw new Error('Invalid telemetry data'); | |
| } | |
| currentTelemetryData = telemetryData; | |
| // Initialize canvas | |
| canvas = document.getElementById('playback-canvas'); | |
| ctx = canvas.getContext('2d'); | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Render player stats | |
| updatePlayerStats(); | |
| // Update stats overview | |
| updateStatsOverview(); | |
| // Render sequences | |
| renderSequences(); | |
| // Initialize charts | |
| initializeCharts(); | |
| } catch (error) { | |
| console.error('Error initializing app:', error); | |
| alert('Error initializing application. Please check the report file.'); | |
| } | |
| } | |
| function resizeCanvas() { | |
| const container = canvas.parentElement; | |
| canvas.width = container.clientWidth; | |
| canvas.height = container.clientHeight; | |
| } | |
| function updatePlayerStats() { | |
| const playerStats = document.querySelector('custom-player-stats'); | |
| if (playerStats) { | |
| playerStats.setAttribute('name', currentTelemetryData.player_name); | |
| playerStats.setAttribute('steam-id', currentTelemetryData.steam_id); | |
| playerStats.setAttribute('timestamp', new Date(currentTelemetryData.timestamp * 1000).toLocaleString()); | |
| playerStats.setAttribute('sequence-count', currentTelemetryData.sequences.length.toString()); | |
| } | |
| } | |
| function renderSequences() { | |
| const container = document.getElementById('sequences-container'); | |
| container.innerHTML = ''; | |
| if (!currentTelemetryData?.sequences) return; | |
| currentTelemetryData.sequences.forEach((sequence, index) => { | |
| const stats = sequence?.sequence_stats || {}; | |
| const accuracy = stats?.accuracy || 0; | |
| const hits = stats.hits || 0; | |
| const totalShots = stats.total_shots || 0; | |
| const seqElement = document.createElement('div'); | |
| seqElement.className = 'sequence-card bg-gray-700 rounded-lg p-4 cursor-pointer hover:bg-gray-600'; | |
| seqElement.innerHTML = ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <h3 class="font-semibold">Sequence #${index + 1}</h3> | |
| <p class="text-sm text-gray-400">${sequence.sequence_id || 'Unknown ID'}</p> | |
| </div> | |
| <span class="px-2 py-1 rounded text-xs font-medium ${ | |
| accuracy > 0.6 ? 'bg-red-500' : 'bg-blue-500' | |
| }">${Math.round(accuracy * 100)}% accuracy</span> | |
| </div> | |
| <div class="mt-3"> | |
| <div class="flex justify-between text-xs mb-1"> | |
| <span>${hits} hits</span> | |
| <span>${totalShots} shots</span> | |
| </div> | |
| <div class="heatmap mb-2"></div> | |
| <div class="flex justify-between text-xs"> | |
| <span>Suspicion level</span> | |
| <span>${calculateSuspicionScore(stats)}/100</span> | |
| </div> | |
| <div class="suspicion-meter" style="width: ${calculateSuspicionScore(stats)}%"></div> | |
| </div> | |
| `; | |
| seqElement.addEventListener('click', () => { | |
| loadSequenceForPlayback(sequence); | |
| }); | |
| container.appendChild(seqElement); | |
| }); | |
| } | |
| function calculateSuspicionScore(stats = {}) { | |
| // Simple heuristic for suspicion score | |
| let score = 0; | |
| // High accuracy is suspicious | |
| score += Math.min(30, (stats.accuracy || 0) * 50); | |
| // Micro-smoothing detection | |
| score += (stats.micro_smoothing_counts_r2 || 0) * 5; | |
| // Flick hits are suspicious | |
| score += (stats.flick_hits || 0) * 3; | |
| // Teleport shots are very suspicious | |
| score += (stats.teleport_shots_ratio || 0) * 20; | |
| return Math.min(100, Math.round(score)); | |
| } | |
| function loadSequenceForPlayback(sequence) { | |
| stopPlayback(); | |
| currentPlaybackSequence = sequence; | |
| currentTick = 0; | |
| // Highlight selected sequence | |
| document.querySelectorAll('.sequence-card').forEach(card => { | |
| card.classList.remove('active-playback', 'bg-gray-600'); | |
| }); | |
| event.currentTarget.classList.add('active-playback', 'bg-gray-600'); | |
| // Update playback controls | |
| const controls = document.querySelector('custom-playback-controls'); | |
| if (controls) { | |
| controls.setAttribute('duration', sequence.sequence_stats.duration_ticks); | |
| controls.setAttribute('current-tick', '0'); | |
| } | |
| // Render first frame | |
| renderTick(currentTick); | |
| } | |
| function startPlayback() { | |
| if (!currentPlaybackSequence) return; | |
| const tickDuration = 1000 / 30; // 30 ticks per second | |
| let lastTime = performance.now(); | |
| playbackInterval = setInterval(() => { | |
| const now = performance.now(); | |
| const delta = now - lastTime; | |
| lastTime = now; | |
| const ticksToAdvance = Math.floor(delta / tickDuration); | |
| currentTick = Math.min(currentTick + ticksToAdvance, currentPlaybackSequence.sequence_stats.duration_ticks - 1); | |
| renderTick(currentTick); | |
| // Update controls | |
| const controls = document.querySelector('custom-playback-controls'); | |
| if (controls) { | |
| controls.setAttribute('current-tick', currentTick.toString()); | |
| } | |
| if (currentTick >= currentPlaybackSequence.sequence_stats.duration_ticks - 1) { | |
| stopPlayback(); | |
| } | |
| }, tickDuration); | |
| } | |
| function stopPlayback() { | |
| if (playbackInterval) { | |
| clearInterval(playbackInterval); | |
| playbackInterval = null; | |
| } | |
| } | |
| function renderTick(tick) { | |
| if (!currentPlaybackSequence || !canvas) return; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw background grid | |
| drawGrid(); | |
| // Find the tick data (this is simplified - real implementation would interpolate) | |
| const tickData = findTickData(tick); | |
| if (!tickData) return; | |
| // Draw player and aiming data | |
| drawPlayer(tickData); | |
| // If target is in sight, draw target | |
| if (tickData.is_target_in_sight) { | |
| drawTarget(tickData); | |
| } | |
| // Draw shot information if this is a shot tick | |
| if (tickData.shot_fired) { | |
| drawShot(tickData); | |
| } | |
| } | |
| function findTickData(tick) { | |
| // Simplified - in a real implementation you'd interpolate between ticks | |
| const tickStr = tick.toString(); | |
| return currentPlaybackSequence.tick_data[tickStr] || | |
| currentPlaybackSequence.pre_shot_ticks.find(t => t.tick === tick); | |
| } | |
| function drawGrid() { | |
| const gridSize = 50; | |
| const width = canvas.width; | |
| const height = canvas.height; | |
| ctx.strokeStyle = '#333'; | |
| ctx.lineWidth = 1; | |
| // Vertical lines | |
| for (let x = 0; x <= width; x += gridSize) { | |
| ctx.beginPath(); | |
| ctx.moveTo(x, 0); | |
| ctx.lineTo(x, height); | |
| ctx.stroke(); | |
| } | |
| // Horizontal lines | |
| for (let y = 0; y <= height; y += gridSize) { | |
| ctx.beginPath(); | |
| ctx.moveTo(0, y); | |
| ctx.lineTo(width, y); | |
| ctx.stroke(); | |
| } | |
| } | |
| function drawPlayer(tickData) { | |
| const centerX = canvas.width / 2; | |
| const centerY = canvas.height / 2; | |
| // Draw player (simple circle) | |
| ctx.fillStyle = '#3b82f6'; | |
| ctx.beginPath(); | |
| ctx.arc(centerX, centerY, 10, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw aiming direction | |
| const angle = tickData.flick_angle || 0; | |
| const length = 30; | |
| const endX = centerX + Math.cos(angle) * length; | |
| const endY = centerY + Math.sin(angle) * length; | |
| ctx.strokeStyle = '#ffffff'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.moveTo(centerX, centerY); | |
| ctx.lineTo(endX, endY); | |
| ctx.stroke(); | |
| // Draw recoil compensation | |
| if (tickData.recoil_compensation_x || tickData.recoil_compensation_y) { | |
| const compX = tickData.recoil_compensation_x * 20; | |
| const compY = tickData.recoil_compensation_y * 20; | |
| ctx.strokeStyle = '#ef4444'; | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.moveTo(endX, endY); | |
| ctx.lineTo(endX + compX, endY + compY); | |
| ctx.stroke(); | |
| // Draw compensation point | |
| ctx.fillStyle = '#ef4444'; | |
| ctx.beginPath(); | |
| ctx.arc(endX + compX, endY + compY, 3, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } | |
| } | |
| function drawTarget(tickData) { | |
| const centerX = canvas.width / 2 + 100; // Fixed position for demo | |
| const centerY = canvas.height / 2; | |
| // Draw target (circle with crosshair) | |
| ctx.fillStyle = '#ef4444'; | |
| ctx.beginPath(); | |
| ctx.arc(centerX, centerY, 15, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.strokeStyle = '#ffffff'; | |
| ctx.lineWidth = 2; | |
| // Crosshair | |
| const crossSize = 10; | |
| ctx.beginPath(); | |
| ctx.moveTo(centerX - crossSize, centerY); | |
| ctx.lineTo(centerX + crossSize, centerY); | |
| ctx.moveTo(centerX, centerY - crossSize); | |
| ctx.lineTo(centerX, centerY + crossSize); | |
| ctx.stroke(); | |
| // Velocity vector if available | |
| if (tickData.target_speed) { | |
| const velX = Math.cos(0) * tickData.target_speed * 0.1; // Simplified | |
| const velY = Math.sin(0) * tickData.target_speed * 0.1; // Simplified | |
| ctx.strokeStyle = '#f59e0b'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.moveTo(centerX, centerY); | |
| ctx.lineTo(centerX + velX, centerY + velY); | |
| ctx.stroke(); | |
| } | |
| } | |
| function drawShot(tickData) { | |
| const centerX = canvas.width / 2; | |
| const centerY = canvas.height / 2; | |
| const targetX = canvas.width / 2 + 100; | |
| const targetY = canvas.height / 2; | |
| // Draw shot line | |
| ctx.strokeStyle = tickData.shot_hit ? '#10b981' : '#ef4444'; | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.moveTo(centerX, centerY); | |
| ctx.lineTo(targetX, targetY); | |
| ctx.stroke(); | |
| // Draw hit/miss indicator | |
| const indicatorX = targetX; | |
| const indicatorY = targetY; | |
| ctx.fillStyle = tickData.shot_hit ? '#10b981' : '#ef4444'; | |
| ctx.beginPath(); | |
| if (tickData.shot_hit) { | |
| ctx.arc(indicatorX, indicatorY, 8, 0, Math.PI * 2); | |
| } else { | |
| ctx.moveTo(indicatorX - 8, indicatorY - 8); | |
| ctx.lineTo(indicatorX + 8, indicatorY + 8); | |
| ctx.moveTo(indicatorX + 8, indicatorY - 8); | |
| ctx.lineTo(indicatorX - 8, indicatorY + 8); | |
| } | |
| ctx.fill(); | |
| } | |
| // Expose functions to web components | |
| window.playbackControls = { | |
| play: startPlayback, | |
| pause: stopPlayback, | |
| seek: (tick) => { | |
| currentTick = tick; | |
| renderTick(currentTick); | |
| } | |
| }; |