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


시버리 2 공식 한국 64명 팀 서버 맵 로테이션

Seoul – 64-player Mixed Modes

게임 모드맵 이름맵 특징 1맵 특징 2맵 특징 3
팀 임무Galencourt배 폭파유물 파괴왕 무덤 파괴
팀 임무Rudhelm Siege공성탑 이동텐트 방화메이슨 계승자 살해
팀 데스매치Courtyard안 뜰비무장으로 시작
팀 임무Darkforest호송대바리케이트 방화공작 살해
팀 임무Baudwyn (Bulwark)공성 무기 파괴대포 이동두꺼운 성벽 파괴
팀 데스매치Tournament Grounds토너먼트 경기장때리면 돌아가는 함정
팀 임무Coxwell마을 방화금 약탈병사 학살
팀 임무Lionspire공성추 이동광장 점령트레뷰셋 파괴
팀 데스매치Frozen Wreck눈보라부서지는 얼음바닥
팀 임무Montcrux사막 방어 요새두꺼운 벽 폭파
팀 임무Bridgetown농장 방화시장 파괴귀족 낙사시키기
팀 데스매치Desert사막발리스타
팀 임무Thayic (Stronghold)눈보라투석기로 성벽 파괴아르곤 왕 호위
팀 임무Falmire인질 구출다리 점령챔피언 호위
팀 데스매치Wardenglade잔디안개비 오는 소리
팀 임무Askandir (Library)등대 파괴문화재 파괴도서관 방화
팀 임무Aberfell (Raid)돼지/농부 약탈모노리스 파괴드루이드 살해
팀 데스매치Fighting Pit아레나불 함정중앙 가시 구멍

OpenBSD에 ZeroTier-One VPN 설치하기

이 글은 OpenBSD 7.2 버전과 ZeroTier One 1.10.6 버전을 사용합니다.
글 작성 시점에서 OS와 소프트웨어의 가장 최신 버전입니다.

OS 정보

$ uname -a

OpenBSD openbsd.ourdare.com 7.2 GENERIC.MP#758 amd64

필요한 패키지 설치

$ sudo pkg_add wget gmake

quirks-6.42 signed on 2023-03-21T19:29:38Z
wget-1.21.3:libunistring-0.9.7: ok
wget-1.21.3:libidn2-2.3.0p0: ok
wget-1.21.3:bzip2-1.0.8p0: ok
wget-1.21.3:pcre2-10.37: ok
wget-1.21.3:libpsl-0.21.1: ok
wget-1.21.3: ok
gmake-4.3: ok

ZeroTier-One 소스 다운로드 및 압축 해제

$ wget https://github.com/zerotier/ZeroTierOne/archive/refs/tags/1.10.6.tar.gz

--2023-03-29 14:05:54--  https://github.com/zerotier/ZeroTierOne/archive/refs/tags/1.10.6.tar.gz
github.com (github.com) 해석 중... 20.200.245.247
다음으로 연결 중: github.com (github.com)|20.200.245.247|:443... 연결했습니다.
HTTP 요청을 보냈습니다. 응답 기다리는 중... 302 Found
위치: https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/refs/tags/1.10.6 [따라감]
--2023-03-29 14:05:55--  https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/refs/tags/1.10.6
codeload.github.com (codeload.github.com) 해석 중... 20.200.245.246
다음으로 연결 중: codeload.github.com (codeload.github.com)|20.200.245.246|:443... 연결했습니다.
HTTP 요청을 보냈습니다. 응답 기다리는 중... 200 OK
길이: 15259391 (15M) [application/x-gzip]
저장 위치: `1.10.6.tar.gz'

1.10.6.tar.gz	100%[======================================================================>]  14.55M  6.90MB/s    /  2.1s

2023-03-29 14:05:57 (6.90 MB/s) - `1.10.6.tar.gz' 저장함 [15259391/15259391]



$ tar xzf 1.10.6.tar.gz

빌드 및 설치

