diff --git a/assets/jsons/Action.json b/assets/jsons/Action.json new file mode 100644 index 0000000..dc75ce4 --- /dev/null +++ b/assets/jsons/Action.json @@ -0,0 +1,150 @@ +{ + "185": { + "cast": 20, + "recast": 25 + }, + "188": { + "cast": 0, + "recast": 300 + }, + "3540": { + "cast": 0, + "recast": 900 + }, + "3613": { + "cast": 0, + "recast": 600 + }, + "7385": { + "cast": 0, + "recast": 1200 + }, + "7388": { + "cast": 0, + "recast": 900 + }, + "7405": { + "cast": 0, + "recast": 1200 + }, + "7432": { + "cast": 0, + "recast": 300 + }, + "7535": { + "cast": 0, + "recast": 600 + }, + "7549": { + "cast": 0, + "recast": 900 + }, + "7560": { + "cast": 0, + "recast": 900 + }, + "16012": { + "cast": 0, + "recast": 1200 + }, + "16160": { + "cast": 0, + "recast": 900 + }, + "16471": { + "cast": 0, + "recast": 900 + }, + "16536": { + "cast": 0, + "recast": 1200 + }, + "16538": { + "cast": 0, + "recast": 1200 + }, + "16548": { + "cast": 0, + "recast": 30 + }, + "16556": { + "cast": 0, + "recast": 300 + }, + "16559": { + "cast": 0, + "recast": 1200 + }, + "16889": { + "cast": 0, + "recast": 1200 + }, + "24291": { + "cast": 0, + "recast": 15 + }, + "24292": { + "cast": 0, + "recast": 15 + }, + "24298": { + "cast": 0, + "recast": 300 + }, + "24305": { + "cast": 0, + "recast": 1200 + }, + "24310": { + "cast": 0, + "recast": 1200 + }, + "24311": { + "cast": 0, + "recast": 1200 + }, + "25751": { + "cast": 0, + "recast": 250 + }, + "25789": { + "cast": 0, + "recast": 15 + }, + "25799": { + "cast": 0, + "recast": 600 + }, + "25857": { + "cast": 0, + "recast": 1200 + }, + "25868": { + "cast": 0, + "recast": 1200 + }, + "34685": { + "cast": 0, + "recast": 1200 + }, + "34686": { + "cast": 0, + "recast": 10 + }, + "36920": { + "cast": 0, + "recast": 1200 + }, + "37011": { + "cast": 0, + "recast": 10 + }, + "37025": { + "cast": 0, + "recast": 10 + }, + "37034": { + "cast": 0, + "recast": 15 + } +} diff --git a/scripts/update_action_json.php b/scripts/update_action_json.php new file mode 100644 index 0000000..35aae37 --- /dev/null +++ b/scripts/update_action_json.php @@ -0,0 +1,235 @@ + $ability) { + $id = (int)($ability['extraAbilityGameID'] ?? 0); + if ($id <= 0) { + fwrite(STDERR, 'Skipping mitigation without extraAbilityGameID: ' . $name . PHP_EOL); + continue; + } + + $ids[$id] = true; + } + + if (!$ids) { + fail('No extraAbilityGameID values found in MITIGATION_ABILITIES'); + } + + ksort($ids, SORT_NUMERIC); + return array_keys($ids); +} + +function download_url(string $url): string +{ + $lastError = ''; + $allowInsecureDownload = getenv('FF14_MITIGATOR_INSECURE_DOWNLOAD') === '1'; + + if (function_exists('curl_init')) { + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_CONNECTTIMEOUT => 15, + CURLOPT_TIMEOUT => 120, + CURLOPT_USERAGENT => 'ff14-mitigator-action-cache/1.0', + CURLOPT_SSL_VERIFYPEER => !$allowInsecureDownload, + CURLOPT_SSL_VERIFYHOST => $allowInsecureDownload ? 0 : 2, + ]); + + $body = curl_exec($ch); + $error = curl_error($ch); + $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($body !== false && $status >= 200 && $status < 300) { + return $body; + } + + $lastError = 'cURL HTTP ' . $status . ($error ? ': ' . $error : ''); + } + + $wrappers = stream_get_wrappers(); + if (in_array('https', $wrappers, true)) { + $context = stream_context_create([ + 'http' => [ + 'timeout' => 120, + 'user_agent' => 'ff14-mitigator-action-cache/1.0', + ], + 'ssl' => [ + 'verify_peer' => !$allowInsecureDownload, + 'verify_peer_name' => !$allowInsecureDownload, + ], + ]); + + $body = file_get_contents($url, false, $context); + if ($body !== false) { + return $body; + } + + $lastError = trim($lastError . '; file_get_contents failed', '; '); + } + + if ($lastError !== '') { + fail('Could not download Action.json. ' . $lastError); + } + + fail('This PHP installation has neither cURL nor the HTTPS stream wrapper enabled.'); +} + +function action_field(array $action, string $field): ?int +{ + if (array_key_exists($field, $action) && is_numeric($action[$field])) { + return (int)$action[$field]; + } + + $fields = $action['fields'] ?? null; + if (is_array($fields) && array_key_exists($field, $fields) && is_numeric($fields[$field])) { + return (int)$fields[$field]; + } + + return null; +} + +$actionIds = read_mitigation_action_ids($mitigationSource); +$wanted = array_fill_keys(array_map('strval', $actionIds), true); + +$json = download_url(ACTION_SOURCE_URL); +$actions = json_decode($json, true); + +if (!is_array($actions)) { + fail('Downloaded Action.json is not valid JSON: ' . json_last_error_msg()); +} + +$filtered = []; +foreach ($wanted as $id => $_) { + $action = $actions[$id] ?? null; + if (!is_array($action)) { + fwrite(STDERR, 'Missing action in downloaded Action.json: ' . $id . PHP_EOL); + continue; + } + + $filtered[$id] = [ + 'cast' => action_field($action, 'Cast100ms'), + 'recast' => action_field($action, 'Recast100ms'), + ]; +} + +if (!$filtered) { + fail('No matching mitigation actions found in downloaded Action.json'); +} + +ksort($filtered, SORT_NUMERIC); + +$outputDir = dirname($outputFile); +if (!is_dir($outputDir) && !mkdir($outputDir, 0775, true) && !is_dir($outputDir)) { + fail('Could not create output directory: ' . $outputDir); +} + +$encoded = json_encode($filtered, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); +if ($encoded === false) { + fail('Could not encode filtered Action.json: ' . json_last_error_msg()); +} + +if (file_put_contents($outputFile, $encoded . PHP_EOL, LOCK_EX) === false) { + fail('Could not write output file: ' . $outputFile); +} + +echo 'Saved ' . count($filtered) . ' actions to ' . $outputFile . PHP_EOL;