카테고리 보관물: 파워셸

파워셸 관련 카테고리.

시버리 2 Listplayers 정리기

이 프로그램은 Chivalry 2에서 플레이어 데이터를 정리하기 위한 유저 친화적인 그래픽 인터페이스를 제공합니다. 플레이어 정보 관리, 닉네임 변경 추적, 특정 플레이어 우선 순위 지정 등의 과정을 간소화합니다.

주요 기능:

  • 그래픽 사용자 인터페이스: GUI 프로그램은 접근성과 유저 친화성을 높입니다. 인터페이스에는 데이터 처리, Pinned Player 불러오기, 변경 사항 저장을 위한 버튼뿐만 아니라 Pinned Player를 표시하고 편집하기 위한 텍스트 박스가 포함되어 있습니다.
  • 플레이어 데이터베이스 업데이트: Chivalry2PlayersDatabase.log라는 새 파일을 자동으로 업데이트하거나 생성합니다. PlayFabPlayerId 및 관련 닉네임, 마지막 업데이트 시간을 명확하고 구조화된 형식으로 플레이어를 정리합니다.
  • 닉네임 변경 추적: 각 PlayFabPlayerId에 대한 닉네임의 기록을 유지하여 시간이 지남에 따라 플레이어 닉네임의 변경 사항을 추적할 수 있습니다.
  • Pinned Player 우선 순위 지정: Chivalry2PinnedPlayers.log에 나열된 플레이어는 우선 순위가 주어지며 Chivalry2PlayersDatabase.log의 상단에 “*Pinned Player” 표시와 함께 배치됩니다. 이는 특정 플레이어를 추적하는 데 특히 유용합니다.
  • 실시간 클립보드 처리: “Process ListPlayers” 버튼을 사용하면 클립보드에서 직접 플레이어 데이터를 처리할 수 있어 수동 파일 관리의 필요성을 없앱니다. 이 기능은 특히 활발한 게임 세션 중에 효율성과 사용 용이성을 위해 설계되었습니다.

사용 방법:

메인 GUI 창
1. Chivalry 2 서버에서 물결 키(~)를 눌러 콘솔을 열고 “listplayers”를 입력한 다음 엔터 키를 누릅니다.
2. 창에 있는 “Process ListPlayers” 버튼을 클릭하여 정리된 데이터를 얻습니다.
3. 오른쪽 텍스트 박스에 PlayFabPlayerId를 입력합니다(한 줄당 하나의 PlayFabPlayerId). 그리고 “Save Pinned Players”를 클릭합니다.
4. 클립보드에 listplayers가 있을 때 “Process ListPlayers” 버튼을 다시 클릭하면 Pinned Player가 상단에 표시됩니다.
여러 개의 닉네임이 기록된 플레이어들

1.35 버전의 변경 사항

  • 닉네임 처리 방법을 개선했습니다.

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) {
        if ($player -match '^(?<Nickname>.+?)\s+-\s+(?<PlayFabPlayerId>[A-F0-9]{10,16})\s+-') {
            $Nickname = $matches['Nickname'].Trim()
            $PlayFabPlayerId = $matches['PlayFabPlayerId'].Trim()

            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.35 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({
    Process-ListPlayers
    $textBox.Text = Get-Content -Path .\Chivalry2PlayersDatabase.log -Encoding UTF8 -Raw
})

$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.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부터 배치됩니다.