$ cd ZeroTierOne-1.10.6



$ gmake && sudo gmake install

서비스 등록 및 시작

/etc/rc.d/zerotier 를 새로 만들어서 아래 내용으로 채우고 저장합니다.

#!/bin/ksh

daemon="/usr/local/sbin/zerotier-one -d"

. /etc/rc.d/rc.subr

rc_cmd $1
$ sudo chmod u+x /etc/rc.d/zerotier



$ sudo rcctl enable zerotier



$ sudo rcctl start zerotier

zerotier(ok)

명령어 사용

$ sudo zerotier-cli info
200 info ㅁㅁㅁㅁㅁㅁㅁㅁㅁㅁ 1.10.6 ONLINE



$ sudo zerotier-cli join <networkid>

OmniOS에서 OpenTTD JGRPP Dedicated 서버 구축하기 (Unix / Oracle Solaris / illumos)

※ 이 글에서는 OmniOS r151038 버전과 JGRPP v0.52.0 버전이 사용되었습니다.
글 작성 시점에서 r151038 버전은 OmniOS LTS 릴리즈의 가장 최신 버전이고, v0.52.0은 JGRPP의 가장 최신 릴리즈입니다.

OS 정보

~$ uname -a



SunOS omnioslts 5.11 omnios-r151038-7bb369b3b5 i86pc i386 i86pc



~$ cat /etc/os-release



NAME="OmniOS"
PRETTY_NAME="OmniOS Community Edition v11 r151038cs"
CPE_NAME="cpe:/o:omniosce:omnios:11:151038:97"
ID=omnios
VERSION=r151038cs
VERSION_ID=r151038cs
BUILD_ID=151038.97.2023.03.11
HOME_URL="https://omnios.org/"
SUPPORT_URL="https://omnios.org/"
BUG_REPORT_URL="https://github.com/omniosorg/omnios-build/issues/new"

필요한 패키지 설치

~$ sudo pkg install gcc10 cmake gnu-make



설치할 패키지:   2
변경할 서비스:   1
부트 환경 만들기: 아니오
백업 부트 환경 만들기: 아니오

다운로드		패키지	파일		XFER(MB)		속도
완료됨		2/2	3199/3199	25.5/25.5		471k/s

단계					항목
작업 설치				3358/3358
패키지 상태 데이터베이스 업데이트		완료
패키지 캐시 업데이트			0/0
이미지 상태 업데이트			완료
빠른 조회 데이터베이스 만들기		완료
검색 색인 읽기				완료
검색 색인 업데이트			2/2
패키지 캐시 업데이트			2/2



~$ gcc --version



gcc (OmniOS 151038/10.4.0-il-1) 10.4.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.



~$ cmake --version



cmake version 3.25.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).



~$ gmake --version



GNU Make 4.3
Built for x86_64-pc-solaris2.11
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

JGRPP 소스 다운로드 및 압축 해제

~$ wget https://github.com/JGRennison/OpenTTD-patches/archive/refs/tags/jgrpp-0.52.0.tar.gz



--2023-03-24 15:02:55--  https://github.com/JGRennison/OpenTTD-patches/archive/refs/tags/jgrpp-0.52.0.tar.gz
Resolving github.com... 20.200.245.247
Connecting to github.com|20.200.245.247|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/JGRennison/OpenTTD-patches/tar.gz/refs/tags/jgrpp-0.52.0 [following]
--2023-03-24 15:02:56--  https://codeload.github.com/JGRennison/OpenTTD-patches/tar.gz/refs/tags/jgrpp-0.52.0
Resolving codeload.github.com... 20.200.245.246
Connecting to codeload.github.com|20.200.245.246|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘jgrpp-0.52.0.tar.gz’

jgrpp-0.52.0.tar.gz                      [          <=>                                                         ]  11.64M  6.13MB/s    in 1.9s

2023-03-24 15:02:58 (6.13 MB/s) - ‘jgrpp-0.52.0.tar.gz’ saved [12207921]



