Log Parser Architecture
ゲームログを解析して試合情報(BAN/PICK / 勝敗 / 統計)を抽出するエンジンの構成。code-split-and-coverage spec で 3 層 + finalize + statistics に分割した。
ファイル構成
src/utils/logAnalyzer/
├── LogParser.ts # 一括解析モード(バッチ用 facade)
├── StreamLogParser.ts # 旧ストリーム実装(互換のため残置)
├── CharacterService.ts # キャラ ID ↔ 名前変換
├── characterRegistry.ts # いま使うキャラデータ(同梱 or 配信されたリソースパック)
├── toDbMatch.ts # MatchData → DB 保存形式(全経路で共通の変換)
├── matchId.ts # ログ由来の試合 ID / 日付 / 時刻
├── placeholderUid.ts # UID が分からないプレイヤーの仮 UID
├── pickOrder.ts # pick が何番目に確定したか / BP の席
├── parserVersion.ts # 解析器の版(matches.log_parser_version)
├── banTarget.ts # BAN がどのプレイヤーの所持キャラに向いたか
├── team.ts # チームの DB 表現
├── StatisticsCalculator.ts # 統計集計の facade
├── statistics/
│ └── aggregate.ts # 純関数の試合統計集計(spec で抽出)
├── core/
│ ├── LogParserCore.ts # 一括解析オーケストレータ
│ ├── StreamLogParserV2.ts # ストリーム解析(リアルタイム用、StreamFsm 内蔵)
│ ├── line/
│ │ └── LineParser.ts # 純粋な行パーサ(regex / 抽出関数 / MARKER)
│ ├── fsm/
│ │ └── StreamFsm.ts # phase の状態機械
│ ├── aggregator/
│ │ └── Aggregator.ts # picks / bans / players の集約
│ └── finalize/
│ └── finalizeMatchData.ts # 集計結果の最終整形
├── battleType/ # BattleType の数値 ↔ 文字列マッピング
├── matchFilters.ts
├── timeUtils.ts
└── types.tsscripts/analyze-log/ は CLI 解析スクリプト群(同じエンジンを使う薄いラッパ)。
レイヤー構成
| 層 | ファイル | 責務 | 依存 |
|---|---|---|---|
| line | core/line/LineParser.ts | 1 行を ParsedLine に変換する純粋関数群(regex / 抽出関数 / MARKER) | なし |
| fsm | core/fsm/StreamFsm.ts | phase の状態保持・遷移・onChange リスナー | line(型のみ) |
| aggregator | core/aggregator/Aggregator.ts | 重複排除 / ソート / Normal 戦キャラ抽出 | line(型のみ)/ CharacterServiceLike (DI) |
| finalize | core/finalize/finalizeMatchData.ts | rank / type 推定 / 整形 | aggregator |
| statistics | statistics/aggregate.ts | 試合配列から KPI を集計(純関数) | finalize 後の MatchData 配列 |
状態機械(StreamFsm)
リアルタイム解析時の phase 遷移。StreamLogParserV2.ts が内蔵する。
各状態の役割:
| 状態 | 入力で受け付ける主なマーカー | 出力 / 次状態 |
|---|---|---|
IDLE | 試合開始系マーカー | BP へ |
BP | BP数据日志 (Rank), プレイヤー BAN/PICK | aggregator に push |
LOADING | 匹配成功开始加载战斗 | INGAME へ(タイムアウトすれば IDLE) |
INGAME | ON GAME START, ON BATTLE END PUSH | RESULT へ |
RESULT | upload sdk battle player | MatchData を確定し IDLE へ |
実遷移表は core/fsm/StreamFsm.ts の transition() メソッドに 明示的に列挙 されている。
ログマーカー
BP フェーズ関連
| マーカー | 説明 | 出現条件 |
|---|---|---|
BP数据日志 | BAN/PICK 詳細(プレイヤー名 / キャラ ID 等) | Rank のみ |
匹配成功开始加载战斗 | マッチング成功 → BP フェーズ終了 | 両方 |
PageBattleBanPick2V2.prefab | BP 画面 UI ログ | Rank のみ |
試合進行関連
| マーカー | 説明 |
|---|---|
ON GAME START | ゲーム開始 |
ON BATTLE END PUSH | バトル終了 |
upload sdk battle player | 試合結果(プレイヤー名・勝敗) |
マッチタイプマーカー
| マーカー | 説明 |
|---|---|
TYPE:Rank_2v2 | ランク 2v2 |
TYPE:Rank_1v1 | ランク 1v1 |
TYPE:Normal_2v2 | ノーマル 2v2 |
TYPE:Normal_1v1 | ノーマル 1v1 |
TYPE:Custom | カスタム |
Rank 判定ロジック(finalize/finalizeMatchData.ts)
集計後 (Aggregator 出力時点) は rank/normal 判定が確定していないことがある。finalizeMatchData で以下の 優先順位 で確定する。
| 優先度 | 判定基準 | 信頼度 |
|---|---|---|
| 1 | TYPE: マーカー(試合結果の前 500 行以内) | 確定 |
| 2 | BP区間内に BP数据日志 がある | Rank と推定 |
| 3 | プレイヤー数(2 = 1v1, 4 = 2v2)+ 既定 Normal | フォールバック |
一括解析 vs ストリーム解析
| 種別 | エントリ | 用途 |
|---|---|---|
| 一括 | LogParser → LogParserCore | 既存ログファイルの解析(DB インポート時) |
| ストリーム | StreamLogParserV2 | リアルタイムログ監視 (LogWatcherService) |
両方とも内部では同じ line / aggregator / finalize を使う。違いは fsm の有無のみ:
DB 保存形式への変換(toDbMatch)
解析結果(MatchData)を DB の 1 試合分(matches / players / bans / picks)に変えるのは toDbMatch.ts の toDbMatch() だけ。 リアルタイム記録(LogWatcherService)・起動時取り込み(electron/utils/parseLogFiles.ts)・ログ解析タブ(useMatchConverter)の 3 経路が同じ関数を通るので、同じログは同じ行になる。
- 試合 ID:
matchId.tsの${date} ${time}_${type}。既存 DB との互換のため形式は変えない。ID が違っても同じ試合 (どちらもログ由来・同じ日付で時刻差 ±3 秒以内・参加者名の集合が同じ)は保存時に 1 行へまとめる(electron/database/match/equivalentLogMatch.ts) - 仮 UID(
placeholderUid.ts): ログで UID が取れないプレイヤーは 2 形式だけ。自分の名前(プロファイルの名前)と一致すればplayer:<名前>、それ以外はUnknown。Recorder の本人判定・プロファイル照合はplayer:を見る。旧形式unknown_<n>は起動時の移行 (placeholderUidMigration.ts)で書き換えるが、判定では今も仮 UID として扱う - pick 順・席(
pickOrder.ts): ログの「被选序号」は使えないので、確定した pick が初めて出た BP ダンプの時刻順に番号を付ける。 同じダンプで出た人は同順位(例: 1, 2, 3, 3)。時刻の分からない pick には付けない。BP の席(P1〜P4)と TeamSort も保存する (players のbp_position/team_sort/pick_order、picks のpick_order) - 解析器の版(
parserVersion.ts): 行ごとにmatches.log_parser_versionを残す。保存済みの行も直したい解析器の修正をしたらLOG_PARSER_VERSIONを 1 つ上げる。書き手(Recorder / own-writer Manager)の次の起動で、版の古いログ試合をログファイルから 読み直し、ログ由来の列だけを更新する(electron/database/match/logReanalysis.ts、呼び出しはelectron/main/startupLogReanalysis.ts)。 API の戦績詳細が合流した列は触らない。ログファイルが残っていない試合はそのまま
Statistics 集計(純関数)
statistics/aggregate.ts は MatchData[] を入力に取る純関数群。StatisticsCalculator の facade から呼ばれる。
主な集計関数:
aggregateMatchStatistics(matches, ...)— プレイヤー / キャラ pick・ban / 対戦相手分析をまとめて返すemptyStatistics()— 空集計の初期値
純関数なので vitest で簡単にテストできる(集計の挙動は core/__tests__/ のシナリオ系テストでカバー)。
CharacterService
キャラの ID / DB 名 / 画像名 の対応を管理する。nameAliases で DB 名と画像ファイル名のずれを吸収する。
キャラデータの持ち主は characterRegistry.ts(プロセスに 1 つ)。既定は同梱の src/data/characters.json(キーはキャラ ID = ログの ROLE <n>)で、 配信されたリソースパックが届いたら丸ごと差し替える。引数なしの new CharacterService() はコピーを持たずに毎回 registry を読むので、 差し替え後も作り直さずに新しいキャラが引ける(引数で DB を渡したときはそれを固定で使う。テスト・比較用)。
新規キャラ追加時の手順は Add New Character を参照。
テスト
テストは各層のソースと同一ディレクトリに colocate している。
| 層 | テストファイル | 主なケース |
|---|---|---|
| line | core/line/LineParser.test.ts | regex の table-driven |
| fsm | core/fsm/StreamFsm.test.ts | 全遷移を網羅 |
| aggregator | core/aggregator/Aggregator.test.ts | fixture ベース |
| finalize | core/finalize/finalizeMatchData.test.ts | rank/type 推定 |
| 一括 | core/LogParserCore.test.ts / __tests__/LogParserCore.test.ts | 大規模 fixture |
| ストリーム | core/StreamLogParserV2.test.ts + .regression.test.ts + .casual.test.ts | 統合シナリオ |
CLI ツール
yarn analyze-log # デフォルト UID で解析
npx tsx scripts/analyze-log/index.ts "path/to/log.txt"
# 特定マーカー確認
grep -n "BP数据日志" scripts/testData/.../Log_*.txt | head -10
grep -n "TYPE:" scripts/testData/.../Log_*.txt | head -30解析結果の出力内容
試合基本情報
- 試合 ID / タイムスタンプ
- マッチタイプ(
Rank_2v2/Normal_1v1等) - ゲーム開始・終了時刻 / duration
プレイヤー情報
- 名前 / UID
- 勝敗 / チーム(A or B)
- 使用キャラ / 所持キャラ枠
BAN/PICK 情報(Rank のみ)
- BAN されたキャラ + BAN したプレイヤー
- PICK されたキャラ + PICK したプレイヤー
統計情報(statistics/aggregate)
- プレイヤー別 勝敗数・勝率
- キャラ別 BAN/PICK 回数 / 勝率
- 対戦相手分析
関連ドキュメント
- 全体構成: Source Architecture
- DB スキーマ: Database Design
- データ統合: Data Integration Inventory
Last verified: 2026-10-24 / commit e111fbeb