LestradeMoran's picture
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
Raw
History Blame Contribute Delete
20.1 kB
// 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);
}
};