~$ tar xzf jgrpp-0.52.0.tar.gz

소스 파일 수정

압축을 풀어서 나온 소스 폴더로 이동.

~$ cd OpenTTD-patches-jgrpp-0.52.0

CMakeLists.txt 를 텍스트 편집기로 열어 313 줄에 위치한 target_link_libraries 함수 내부에 아래 세 줄을 추가.

${CMAKE_DL_LIBS}
-lsocket
-lnsl


add_dependencies(openttd
    find_version)

target_link_libraries(openttd
    openttd::languages
    openttd::settings
    openttd::media
    openttd::basesets
    openttd::script_api
    openttd::binfiles
    Threads::Threads
    ${CMAKE_DL_LIBS}
    -lsocket
    -lnsl
)

if(HAIKU)
    target_link_libraries(openttd "be" "network" "midi")
endif()

build-dedicated.sh 를 텍스트 편집기로 열어 7 줄 중간에 위치한 make를 gmake로 변경.


cd build
rm CMakeCache.txt
cmake .. -DOPTION_DEDICATED=true && gmake -j$(nproc 2>/dev/null || echo "1")

src/core/alloc_func.hpp 를 텍스트 편집기로 열어 맨 위에 아래 세 줄을 추가.

#ifdef __sun
#include <alloca.h>
#endif
#ifdef __sun
#include <alloca.h>
#endif

/*
 * This file is part of OpenTTD.

src/string_func.h 를 텍스트 편집기로 열어 279 줄 앞에 // 를 추가해 주석 처리.



#       define DEFINE_STRCASESTR
// char *strcasestr(const char *haystack, const char *needle);
#endif /* strcasestr is available */

src/network/core/os_abstraction.cpp 를 텍스트 편집기로 열어 196 줄 중간의 SO_REUSEPORTSO_REUSEADDR 로 변경.



#else
        int reuse_port = 1;
        return setsockopt(d, SOL_SOCKET, SO_REUSEADDR, &reuse_port, sizeof(reuse_port)) == 0;
#endif
}

src/3rdparty/randombytes/randombytes.h 를 텍스트 편집기로 열어 19 줄의 randombytes 함수를 아래와 같이 수정.



/*
 * Write `n` bytes of high quality random bytes to `buf`
 */
int randombytes(unsigned char *buf, size_t n);

#ifdef __cplusplus
}
#endif

src/3rdparty/randombytes/randombytes.c 를 텍스트 편집기로 열어 내용을 모두 삭제한 뒤 아래 내용으로 채우기.

#include "randombytes.h"
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

int randombytes(unsigned char *x, size_t xlen)
{
    int fd;
    ssize_t bytes_read;

    if (xlen == 0) return 0;
    assert(x != NULL);

    fd = open("/dev/urandom", O_RDONLY);
    if (fd == -1) {
        fd = open("/dev/random", O_RDONLY);
    }
    assert(fd != -1);

    bytes_read = read(fd, x, xlen);
    assert(bytes_read == xlen);

    close(fd);

    return 0;
}

src/os/unix/crashlog_unix.cpp 를 텍스트 편집기로 열어 #include <ucontext.h> 한 줄을 25 줄에 위치한 #include <sys/mman.h> 아래에 추가.


#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <ucontext.h>

#if defined(WITH_DBG_GDB)
#include <sys/syscall.h>
#endif /* WITH_DBG_GDB */

같은 파일에서 400, 418 줄에 위치한 gregs[REG_EFL] 두 개를 gregs[REG_RFL] 로 변경.


                        gregs[REG_R14],
                        gregs[REG_R15],
                        gregs[REG_RIP],
                        gregs[REG_RFL]
                );
#elif defined(__i386)
                const gregset_t &gregs = ucontext->uc_mcontext.gregs;
                buffer += seprintf(buffer, last,

                        gregs[REG_EBP],
                        gregs[REG_ESP],
                        gregs[REG_EIP],
                        gregs[REG_RFL]
                );
