company_banner

Идеальный скрипт запуска сервера Minecraft



    Автор очень любит игру, и сам является администратором небольшого сервера «чисто для друзей». Как водится среди любителей, на сервере замодировано все, а это влечёт за собой нестабильность работы и как следствие падения. Так как Powershell автор знает лучше, чем расположение магазинов на своей улице, он принял решение сделать «Лучший Скрипт Для Запуска Майнкрафт 2020». Этот же скрипт послужил основой для шаблона в маркетплейсе Ruvds. Но все исходники уже есть в статье. Сейчас по порядку, как это все производилось.

    Нужные нам команды


    Альтернативное логирование


    Однажды поставив еще пару модов я обнаружил, что сервер, судя по всему, падает без объявления войны. Сервер не писал ошибки в latest.log или в debug, а консоль, которая по идее эту ошибку должна была написать и остановиться, была закрыта.

    Не хочет писать – не нужно. У нас есть Powershell с командлетом Tee-Object, который берёт объект и выводит его в файл и в консоль одновременно.

    .\handler.ps1 | Tee-Object .\StandardOutput.txt -Append

    Таким образом, Powershell будет забирать StandardOutput и записывать его в файл. Не пытайтесь использовать Start-Process, потому что он вернет System.ComponentModel.Component, а не StandardOutput, а -RedirectStandardOutput сделает невозможным ввод в консоль, чего мы хотим избежать.

    Аргументы запуска


    Поставив ту самую пару модов, автор заметил, что на сервере к тому же не хватает оперативной памяти. А это нужно менять аргументы запуска. Вместо того чтобы каждый раз менять их в start.bat, который все используют просто используйте этот скрипт.

    Так как Tee-Object читает StandardOutput, только когда исполняемый файл вызывается «Прямо так», придется сделать еще один скрипт. Этот скрипт будет запускать сам майнкрафт. Начнем с аргументов.

    Чтобы в будущем предаваться ультимативной лени, скрипт должен собирать аргументы запуска на лету. Для этого начнем с поиска последней версии forge.

    $forge = ((Get-ChildItem | Where-Object Name -Like "forge*").Name | Sort-Object -Descending) | Select-Object -last 1

    С помощью sort-object мы всегда будем брать объект с самой большой циферкой, сколько бы вы туда их не положили. Ультимативная лень.

    Теперь нужно назначить серверу память. Для этого берем количество системной памяти и записываем его сумму в string.

    $ram = ((Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb)
    $xmx = "-Xms" + $ram + "G"

    Правильный автоматический перезапуск


    Автор видел .bat файлы от других людей, но они не учитывали причину, по которой сервер был остановлен. Это неудобно, что если нужно просто поменять файл мода или удалить что-то?
    Теперь сделаем правильный перезапуск. Автор ранее натыкался на странные скрипты, которые перезапускали сервер не смотря на то, почему сервер завершил работу. Мы же будем использовать exitcode. Java использует 0 как успешное завершение, отсюда и будем плясать.

    Сначала создадим функцию, которая будет перезапускать сервер в случае его неудачного завершения его работы.

    function Get-MinecraftExitCode {
       
        do {
            
            if ($global:Process.ExitCode -ne 0) {
                Write-Log
                Restart-Minecraft
            }
            else {
                Write-Log
            }
     
        } until ($global:Process.ExitCode -eq 0)
        
    }
    

    Скрипт останется в цикле до тех пор, пока сервер из своей же консоли не завершит работу штатно, с помощью команды /stop.

    Если мы все решили автоматизировать, то неплохо бы и собирать дату запуска, завершения, а также, причину завершения.

    Для этого мы записываем результат Start-Process в переменную. В скрипте это выглядит так:

    $global:Process = Start-Process -FilePath  "C:\Program Files (x86)\common files\Oracle\Java\javapath_target_*\java.exe" -ArgumentList "$xmx -server -jar $forge nogui" -Wait -NoNewWindow -PassThru

    А дальше записываем результаты в файл. Вот что возвращается нам в переменную:

    $global:Process.StartTime
    $global:Process.ExitCode	
    $global:Process.ExitTime

    Все это с помощью Add-Content можно добавить в файл. Немного причесав, получаем такой скрипт, а на зовем его handler.ps1.

    Add-Content -Value "Start time:" -Path $Logfile 
    $global:Process.StartTime
     
    Add-Content -Value "Exit code:" -Path $Logfile 
    $global:Process.ExitCode | Add-Content $Logfile
        
    Add-Content -Value "Exit time:" -Path $Logfile 
    $global:Process.ExitTime | Add-Content $Logfile
    

    Теперь давайте оформим скрипт с запуском handler’a.

    Правильная автозагрузка


    Автор хочет одним модулем запускать майнкрафт различных версий из любых путей, а также иметь возможность складывать логи в конкретную папку.

    Проблема заключается в том, что процесс должен запустить пользователь, который находится в системе. Это можно делать через рабочий стол или WinRm. Если запускать сервер от имени системы или даже администратора, но не входить в систему, то Server.jar не сможет даже прочитать eula.txt и запуститься.

    Включить автовход в систему мы можем с помощью добавления трех записей в реестр.

    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -Value $Username -ErrorAction SilentlyContinue
    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -Value $Password  -ErrorAction SilentlyContinue
    New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 1 -ErrorAction SilentlyContinue

    Это небезопасно. Логин и пароль указываются тут плейнтекстом, поэтому под запуск сервера нужно заводить отдельного пользователя, который имеет доступ на уровне пользователя, или в еще более узкой группе. Использовать стандартного администратора для этого категорически не рекомендуется.

    С автовходом разобрались. Теперь нужно зарегистрировать новую таску под сервер. Запускать будем команду из Powershell, поэтому выглядеть это будет так:

    $Trigger = New-ScheduledTaskTrigger -AtLogOn
    $User = "ServerAdmin"
    $PS = New-ScheduledTaskAction -Execute 'PowerShell.exe" -Argument "Start-Minecraft -Type Forge -LogFile "C:\minecraft\stdout.txt" -MinecraftPath "C:\minecraft\"'
    Register-ScheduledTask -TaskName "StartSSMS" -Trigger $Trigger -User $User -Action $PS -RunLevel Highest

    Собираем модуль


    Теперь давайте оформим все в модули, которые можно будет потом использовать. Весь код готовых скриптов тут, импортируйте и пользуйтесь.

    Все описанное выше вы можете использовать отдельно, если не хотите заморачиваться с модулями.

    Start-Minecraft


    Сначала сделаем модуль, который только и будет делать, что запускать скрипт, который будет слушать и записывать standardoutput.

    В блоке параметров он запрашивает из какой папки запускать майнкрафт и куда складывать лог.

    Set-Location (Split-Path $MyInvocation.MyCommand.Path)
    function Start-Minecraft {
        [CmdletBinding()]
        param (
            [Parameter()]
            [ValidateNotNullOrEmpty()]
            [string]
            $LogFile,
     
            [Parameter(Mandatory)]  
            [ValidateSet('Vanilla', 'Forge')]
            [ValidateNotNullOrEmpty()]
            [string]
            $Type,
     
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string[]]
            $MinecraftPath
     
        )
        powershell.exe -file .\handler.ps1 -type $type -MinecraftPath $MinecraftPath | Tee-Object $LogFile -Append
    }
    Export-ModuleMember -Function Start-Minecraft

    А запускать майнкрафт нужно будет так:

    Start-Minecraft -Type Forge -LogFile "C:\minecraft\stdout.txt" -MinecraftPath "C:\minecraft\"

    Теперь перейдем к готовому к употреблению Handler.ps1

    Чтобы наш скрипт мог принимать параметры при вызове, также нужно указывать блок параметров. Обратите внимание, он запускает Oracle Java, если вы используете другой дистрибутив, нужно будет изменить путь до исполняемого файла.

    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$type,
     
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$MinecraftPath,
     
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$StandardOutput
    )
     
    Set-Location $MinecraftPath
     
    function Restart-Minecraft {
     
        Write-host "=============== Starting godlike game server ============"
     
        $forge = ((Get-ChildItem | Where-Object Name -Like "forge*").Name | Sort-Object -Descending) | Select-Object -first 1
     
        $ram = ((Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum /1gb)
        $xmx = "-Xms" + $ram + "G"
        $global:Process = Start-Process -FilePath  "C:\Program Files (x86)\common files\Oracle\Java\javapath_target_*\java.exe" -ArgumentList "$xmx -server -jar $forge nogui" -Wait -NoNewWindow -PassThru
        
    }
     
    function Write-Log {
        Write-host "Start time:" $global:Process.StartTime
     
        Write-host "Exit code:" $global:Process.ExitCode
        
        Write-host "Exit time:" $global:Process.ExitTime
     
        Write-host "=============== Stopped godlike game server ============="
    }
     
    function Get-MinecraftExitCode {
       
        do {
            
            if ($global:Process.ExitCode -ne 0) {
                Restart-Minecraft
                Write-Log
            }
            else {
                Write-Log
            }
     
        } until ($global:Process.ExitCode -eq 0)
        
    }
     
    Get-MinecraftExitCode
    

    Register-Minecraft


    Скрипт, практически, повторяет Start-Minecraft, за исключением того, что только регистрирует новую задачу. Принимает те же самые аргументы. Имя пользователя, если не было указано, берет текущего.

    function Register-Minecraft {
        [CmdletBinding()]
        param (
            [Parameter()]
            [ValidateNotNullOrEmpty()]
            [string]
            $LogFile,
     
            [Parameter(Mandatory)]  
            [ValidateSet('Vanilla', 'Forge')]
            [ValidateNotNullOrEmpty()]
            [string]$Type,
     
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$MinecraftPath,
     
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string]$User,
     
            [Parameter(Mandatory)]
            [string]$TaskName = $env:USERNAME
        )
     
        $Trigger = New-ScheduledTaskTrigger -AtLogOn
        $arguments = "Start-Minecraft -Type $Type -LogFile $LogFile -MinecraftPath $MinecraftPath"
        $PS = New-ScheduledTaskAction -Execute "PowerShell" -Argument "-noexit -command $arguments"
        Register-ScheduledTask -TaskName $TaskName -Trigger $Trigger -User $User -Action $PS -RunLevel Highest
        
    }
     
    Export-ModuleMember -Function Register-Minecraft

    Register-Autologon


    В блоке параметров скрипт принимает параметр Username и Password. Если Username не был указан, используется имя текущего пользователя.

    function Set-Autologon {
     
        param (
            [Parameter(
            HelpMessage="Username for autologon")]
            $Username = $env:USERNAME,
     
            [Parameter(Mandatory=$true,
            HelpMessage="User password")]
            [ValidateNotNullOrEmpty()]
            $Password
        )
     
        $i = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
     
        if ($null -eq $i) {
            New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -Value $Username
            New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -Value $Password 
            New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 1
            Write-Verbose "Set-Autologon will enable user auto logon."
     
        }
        else {
            Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultUserName -Value $Username
            Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name DefaultPassword -Value $Password
            Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 1
        }
     
        
        Write-Verbose "Autologon was set successfully."
     
    }
    

    Запуск этого скрипта выглядит так:

    Set-Autologon -Password "PlaintextPassword"

    Как пользоваться


    Сейчас рассмотрим то, как сам автор пользуется всем этим. Как правильно нужно разворачивать публичный сервер Minecraft на Windows. Начнем с самого начала.

    1. Создаем пользователя

    $pass = Get-Credential
    New-LocalUser -Name "MinecraftServer" -Password $pass.Password -AccountNeverExpires -PasswordNeverExpires -UserMayNotChangePassword

    2. Регистрируем задание по запуску скрипта

    Можете зарегистрировать с помощью модуля, так:

    Register-Minecraft -Type Forge -LogFile "C:\minecraft\stdout.txt" -MinecraftPath "C:\minecraft\" -User "MInecraftServer" -TaskName "MinecraftStarter"

    Или воспользоваться стандартными средствами:

    $Trigger = New-ScheduledTaskTrigger -AtLogOn
    $User = "ServerAdmin"
    $PS = New-ScheduledTaskAction -Execute 'PowerShell.exe" -Argument "Start-Minecraft -Type Forge -LogFile "C:\minecraft\stdout.txt" -MinecraftPath "C:\minecraft\"'
    Register-ScheduledTask -TaskName "StartSSMS" -Trigger $Trigger -User $User -Action $PS -RunLevel Highest

    3. Включаем автовход в систему и перезагружаем машину

    Set-Autologon -Username "MinecraftServer" -Password "Qw3"

    Завершение


    Автор делал скрипт, в том числе и для себя, поэтому, с удовольствием выслушает ваши предложения по улучшению скрипта. Автор надеется, что весь этот код был для вас хотя бы минимально полезен, а статья интересна.

    RUVDS.com
    RUVDS – хостинг VDS/VPS серверов

    Комментарии 19

      +1

      На всякий случай оставлю это здесь.
      https://hub.docker.com/r/itzg/minecraft-server/

        –1
        Зачем нужен Майнкрафт, когда есть Eco?
          +1
          Может затем, что в майнкрафте «всё привычно, всё знакомо»? :-)
            +1
            Ну, так это легко решается накидыванием пары-тройки модов, насколько я знаю.

            Вопрос, конечно, слегка провокационный, потому что Майнкрафт — это всё-таки в первую очередь конструктор, а Эко — песочница про взаимодействие людей.
            +2
            Майнкрафт широк и глубок, в нём кто угодно может найти что-нибудь себе по душе. Отсюда и популярность даже несмотря на кучу проблем (нет, не графика — с ней всё хорошо). Eco до майнкрафта явно не дотягивает.
              –1
              Не соглашусь.

              Как конструктор, в котором можно создавать всякие восьмибитные компьютеры и ваять статуи Иллиданов — безусловно МК вне конкуренции. Как многопользовательская игра — МК рядом не стоял с обалденнейшими социальными механиками Эко.
                0
                Как конструктор, в котором можно создавать всякие восьмибитные компьютеры и ваять статуи Иллиданов — безусловно МК вне конкуренции. Как многопользовательская игра — МК рядом не стоял с обалденнейшими социальными механиками Эко.

                Хм… сомнительное утверждения, учитывая моды :)

                  –1
                  Моды — это всё-таки не часть изначальной концепции. Понятно, что при достаточно многофункциональных и удобных инструментах, можно хоть файтинг, хоть пошаговую стратегию сделать. На мой взгляд — это немного не то, примерно как со Скайримом, где тоже раздолье для моддерства.
              +1

              Потому что манкрафт можно запустить и на линуксе

                –1
                И серверную, и клиентскую часть Эко тоже можно запустить на линуксе. И на OS X даже.
                  0

                  В стиме Эко только для винды. Как так?

                +1
                Тут, наверно, будет уместен старый пост с БАШа )

                Fudzy
                AKG или Koss?

                GolerGkA
                знаешь, я не настолько специалист, чтобы сравнивать сразу две большие фирмы, многие годы конкурирующие и, несомненно, компетентные в свои области, благо по этому поводу наверняка сломано немало копий в баталиях на форумах, до которых я не дорос в своём аудиофильском развитии

                GolerGkA
                но Koss — херня
                  0
                  «Процессор: AMD Ryzen 5 1500X 3.5 GHz Quad-Core or Intel i5-6500 3.2 GHz Quad-Core or similar
                  Оперативная память: 8 GB ОЗУ
                  Видеокарта: AMD Radeon HD 7850 or NVIDIA GeForce GT 640 or Intel HD Graphics 540 or similar (2 GB memory recommended)» — рекомендуемые для Eco, хах.
                  Нет, не нужен нам такой «майнкрафт» имхо.
                    0
                    Так же про всё что угодно можно сказать?
                      0
                      Конечно :) Зачем нужно что угодно, когда есть Эко?
                    +1

                    Наконец-то полезный контент (не сарказм)

                      0
                      Стараемся :-)
                      +1
                      Оставлю для линуксоидов linuxgsm.com :)
                        0
                        Осталось раскрыть тему Docker + forge + windows 10 WSL
                        Ну или хотя бы Docker + forge

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое