[LR2] 今更開発するLR2IRのライバルDB取得ツール、プレイ曲数1万とかの人の「ライバルデータのダウンロードに失敗しました。」時に使う想定(ChatGPTに作ってもらいました) [BMS]
背景
LR2のライバル機能は、ライバルのスコア更新件数が8000件程度になると取得に失敗します。(本noteのサムネ画像のようになります)
8000という数字は自分で検証したわけではないのではっきりとしたことは分かりませんが、LR2IRでそのような記載を見かけました。
試しに、プレイした曲数が7500程度の方をライバルに登録してみたところ、正常に取得できましたのでやはり8000程度がボーダーなのだと思います。
GLAssistというツールにも、独自にライバルDBを更新する機能はありますが「\LR2files\Rival」にライバルのデータがない状態で新規に取得するためには小細工をしなければならないと思いますし、現在開発や配布は停止しているツールだと思います。
そこで、需要はほぼないと思いますが、私のプレイ曲数が14,000を超えたときに、そういえばこんな不具合があったなと急に思い出したので、表題のツールをChatGPTに作ってもらいました。
プロンプト
以下のプロンプトを入力して、概ね動きそうなPowerShell 7用のスクリプトが得られました。しかし、そのままでは動かなかったので、5往復くらい修正の依頼をしてようやく完成に至りました。
PowerShell 7で次の操作を行うスクリプトを作ってください。
sqlite3.exeも利用します。
1. まず以下のURLにアクセスしてXML形式のデータを取得してください。
${RLR2ID}の部分は、スクリプトの冒頭に定数として定義して自由に変えられるようにしてください。
今回は119698を仮の設定値としてください。
http://www.dream-pro.info/~lavalse/LR2IR/2/getplayerxml.cgi?id=${RLR2ID}
2. 取得できるXMLの形式は以下のようになっています。
<?xml version="1.0" encoding="shift_jis"?>
<scorelist>
<score>
<hash>37dbd73d0c1d8d64e3f56acef6114b36</hash>
<clear>5</clear>
<notes>273</notes>
<combo>273</combo>
<pg>233</pg>
<gr>36</gr>
<gd>4</gd>
<bd>0</bd>
<pr>0</pr>
<minbp>0</minbp>
<option>0</option>
<lastupdate>1721937846</lastupdate>
</score>
<score>
<hash>621ad2a006a4d39a57749c5f8dcc11b6</hash>
<clear>4</clear>
<notes>1530</notes>
<combo>897</combo>
<pg>1236</pg>
<gr>279</gr>
<gd>7</gd>
<bd>4</bd>
<pr>8</pr>
<minbp>10</minbp>
<option>0</option>
<lastupdate>1732719080</lastupdate>
</score>
</scorelist>
<rivalname>アミメキリン</rivalname>
このうち、<score></score>内を各行としたCSV形式に変換してください。
XMLの各項目とCSVのheaderは以下のように対応させてください。
hash ⇒ hash
clear ⇒ r_clear
notes ⇒ r_totalnotes
combo ⇒ r_maxcombo
pg ⇒ r_perfect
gr ⇒ r_great
gd ⇒ r_good
bd ⇒ r_bad
pr ⇒ r_poor
minbp ⇒ r_minbp
option ⇒ r_option
lastupdate ⇒ r_lastupdate
3. sqlite3を用いて、以下のCREATE文で.dbを作成してください。ファイル名は、スクリプトの${RLR2ID}を参照し、「119698.db」のようにしてください。
CREATE TABLE rival(hash TEXT primary key,r_clear INTEGER,r_totalnotes INTEGER,r_maxcombo INTEGER,r_perfect INTEGER,r_great INTEGER,r_good INTEGER,r_bad INTEGER,r_poor INTEGER,r_minbp INTEGER,r_option INTEGER,r_lastupdate INGEGER)
4. 3で作成した.dbに2で作成したCSVをインポートしてください。
5. 最後に「ライバルデータのダウンロードに成功しましたID:${RLR2ID}」と表示してください。
その他要望。
・XMLの取得など、try-catchでエラーがcatchされた際は、「ライバルデータのダウンロードに失敗しましたID:${RLR2ID}」と表示してください。
完成したスクリプト、使い方
まず「PowerShell 7」と「sqlite3.exe」が必要なので以下の記事の該当部分を参考に入手してください。
スクリプトの「# 設定用」の定数2つを書き換えてください。
あとは実行すれば、Get-LR2IRRivalDB.ps1と同じディレクトリに、ライバルのスコアDBと.lr2folderが出力されます。これを「\LR2files\Rival」内に入れればLR2から読み込まれるはずです。
注意
パスに日本語を含む場所ではうまく動きません。
おそらくsqlite3.exeの仕様なのですが、.importコマンドの"FILE"引数に渡すファイル名に日本語が含まれているとうまく動きません。パスの区切り文字は「\」「/」のどちらでも大丈夫なようです。
# これは「PowerShell 7」で動作確認したスクリプトです
# 設定用
$RLR2ID = 119698 # ライバルのLR2ID、0埋めしないこと 例 000001 → 1
$SQLITE_PATH = "D:\BIN\SQLite\sqlite-tools-win-x64-3460100\sqlite3.exe" # sqlite3.exe のパス
# 各種定数
$BASE_URL = "http://www.dream-pro.info/~lavalse/LR2IR/2/getplayerxml.cgi?id=${RLR2ID}"
$DB_FILE_NAME = "${PSScriptRoot}\${RLR2ID}.db"
$TEMP_XML_FILE = "${PSScriptRoot}\temp.xml"
$TEMP_CSV_FILE = "${PSScriptRoot}\temp.csv"
# .lr2folderテンプレートの定義
$LR2FOLDER_TEMPLATE = @"
#COMMAND __RIVAL__
#MAXTRACKS ${RLR2ID}
#CATEGORY ライバルフォルダ
#TITLE {rivalName}
#INFORMATION_A {rivalName}のプレイした曲を表示します
#INFORMATION_B
"@
# 設定が有効か確認
try {
@($SQLITE_PATH) | ForEach-Object { Get-Command $_ -ErrorAction Stop } | Out-Null
}
catch {
Write-Host "[Error] 設定値がおかしいです" -BackgroundColor Red
Write-Host "スクリプトが完了しました。Enterキーを押すとウィンドウを閉じます。"; Read-Host
exit
}
try {
# 1. XMLデータの取得
Write-Host "XMLデータを取得しています..."
Invoke-WebRequest -Uri $BASE_URL -OutFile $TEMP_XML_FILE
# 2. 不正な文字(#)を除去
Write-Host "不正な文字を除去しています..."
$xmlContent = Get-Content $TEMP_XML_FILE -Encoding "shift_jis" -Raw # -Rawを使わずに改行文字ごとに分割して配列にするとかなり遅くなる
# 先頭の不正な文字(#)を除去
$xmlContent = $xmlContent.TrimStart('#')
# 最終行の削除(XMLでは必ず1つのルート要素でなければならないが、<scorelist>と<rivalname>が並列してしまっている)
$xmlLines = $xmlContent -split "`n" # 行ごとに分割
$rivalName = $xmlLines[$xmlLines.Length - 2].TrimStart('<rivalname>').TrimEnd('</rivalname>') # ライバル名を取得
$xmlLines = $xmlLines[0..($xmlLines.Length - 3)] # 最終行を除外
$xmlContent = $xmlLines -join "`n" # 行を再度結合
# 3. XML文字列をロード
$xmlDocument = New-Object System.Xml.XmlDocument
$xmlDocument.LoadXml($xmlContent)
# 4. XMLを解析しCSV形式に変換
Write-Host "XMLを解析中..."
$csvHeader = "hash,r_clear,r_totalnotes,r_maxcombo,r_perfect,r_great,r_good,r_bad,r_poor,r_minbp,r_option,r_lastupdate"
$csvLines = @(
$csvHeader
foreach ($score in $xmlDocument.scorelist.score) {
"$($score.hash),$($score.clear),$($score.notes),$($score.combo),$($score.pg),$($score.gr),$($score.gd),$($score.bd),$($score.pr),$($score.minbp),$($score.option),$($score.lastupdate)"
}
) -join "`r`n"
# 5. 一時CSVファイルに保存
Write-Host "CSVデータを生成中..."
Set-Content -Path $TEMP_CSV_FILE -Value $csvLines -Encoding UTF8
# 6. SQLiteデータベースの作成
Write-Host "SQLiteデータベースを作成中..."
& $SQLITE_PATH $DB_FILE_NAME "CREATE TABLE rival(hash TEXT primary key, r_clear INTEGER, r_totalnotes INTEGER, r_maxcombo INTEGER, r_perfect INTEGER, r_great INTEGER, r_good INTEGER, r_bad INTEGER, r_poor INTEGER, r_minbp INTEGER, r_option INTEGER, r_lastupdate INTEGER);"
# 7. CSVデータをSQLiteにインポート
Write-Host "CSVデータをインポート中..."
& $SQLITE_PATH $DB_FILE_NAME ".import --csv --skip 1 $TEMP_CSV_FILE rival"
# .lr2folder出力
Write-Host ".lr2folder出力中..."
$lr2folderContent = $LR2FOLDER_TEMPLATE -replace "{rivalName}", $rivalName
$lr2folderContent | Out-File -LiteralPath "${PSScriptRoot}\${RLR2ID}.lr2folder" -Encoding "shift_jis"
# 8. 処理成功メッセージ
Write-Host "ライバルデータのダウンロードに成功しましたID:${RLR2ID}"
# 9. 一時ファイルを削除
Remove-Item $TEMP_XML_FILE, $TEMP_CSV_FILE -Force
}
catch {
# エラーハンドリング
Write-Host "ライバルデータのダウンロードに失敗しましたID:${RLR2ID}" -ForegroundColor Red
Write-Host "エラー内容:" -ForegroundColor Yellow
Write-Host $_.Exception
}
Write-Host "スクリプトが完了しました。Enterキーを押すとウィンドウを閉じます。"; Read-Host
感想
ChatGPTとの対話の中で、PowerShellでの文字コードの取り扱いや、sqlite3.exeのコマンドに若干怪しいところはありましたが、ちょっと直せば動く程度の出力が得られたのでやはり便利だなと思いました。
とはいえ、パフォーマンスの面ではfor文の中で文字列や配列を結合する際に「+=」演算子を使うようなスクリプトが出てきてしまうことが多いので、そこはPowerShellの特性に合わせて「-join」演算子を使うように手直ししないと、パフォーマンスが大幅に悪化してしまうので注意が必要だと思いました。
あとなんか、XMLの解析部分が遅いような気がしますが、遅いといっても数秒位ですし、手軽なスクリプトということで仕方ないのかなと思います。数十人単位でライバルデータを取得する場合にはPowerShell以外で作った方が良いかもしれません。