#endif
#endif

빌드

~$ ./build-dedicated.sh

Base Graphics 다운로드 및 압축 해제

~$ wget https://cdn.openttd.org/opengfx-releases/7.1/opengfx-7.1-all.zip



Resolving cdn.openttd.org... 54.230.167.118, 54.230.167.93, 54.230.167.82, ...
Connecting to cdn.openttd.org|54.230.167.118|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3547160 (3.4M) [application/zip]
Saving to: ‘opengfx-7.1-all.zip’

opengfx-7.1-all.zip                  100%[=====================================================================>]   3.38M  9.53MB/s    in 0.4s

2023-03-24 16:02:24 (9.53 MB/s) - ‘opengfx-7.1-all.zip’ saved [3547160/3547160]



~$ unzip opengfx-7.1-all.zip && mv opengfx-7.1.tar build/baseset/



Archive:  opengfx-7.1-all.zip
  inflating: opengfx-7.1.tar

설치

~$ cd build && sudo gmake install

서버 실행

~$ /usr/local/games/openttd -D

OmniOS에서 마인크래프트 서버 구축하기 (Paper Spigot 1.17 +) (Unix / Oracle Solaris / illumos)

OS 정보

~$ uname -a

SunOS minecraft 5.11 omnios-master-72433da6283 i86pc i386 i86pc

~$ cat /etc/os-release

NAME="OmniOS"
PRETTY_NAME="OmniOS Community Edition v11 r151041"
CPE_NAME="cpe:/o:omniosce:omnios:11:151041:0"
ID=omnios
VERSION=r151041
VERSION_ID=r151041
BUILD_ID=151041.0.2021.11.19
HOME_URL="https://omnios.org/"
SUPPORT_URL="https://omnios.org/"
BUG_REPORT_URL="https://github.com/omniosorg/omnios-build/issues/new"

Java 확인 및 설치

~$ java

-bash: java: command not found

~$ pkg search jdk | awk '/17/'

basename    dir    usr/jdk                   pkg:/runtime/java/openjdk17@17.0.1.12-151041.0
basename    dir    usr/jdk                   pkg:/runtime/java/openjdk17@17.0.1.12-151041.0

~$ sudo pkg install openjdk17

     설치할 패키지:   2
     변경할 조정자:   1
     변경할 서비스:   1
   부트 환경 만들기: 아니오
백업 부트 환경 만들기: 아니오

다운로드                                     패키지            파일     XFER(MB)      속도
완료됨                                      2/2       250/250  139.1/139.1  1.4M/s

단계                                                항목
새 작업 설치                                      624/624
패키지 상태 데이터베이스 업데이트                                완료 
패키지 캐시 업데이트                                      0/0 
이미지 상태 업데이트                                       완료 
빠른 조회 데이터베이스 만들기                                  완료 
검색 색인 읽기                                          완료 
검색 색인 업데이트                                       2/2 
패키지 캐시 업데이트                                      2/2 

~$ java -version

openjdk version "17.0.1" 2021-10-19
OpenJDK Runtime Environment (build 17.0.1+12-omnios-151041)
OpenJDK 64-Bit Server VM (build 17.0.1+12-omnios-151041, mixed mode, sharing)

마인크래프트 디렉터리 생성, Paper Spigot 다운로드 및 실행

~$ mkdir ~/minecraft && cd ~/minecraft

https://papermc.io/downloads 에서 최신버전의 Paper Spigot 다운로드

~/minecraft$ wget https://papermc.io/api/v2/projects/paper/versions/1.17.1/builds/388/downloads/paper-1.17.1-388.jar

--2021-11-26 11:20:47--  https://papermc.io/api/v2/projects/paper/versions/1.17.1/builds/388/downloads/paper-1.17.1-388.jar
Resolving papermc.io... 172.67.72.198, 104.26.13.138, 104.26.12.138, ...
Connecting to papermc.io|172.67.72.198|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 61843877 (59M) [application/java-archive]
Saving to: ‘paper-1.17.1-388.jar’

