The perfect Minecraft server startup script

The perfect Minecraft server startup script

The author loves the game very much, and he himself is the administrator of a small server “purely for friends”. As usual among amateurs, everything on the server is modded, and this entails instability of work and, as a result, a fall. Since the author knows Powershell better than the location of stores on his street, he decided to do "Best Script To Launch Minecraft 2020". The same script served as the basis for the template in Ruvds marketplace. But all the sources are already in the article. Now, in order, how it was all done.

The commands we need

Alternative logging

Once I installed a couple more mods, I found that the server, apparently, was crashing without declaring war. The server did not write errors to latest.log or debug, and the console, which, in theory, was supposed to write this error and stop, was closed.

If you don't want to write, don't. We have Powershell with a cmdlet Tee-Object, which takes an object and outputs it to a file and to the console at the same time.

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

So Powershell will pick up the StandardOutput and write it to a file. Don't try to use start-process, because it will return a System.ComponentModel.Component, not a StandardOutput, and -RedirectStandardOutput will make it impossible to type into the console, which is what we want to avoid.

Launch Arguments

Having installed the same pair of mods, the author noticed that the server also did not have enough RAM. And it is necessary to change the launch arguments. Instead of changing them every time in start.bat, which everyone uses, just use this script.

Since Tee-Object reads StandardOutput only when the executable is called "Just like that", one more script will have to be done. This script will run minecraft itself. Let's start with the arguments.

In order to indulge in ultimate laziness in the future, the script must collect launch arguments on the fly. To do this, let's start by looking for the latest version forge.

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

With sort-object , we will always take the object with the largest number, no matter how many you put there. Ultimate laziness.

Now we need to assign memory to the server. To do this, we take the amount of system memory and write its amount to string.

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

Proper automatic restart

The author has seen .bat files from other people, but they didn't take into account the reason why the server was stopped. It's inconvenient, what if you just need to change the mod file or delete something?
Now let's do a proper restart. The author previously came across strange scripts that restarted the server regardless of why the server exited. We will use exitcode. Java uses 0 as success, so let's dance from there.

First, let's create a function that will restart the server if it fails.

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

The script will remain in the loop until the server exits normally from its own console using the /stop command.

If we decided to automate everything, then it would be nice to collect the date of launch, end, and also the reason for the end.

To do this, we write the result of Start-Process to a variable. In a script it looks like this:

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

And then write the results to a file. Here is what is returned to us in a variable:

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

All this can be added to the file using Add-Content. Having brushed a little, we get such a script, but we call it 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

Now let's design the script with the launch of the handler.

Proper autoload

The author wants to run minecraft of various versions from any path with one module, and also be able to put logs in a specific folder.

The problem is that the process must be started by a user who is logged in. This can be done through the desktop or WinRm. If you run the server as a system or even an administrator, but do not log in, then Server.jar will not even be able to read eula.txt and start.

We can enable autologon by adding three entries to the registry.

New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultUserName -Value $Username -ErrorAction SilentlyContinue
New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultPassword -Value $Password  -ErrorAction SilentlyContinue
New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name AutoAdminLogon -Value 1 -ErrorAction SilentlyContinue

It is not safe. The login and password are indicated here by the plaintext, therefore, to start the server, you need to create a separate user who has access at the user level, or in an even narrower group. Using the standard administrator for this is strongly discouraged.

We figured out the autologin. Now you need to register a new task for the server. We will run the command from Powershell, so it will look like this:

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

Assembling the module

Now let's arrange everything in modules that can be used later. All code of ready-made scripts is here, import and use.

You can use everything described above separately if you don't want to bother with modules.

start minecraft

First, let's make a module that will do nothing but run a script that will listen and write to standardoutput.

In the parameter block, he asks from which folder to launch minecraft and where to put the log.

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

And you will need to run minecraft like this:

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

Now let's move on to the ready-to-use Handler.ps1

In order for our script to accept parameters when called, we also need to specify a parameter block. Please note that it runs Oracle Java, if you are using a different distribution, you will need to change the path to the executable file.

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 filesOracleJavajavapath_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

The script is practically the same as Start-Minecraft, except that it only registers a new task. Accepts the same arguments. The username, if not specified, takes the current one.

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

In the parameter block, the script accepts the Username and Password parameters. If Username is not specified, the current user's name is used.

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:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon"
 
    if ($null -eq $i) {
        New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultUserName -Value $Username
        New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultPassword -Value $Password 
        New-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name AutoAdminLogon -Value 1
        Write-Verbose "Set-Autologon will enable user auto logon."
 
    }
    else {
        Set-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultUserName -Value $Username
        Set-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name DefaultPassword -Value $Password
        Set-ItemProperty -Path "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersionWinlogon" -Name AutoAdminLogon -Value 1
    }
 
    
    Write-Verbose "Autologon was set successfully."
 
}

Running this script looks like this:

Set-Autologon -Password "PlaintextPassword"

How to use

Now consider how the author himself uses all this. How to properly deploy a Minecraft public server on Windows. Let's start from the very beginning.

1. Create a user

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

2. Register the task to run the script

You can register with a module like so:

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

Or use the standard tools:

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

3. Turn on autologin and reboot the machine

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

Completion

The author made the script, including for himself, therefore, he will be happy to listen to your suggestions for improving the script. The author hopes that all this code was at least minimally useful for you, and the article is interesting.

The perfect Minecraft server startup script

Source: habr.com

Add a comment