이 프로그램은 Chivalry 2에서 플레이어 데이터를 정리하기 위한 유저 친화적인 그래픽 인터페이스를 제공합니다. 플레이어 정보 관리, 닉네임 변경 추적, 특정 플레이어 우선 순위 지정 등의 과정을 간소화합니다.
주요 기능:
- 그래픽 사용자 인터페이스: GUI 프로그램은 접근성과 유저 친화성을 높입니다. 인터페이스에는 데이터 처리, Pinned Player 불러오기, 변경 사항 저장을 위한 버튼뿐만 아니라 Pinned Player를 표시하고 편집하기 위한 텍스트 박스가 포함되어 있습니다.
- 플레이어 데이터베이스 업데이트: Chivalry2PlayersDatabase.log라는 새 파일을 자동으로 업데이트하거나 생성합니다. PlayFabPlayerId 및 관련 닉네임, 마지막 업데이트 시간을 명확하고 구조화된 형식으로 플레이어를 정리합니다.
- 닉네임 변경 추적: 각 PlayFabPlayerId에 대한 닉네임의 기록을 유지하여 시간이 지남에 따라 플레이어 닉네임의 변경 사항을 추적할 수 있습니다.
- Pinned Player 우선 순위 지정: Chivalry2PinnedPlayers.log에 나열된 플레이어는 우선 순위가 주어지며 Chivalry2PlayersDatabase.log의 상단에 “*Pinned Player” 표시와 함께 배치됩니다. 이는 특정 플레이어를 추적하는 데 특히 유용합니다.
- 실시간 클립보드 처리: “Process ListPlayers” 버튼을 사용하면 클립보드에서 직접 플레이어 데이터를 처리할 수 있어 수동 파일 관리의 필요성을 없앱니다. 이 기능은 특히 활발한 게임 세션 중에 효율성과 사용 용이성을 위해 설계되었습니다.
사용 방법:
1.36 버전의 변경 사항
- 처리하는 동안 처리 버튼이 비활성화 됩니다.
PowerShell
# By vintertid from Discord
Add-Type -AssemblyName PresentationFramework
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
$consoleWindow = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consoleWindow, 0)
function Process-ListPlayers {
$currentTime = Get-Date -Format "yyyy-MM-dd-HH:mm:ss UTCK"
Add-Type -AssemblyName System.Windows.Forms
$content = [System.Windows.Forms.Clipboard]::GetText()
$listplayersDir = ".\listplayers"
if (-not (Test-Path $listplayersDir)) {
New-Item -Path $listplayersDir -ItemType Directory
}
$filename = "listplayers_" + (Get-Date -Format "yyyy-MM-dd-HH-mm-ss") + ".log"
$filepath = Join-Path $listplayersDir $filename
if ($content -match "^ServerName - .*\r?\nName - PlayFabPlayerId - EOSPlayerId - Score - Kills - Deaths - Ping\r?\n(.+\r?\n)+") {
Set-Content -Path $filepath -Value $content -Encoding UTF8
} else {
return
}
if (-not $content -match '\b[A-F0-9]{10,16}\b') { return }
$players = $content -split "\r?\n" | Where-Object { $_ -match '^.+ - \b[A-F0-9]{10,16}\b - ' }
if (-not $players) { return }
$existingPlayers = @{}
$updatedPlayerIds = @()
$pinnedPlayers = @()
if (Test-Path .\Chivalry2PinnedPlayers.log) {
$pinnedPlayers = Get-Content -Path .\Chivalry2PinnedPlayers.log -Encoding UTF8
}
if (Test-Path .\Chivalry2PlayersDatabase.log) {
$existingContent = Get-Content -Path .\Chivalry2PlayersDatabase.log -Encoding UTF8
$currentId = $null
foreach ($line in $existingContent) {
if ($line -match '^\*Pinned Player$') {
$isPinned = $true
continue
}
if ($line -match '^[\w-]{13,16}$') {
$currentId = $line
$existingPlayers[$currentId] = @{ 'Nicknames' = @(); 'IsPinned' = $isPinned; 'TimeData' = 'no-time-data-old-version' }
$isPinned = $false
} elseif ($line -match 'listplayered (.+)$') {
$existingPlayers[$currentId]['TimeData'] = $matches[1]
} elseif ($line -match '^\s+-\s+(.*)') {
if (-not $existingPlayers[$currentId]['Nicknames'].Contains($matches[1])) {
$existingPlayers[$currentId]['Nicknames'] += $matches[1]
}
}
}
}
foreach ($player in $players) {
$playerInfo = $player.Split(' - ')
$Nickname = $playerInfo[-1].Trim()
$PlayFabPlayerId = $playerInfo[-2].Trim()
if ($PlayFabPlayerId -eq "NULL") {
continue
}
if ($PlayFabPlayerId -and $existingPlayers.ContainsKey($PlayFabPlayerId)) {
if (-not $existingPlayers[$PlayFabPlayerId]['Nicknames'].Contains($Nickname)) {
$existingPlayers[$PlayFabPlayerId]['Nicknames'] += $Nickname
}
$existingPlayers[$PlayFabPlayerId]['TimeData'] = $currentTime
} elseif ($PlayFabPlayerId) {
$existingPlayers[$PlayFabPlayerId] = @{ 'Nicknames' = @($Nickname); 'IsPinned' = $false; 'TimeData' = $currentTime }
}
$updatedPlayerIds += $PlayFabPlayerId
}
foreach ($existingPlayer in $existingPlayers.Keys) {
if ($existingPlayers[$existingPlayer]['IsPinned'] -and $pinnedPlayers -notcontains $existingPlayer) {
$existingPlayers[$existingPlayer]['IsPinned'] = $false
}
}
foreach ($id in $pinnedPlayers) {
if ($id -and $existingPlayers.ContainsKey($id)) {
$existingPlayers[$id]['IsPinned'] = $true
}
}
$pinnedContent = @()
$mostNicknamesContent = @()
$mostRecentUpdatesContent = @()
$restContent = @()
foreach ($id in $existingPlayers.Keys) {
$nicknames = $existingPlayers[$id]['Nicknames'] -join "`n - "
$timeData = $existingPlayers[$id]['TimeData']
if ($updatedPlayerIds -contains $id -and $timeData -eq 'no-time-data-old-version') {
$timeData = $currentTime
}
if ($existingPlayers[$id]['IsPinned']) {
$entry = "-----------------------------------`n*Pinned Player`n$id`n - $nicknames`nlistplayered $timeData"
$pinnedContent += $entry
} elseif ($existingPlayers[$id]['Nicknames'].Count -gt 1) {
$entry = "-----------------------------------`n$id`n - $nicknames`nlistplayered $timeData"
$mostNicknamesContent += $entry
} elseif ($timeData -eq $currentTime) {
$entry = "-----------------------------------`n$id`n - $nicknames`nlistplayered $timeData"
$mostRecentUpdatesContent += $entry
} else {
$entry = "-----------------------------------`n$id`n - $nicknames`nlistplayered $timeData"
$restContent += $entry
}
}
$mostNicknamesContent = $mostNicknamesContent | Sort-Object { ($_ -split "`n").Count } -Descending
$newContent = $pinnedContent + $mostNicknamesContent + $mostRecentUpdatesContent + $restContent
$newContent -join "`n" | Set-Content -Path .\Chivalry2PlayersDatabase.log -Encoding UTF8
}
$window = New-Object System.Windows.Window
$window.Title = "Chivalry 2 listplayers Organizer 1.37 by vintertid from Discord"
$window.Width = 1200
$window.Height = 800
$mainGrid = New-Object System.Windows.Controls.Grid
$column1 = New-Object System.Windows.Controls.ColumnDefinition
$column2 = New-Object System.Windows.Controls.ColumnDefinition
$mainGrid.ColumnDefinitions.Add($column1)
$mainGrid.ColumnDefinitions.Add($column2)
$stackPanel = New-Object System.Windows.Controls.StackPanel
$stackPanel2 = New-Object System.Windows.Controls.StackPanel
$processButton = New-Object System.Windows.Controls.Button
$processButton.Content = "Process ListPlayers"
$processButton.HorizontalAlignment = "Center"
$processButton.VerticalAlignment = "Top"
$processButton.Margin = New-Object System.Windows.Thickness(10)
$processButton.Add_Click({
$processButton.IsEnabled = $false
Process-ListPlayers
$textBox.Text = Get-Content -Path .\Chivalry2PlayersDatabase.log -Encoding UTF8 -Raw
$processButton.IsEnabled = $true
})
$timeTextBlock = New-Object System.Windows.Controls.TextBlock
$timeTextBlock.HorizontalAlignment = "Center"
$timeTextBlock.Margin = New-Object System.Windows.Thickness(10)
$timer = New-Object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]::FromSeconds(1)
$timer.Add_Tick({
$timeTextBlock.Text = "Current Time: " + (Get-Date -Format "yyyy-MM-dd-HH:mm:ss UTCK")
})
$timer.Start()
$textBox = New-Object System.Windows.Controls.TextBox
$textBox.VerticalScrollBarVisibility = "Visible"
$textBox.HorizontalAlignment = "Stretch"
$textBox.Height = 500
$textBox.Margin = New-Object System.Windows.Thickness(10)
$textBox.IsReadOnly = $true
$searchBox = New-Object System.Windows.Controls.TextBox
$searchBox.HorizontalAlignment = "Stretch"
$searchBox.Margin = New-Object System.Windows.Thickness(10)
$searchBox.Height = 20
$searchButton = New-Object System.Windows.Controls.Button
$searchButton.Content = "Search"
$searchButton.HorizontalAlignment = "Center"
$searchButton.Margin = New-Object System.Windows.Thickness(10)
$searchButton.Add_Click({
$searchText = $searchBox.Text
if ([string]::IsNullOrWhiteSpace($searchText)) { return }
$startIndex = $textBox.Text.IndexOf($searchText, $textBox.SelectionStart + $textBox.SelectionLength, [System.StringComparison]::OrdinalIgnoreCase)
if ($startIndex -eq -1) {
$startIndex = $textBox.Text.IndexOf($searchText, [System.StringComparison]::OrdinalIgnoreCase)
if ($startIndex -eq -1) { return }
}
$textBox.Select($startIndex, $searchText.Length)
$textBox.Focus()
$textBox.ScrollToLine($textBox.GetLineIndexFromCharacterIndex($startIndex))
})
$textBox2 = New-Object System.Windows.Controls.TextBox
$textBox2.VerticalScrollBarVisibility = "Visible"
$textBox2.HorizontalAlignment = "Stretch"
$textBox2.Height = 500
$textBox2.Margin = New-Object System.Windows.Thickness(10)
$textBox2.AcceptsReturn = $true
$loadButton = New-Object System.Windows.Controls.Button
$loadButton.Content = "Load Pinned Players"
$loadButton.HorizontalAlignment = "Center"
$loadButton.VerticalAlignment = "Top"
$loadButton.Margin = New-Object System.Windows.Thickness(10)
$loadButton.Add_Click({
if (Test-Path .\Chivalry2PinnedPlayers.log) {
$textBox2.Text = Get-Content -Path .\Chivalry2PinnedPlayers.log -Raw
}
})
$saveButton = New-Object System.Windows.Controls.Button
$saveButton.Content = "Save Pinned Players"
$saveButton.HorizontalAlignment = "Center"
$saveButton.VerticalAlignment = "Top"
$saveButton.Margin = New-Object System.Windows.Thickness(10)
$saveButton.Add_Click({
$textBox2.Text | Set-Content -Path .\Chivalry2PinnedPlayers.log -Encoding UTF8
})
$stackPanel.Children.Add($processButton)
$stackPanel.Children.Add($timeTextBlock)
$stackPanel.Children.Add($textBox)
if (Test-Path .\Chivalry2PlayersDatabase.log) {
$textBox.Text = Get-Content -Path .\Chivalry2PlayersDatabase.log -Encoding UTF8 -Raw
}
$stackPanel.Children.Add($searchBox)
$stackPanel.Children.Add($searchButton)
$stackPanel.SetValue([System.Windows.Controls.Grid]::ColumnProperty, 0)
$stackPanel2.Children.Add($textBox2)
if (Test-Path .\Chivalry2PinnedPlayers.log) {
$textBox2.Text = Get-Content -Path .\Chivalry2PinnedPlayers.log -Raw
} else {
New-Item -Path .\Chivalry2PinnedPlayers.log -ItemType File
}
$stackPanel2.Children.Add($loadButton)
$stackPanel2.Children.Add($saveButton)
$stackPanel2.SetValue([System.Windows.Controls.Grid]::ColumnProperty, 1)
$mainGrid.Children.Add($stackPanel)
$mainGrid.Children.Add($stackPanel2)
$window.Content = $mainGrid
$window.ShowDialog()
↓ 오래된 버전
1.35 버전의 변경 사항
- 닉네임 처리 방법을 개선했습니다.
1.34 버전의 변경 사항
- Process ListPlayers 버튼을 누를 시 클립보드의 listplayers 원본 데이터를 저장합니다.
- PlayFabPlayerId 처리 방법을 개선했습니다.
1.32 버전의 변경 사항
- 닉네임에 큰따옴표가 있는 플레이어가 기록되지 않는 문제를 해결했습니다.
1.31 버전의 변경 사항
- 검색 기능은 알파벳의 대소문자를 구분하지 않습니다.
- “Process ListPlayers” 버튼 하단에 현재 시간을 표시합니다.
1.3 버전의 변경 사항
- 그래픽 창과 버튼이 추가되었습니다.
↓ 레거시 버전
이 PowerShell 스크립트는 Chivalry 2 게임의 플레이어 데이터를 효과적으로 정리하고, 사용자가 이름 변경을 추적하고 특정 플레이어를 모니터링할 수 있도록 설계되었습니다.
이 스크립트는 두 가지 주요 로그 파일과 상호 작용합니다:
Chivalry2ListPlayers.log: 현재 플레이어 목록, 플레이어 닉네임 및 고유 PlayFabPlayerId를 포함합니다.
ServerName - example-server--beginner-10p--chivalry-2-live--12345678-1234-1234-1234-1234567890ab 192.0.2.1:52760
Name - PlayFabPlayerId - EOSPlayerId - Score - Kills - Deaths - Ping
<Nickname1> - <PlayFabPlayerId1> - -2030777152 - 250 - 5 - 1 ms
<Nickname2> - <PlayFabPlayerId2> - -2030777152 - 300 - 6 - 2 ms
...
Chivalry2PinnedPlayers.log (선택 사항): 데이터베이스에서 가장 위쪽에 고정하고자 하는 플레이어의 PlayFabPlayerId를 나열합니다. 해당 플레이어는 데이터베이스에 “*Pinned Player” 라인이 추가됩니다.
<PlayFabPlayerId1>
<PlayFabPlayerId2>
<PlayFabPlayerId3>
...
스크립트의 기능은 다음과 같습니다:
- 플레이어 데이터베이스 업데이트: Chivalry2PlayersDatabase.log라는 새 파일을 업데이트하거나 생성하여 플레이어를 PlayFabPlayerId 및 관련 닉네임으로 구조화된 형식으로 정리합니다.
- 닉네임 변경 추적: 각 PlayFabPlayerId에 대해, 스크립트는 닉네임 기록을 유지하여 시간이 지남에 따른 플레이어 닉네임 변경을 추적합니다.
- 고정 플레이어 지정: Chivalry2PinnedPlayers.log에 나열된 플레이어는 Chivalry2PlayersDatabase.log 파일 상단에 배치되며, “*Pinned Player” 상태를 강조하는 특별한 표시로 표시됩니다. 이 기능은 관리자나 게임 내 친구 또는 주목할 만한 개인을 밀접하게 모니터링하고자 하는 사용자에게 특히 유용합니다.
1.2 버전의 변경 사항
- 가장 많은 닉네임을 가진 PlayFabPlayerId 다음에는 가장 최근에 업데이트가 된 PlayFabPlayerId부터 배치됩니다.
- listplayers 출력으로 데이터베이스를 업데이트 할 시 listplayers 출력에 포함된 PlayFabPlayerId에는 현재 시간이 기록됩니다.
1.1 버전의 변경 사항
- Pinned Player 다음에는 가장 많은 닉네임을 가진 PlayFabPlayerId부터 배치됩니다.