paper-1.17.1-388.jar                                                                100%[================================================================================================================================================================================================================>]  58.98M  66.0MB/s    in 0.9s    

2021-11-26 11:20:48 (66.0 MB/s) - ‘paper-1.17.1-388.jar’ saved [61843877/61843877]

~/minecraft$ java -Xms14G -Xmx15G -jar paper-1.17.1-388.jar 

Downloading vanilla jar...
Patching vanilla jar...
System Info: Java 17 (OpenJDK 64-Bit Server VM 17.0.1+12-omnios-151041) Host: SunOS 5.11 (amd64)
Loading libraries, please wait...
[11:32:27 ERROR]: Failed to load properties from file: server.properties
[11:32:27 WARN]: Failed to load eula.txt
[11:32:27 INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.

~/minecraft/eula.txt 의 eula=false 부분을 eula=true 로 수정

~/minecraft$ java -Xms14G -Xmx15G -jar paper-1.17.1-388.jar

System Info: Java 17 (OpenJDK 64-Bit Server VM 17.0.1+12-omnios-151041) Host: SunOS 5.11 (amd64)
Loading libraries, please wait...
[11:33:17 INFO]: Environment: authHost='https://authserver.mojang.com', accountsHost='https://api.mojang.com', sessionHost='https://sessionserver.mojang.com', servicesHost='https://api.minecraftservices.com', name='PROD'
[11:33:17 INFO]: Found new data pack file/bukkit, loading it automatically
[11:33:18 INFO]: Reloading ResourceManager: Default, bukkit
[11:33:18 INFO]: Loaded 7 recipes
[11:33:19 INFO]: Starting minecraft server version 1.17.1
[11:33:19 INFO]: Loading properties
[11:33:19 INFO]: This server is running Paper version git-Paper-388 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT) (Git: 22aaf91)
[11:33:19 INFO]: Server Ping Player Sample Count: 12
[11:33:19 INFO]: Using 4 threads for Netty based IO
[11:33:19 INFO]: Default game type: SURVIVAL
[11:33:19 INFO]: Generating keypair
[11:33:19 INFO]: Starting Minecraft server on *:25565
[11:33:19 INFO]: Using default channel type
[11:33:19 INFO]: Paper: Using Java 11 compression from Velocity.
[11:33:19 INFO]: Paper: Using Java cipher from Velocity.
[11:33:20 INFO]: Preparing level "world"
[11:33:22 WARN]: Unable to find spawn biome
[11:33:23 WARN]: Unable to find spawn biome
[11:33:23 INFO]: Preparing start region for dimension minecraft:overworld
[11:33:23 INFO]: Preparing spawn area: 0%
[11:33:23 INFO]: Time elapsed: 289 ms
[11:33:23 INFO]: Preparing start region for dimension minecraft:the_nether
[11:33:23 INFO]: Time elapsed: 170 ms
[11:33:23 INFO]: Preparing start region for dimension minecraft:the_end
[11:33:23 INFO]: Time elapsed: 69 ms
[11:33:23 INFO]: Running delayed init tasks
[11:33:23 INFO]: Done (4.384s)! For help, type "help"
[11:33:23 INFO]: Timings Reset
> 

※ 실행 명령어의 -Xms14G/-Xmx15G 부분을 서버 환경에 맞게 수정하여 할당할 최소 메모리와 최대 메모리 설정

HordePlusLite – 모드하우 서버 전용 모드 (개발 중단)

캐쥬얼한 바닐라 호드 플레이를 제공하는 서버 전용 모드

mod.io 사이트가 중국 정부에 의해 중국에서의 접근이 금지된 이후로, 모드 서버 운영에 큰 난관을 맞닥뜨리게 된 중국인 친구 Cabbageman3와 그가 운영하는 QQ 그룹의 회원들의 방황을 보다 못해 만들게 된 모드입니다.

