'Method not allowed']); exit; } if (empty($_SESSION['access_token'])) { echo json_encode(['reauth' => true]); exit; } if (($_SESSION['token_expires'] ?? 0) <= time()) { echo json_encode(['reauth' => true]); exit; } $reportCode = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['report_code'] ?? ''); $fightId = (int)($_POST['fight_id'] ?? 0); $startTime = (float)($_POST['start_time'] ?? 0); $endTime = (float)($_POST['end_time'] ?? 0); $playerName = trim($_POST['player_name'] ?? ''); $eventType = trim($_POST['event_type'] ?? ''); $abilityId = (int)($_POST['ability_id'] ?? 0); $limit = max(1, min(500, (int)($_POST['limit'] ?? 20))); $language = strtolower(trim($_POST['language'] ?? 'en')); $language = in_array($language, ['en', 'de', 'fr', 'jp'], true) ? $language : 'en'; $translate = 'true'; $startOffset = (float)($_POST['start_offset'] ?? 0) * 1000; // s → ms $endOffset = isset($_POST['end_offset']) && $_POST['end_offset'] !== '' ? (float)$_POST['end_offset'] * 1000 : null; $allowedTypes = ['DamageTaken', 'DamageDone', 'Healing', 'Casts', 'Buffs', 'Deaths']; $dataType = in_array($_POST['data_type'] ?? '', $allowedTypes) ? $_POST['data_type'] : 'DamageTaken'; if (!$reportCode || !$fightId) { http_response_code(400); echo json_encode(['error' => 'Missing params']); exit; } $queryStart = $startTime + $startOffset; $queryEnd = $endOffset !== null ? $startTime + $endOffset : $endTime; $queryEnd = min($queryEnd, $endTime); $token = $_SESSION['access_token']; function localized_graphql_uri(string $language): string { $host = [ 'de' => 'de.fflogs.com', 'fr' => 'fr.fflogs.com', 'jp' => 'ja.fflogs.com', ][$language] ?? 'www.fflogs.com'; return preg_replace('#https://[^/]+#', 'https://' . $host, GRAPHQL_URI); } function dbg_gql(string $query): array { global $token, $language; $acceptLanguage = $language === 'jp' ? 'ja' : $language; $ch = curl_init(localized_graphql_uri($language)); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['query' => $query]), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token, 'Accept-Language: ' . $acceptLanguage], CURLOPT_SSL_VERIFYPEER => !DEV_MODE, ]); $body = curl_exec($ch); curl_close($ch); return json_decode($body, true) ?? []; } // Resolve player name → actor IDs (source + target) $playerIds = []; if ($playerName !== '') { $pd = dbg_gql(<< $ab['name'], 'type' => $ab['type'] ?? null]; } // Filter by raw event type, player (source OR target), then apply limit // Enrich each event with ability meta (name + type) from masterData $filtered = []; foreach ($events as $ev) { if ($eventType !== '' && ($ev['type'] ?? '') !== $eventType) continue; if ($abilityId > 0 && (int)($ev['abilityGameID'] ?? 0) !== $abilityId) continue; if (!empty($playerIds)) { $srcId = (int)($ev['sourceID'] ?? -1); $tgtId = (int)($ev['targetID'] ?? -1); if (!in_array($srcId, $playerIds) && !in_array($tgtId, $playerIds)) continue; } // Inject ability meta so ability.type is visible directly on the event $gid = (int)($ev['abilityGameID'] ?? 0); if ($gid && isset($abilityMeta[$gid])) { $ev['_ability'] = $abilityMeta[$gid]; } $filtered[] = $ev; if (count($filtered) >= $limit) break; } echo json_encode([ 'data_type' => $dataType, 'event_type' => $eventType ?: null, 'ability_id' => $abilityId ?: null, 'player_name' => $playerName ?: null, 'player_ids' => $playerIds ?: null, 'time_range' => ['from_ms' => (int)$queryStart, 'to_ms' => (int)$queryEnd], 'total_before_limit' => count($events), 'count' => count($filtered), 'ability_meta' => $abilityMeta, // vollständige Lookup-Tabelle: gameID → {name, type} 'events' => $filtered, ], JSON_PRETTY_PRINT);