서버 측에만 설치하는 모드이다 보니 바닐라 서버로 인식이 되며, 플레이어들은 별다른 다운로드 없이 서버에 접속해서 모드의 기능들을 즐길 수 있습니다.

설치

1. zip 파일로 압축 된 모드 다운로드

2. 압축을 풀어서 나온 HordePlusLite.pak 파일을 해당 서버 디렉토리로 이동

리눅스 : Mordhau/Content/Paks
윈도우즈 : Mordhau\Content\Paks

3. 아래 경로에 위치한 Game.ini 파일을 텍스트 편집기로 열기

리눅스 : Mordhau/Saved/Config/LinuxServer/Game.ini
윈도우즈 : Mordhau\Saved\Config\WindowsServer\Game.ini

4. 아래의 한 줄을

SpawnServerActorsOnMapLoad=/Game/Mordhau/Maps/HordePlusLite/BP_HordePlusLite.BP_HordePlusLite_C

이 밑에 추가

[/Script/Mordhau.MordhauGameMode]

지원되는 맵 리스트

  • HRD_Camp
  • HRD_Castello
  • HRD_Feitoria
  • HRD_Grad
  • HRD_MountainPeak
  • HRD_Taiga
  • HRD_Crossroads

기능 리스트

  • 각 웨이브 보상 골드가 1.5배 늘어남
  • 방패막기를 유지하는 적들은 100%의 추가 피해를 받음
  • 닌자들은 어떤 데미지든 입으면 즉시 사망
  • 토마토 적에게서 화염병 제거
  • 플레이어들이 구매할 수 있는 퍽
    • 닷지
    • 플레시 운드
    • 반쪽짜리 블러드러스트 (근접공격으로 적을 죽일 시 체력 50 회복)
    • 반쪽짜리 퓨리 (근접공격으로 적을 죽일 시 스태미나 50 회복)
  • 랜덤 상자에 아래 무기들 추가
    • Lute
    • Jug
    • Stool
    • Carrot
    • Ancient Falx
    • Ogre Club
    • Seymour’s Longsword
    • Great War Axe
  • 랜덤 상자에서 응가 제거
  • 철이나 쇠로 된 무기의 둔기 부분으로 휘두를 시 적의 방어를 뚫고 피해를 줌
    • Longsword Alt
    • Rusty Longsword Alt
    • Estoc Alt
    • Heavy Hand Axe Alt
    • Pole Axe Alt
    • Blacksmith Hammer
    • Frying Fan
    • Sledgehammer
  • 랜덤 상자의 리스폰 시간 단축 (5초 -> 3초)
  • 킬 수에 따른 보너스
    • 50킬 이상 : 5% 이동속도 보너스
    • 100킬 이상 : 3단 점프
    • 150킬 이상 : 10% 이동속도 보너스
    • 200킬 이상 : 초당 5 스태미나 회복
    • 250킬 이상 : 15% 이동속도 보너스
    • 300킬 이상 : 초당 3 생명력 회복
    • 350킬 이상 : 20% 이동속도 보너스
    • 400킬 이상 : 공격중에 공격당해도 멈추지 않음

모드하우 맵 스크린샷들

King DespacitoMetaMod에 바닐라 맵을 서브 레벨링 한 맵들을 등록시키기 위해 찍은 스크린샷들이지만 해당 모드가 업데이트되면서 바닐라 맵을 서브 레벨링 한 맵들에 대해 따로 사진을 등록할 필요가 없게 되어서 그냥 여기 올려보아요.

OmniOS에서 마인크래프트 서버 구축하기 (Paper Spigot ~1.16.x) (Unix / Oracle Solaris / illumos)

※ 마인크래프트 1.17 버전 이후는 최신 글을 확인하세요.

OS 정보

~$ uname -a

SunOS omni 5.11 omnios-master-b7ba24aaa1 i86pc i386 i86pc

~$ cat /etc/os-release

NAME="OmniOS"
PRETTY_NAME="OmniOS Community Edition v11 r151035"
CPE_NAME="cpe:/o:omniosce:omnios:11:151035:0"
ID=omnios
VERSION=r151035
VERSION_ID=r151035
BUILD_ID=151035.0.2020.09.26

Java 확인 및 설치

~$ java -version

-bash: java: command not found

~$ sudo pkg install jdk

Packages to install: 2 Mediators to change: 1 Services to change: 1 Create boot environment: No
Create backup boot environment: No
DOWNLOAD PKGS FILES XFER (MB) SPEED
Completed 2/2 371/371 101.1/101.1 1.0M/s
PHASE ITEMS
Installing new actions 537/537
Updating package state database Done
Updating package cache 0/0
Updating image state Done
Creating fast lookup database Done
Reading search index Done
Updating search index 2/2
Updating package cache 2/2

~$ java -version

openjdk version "1.8.0_265"
OpenJDK Runtime Environment (build 1.8.0_265-omnios-151035-b01)
OpenJDK 64-Bit Server VM (build 25.265-b01, mixed mode)

마인크래프트 디렉터리 생성 및 실행

~$ mkdir ~/minecraft && cd ~/minecraft

https://papermc.io/downloads 에서 다운로드 받은 서버 파일을 ~/minecraft 디렉터리로 이동

~/minecraft$ java -Xmx1G -jar paper-209.jar

Downloading vanilla jar…
Failed to download vanilla jar
javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at sun.security.ssl.Alerts.getSSLException(Alerts.java:214)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1967)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1924)
at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1907)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1423)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1400)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268)
at java.net.URL.openStream(URL.java:1068)
at io.papermc.paperclip.Paperclip.checkVanillaJar(Paperclip.java:190)
at io.papermc.paperclip.Paperclip.checkPaperJar(Paperclip.java:121)
at io.papermc.paperclip.Paperclip.setupEnv(Paperclip.java:94)
at io.papermc.paperclip.Paperclip.main(Paperclip.java:48)
Caused by: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at sun.security.validator.PKIXValidator.(PKIXValidator.java:104)
at sun.security.validator.Validator.getInstance(Validator.java:181)
at sun.security.ssl.X509TrustManagerImpl.getValidator(X509TrustManagerImpl.java:318)
at sun.security.ssl.X509TrustManagerImpl.checkTrustedInit(X509TrustManagerImpl.java:179)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:193)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1670)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:226)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1082)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:1010)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1079)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1388)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1416)
… 11 more
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200)
at java.security.cert.PKIXParameters.(PKIXParameters.java:120)
at java.security.cert.PKIXBuilderParameters.(PKIXBuilderParameters.java:104)
at sun.security.validator.PKIXValidator.(PKIXValidator.java:102)
… 23 more

https://www.oracle.com/kr/java/technologies/javase/javase-jdk8-downloads.html 에서

jdk-8u261-solaris-x64.tar.gz 파일 다운로드 및 압축 풀기

서버 /etc/ssl/java/cacerts 파일을 압축 풀어서 나온 /jdk-8u261-solaris-x64/jdk1.8.0_261/jre/lib/security/cacerts 파일로 대체

~/minecraft$ java -Xmx1G -jar paper-209.jar

Downloading vanilla jar…
Patching vanilla jar…
System Info: Java 1.8 (OpenJDK 64-Bit Server VM 25.265-b01) Host: SunOS 5.11 (amd64)
Loading libraries, please wait…
[16:23:35 ERROR]: Failed to load properties from file: server.properties
[16:23:35 WARN]: Failed to load eula.txt
[16:23:36 INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.

~/minecraft/eula.txt 의 eula=false 부분을 eula=true 로 수정

~/minecraft$ java -Xmx1G -jar paper-209.jar

※ – 실행 명령어의 -Xmx1G부분에서 할당할 메모리 변경
4GB의 경우 -Xmx4G, 16GB의 경우 -Xmx16G