The Ideal Script for Launching a Minecraft Server

The Ideal Script for Launching a Minecraft Server

The author is a big fan of the game and is even an administrator of a small server 'just for friends'. As is common among enthusiasts, everything is modded on the server, which leads to instability and, consequently, crashes. Since the author knows Powershell better than the layout of stores on their street, they decided to create 'The Best Script for Launching Minecraft 2020' This same script served as a basis for the template on the Ruvds marketplace. All source files are already included in the article. Now, step by step, let's see how this was done.

Required Commands

Alternative Logging

Once I installed a couple more mods, I discovered that the server was crashing unexpectedly. The server did not log errors in latest.log or in debug, and the console, which should have reported this error and stopped, was closed.

If it doesn't want to log – that's fine. We have Powershell with the commandlet Tee-Object, which takes the object and outputs it to a file and the console simultaneously.

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

In this way, Powershell will capture StandardOutput and write it to the file. Do not try to use Start-Process, because it will return System.ComponentModel.Component, not StandardOutput, and -RedirectStandardOutput will make it impossible to input in the console, which we want to avoid.

Startup Arguments

After installing that pair of mods, the author noticed that the server was also lacking RAM. This requires changing the startup arguments. Instead of changing them every time in start.bat, which everyone uses, simply use this script.

Since Tee-Object reads StandardOutput only when the executable is called 'Directly', we will need to create another script. This script will launch Minecraft itself. Let's start with the arguments.

To indulge in ultimate laziness in the future, the script should gather the startup arguments on the fly. For this, we'll start by finding the latest version of 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 have placed there. Ultimate laziness.

Now we need to allocate memory to the server. For this, let's take the amount of system memory and record its sum in a 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 others, but they didn't consider the reason why the server was stopped. It's inconvenient that what if you simply need to change a mod file or remove something?
Now, let's make a proper restart. The author previously encountered strange scripts that restarted the server regardless of why it shut down. We will use exit code. Java uses 0 as a successful completion, and that’s where we will start.

First, we will create a function that will restart the server in case of an unsuccessful termination.

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 a loop until the server shuts down normally from its own console using the /stop command.

If we are automating everything, it would also be good to collect the start date, end date, and the reason for termination.

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

$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

Next, we save the results to a file. Here’s what we get in the variable:

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

All this can be added to a file using Add-Content. Polishing it a bit, we get this script, and let’s 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 arrange the script to run the handler.

Proper autostart

The author wants to use one module to launch Minecraft of various versions from any paths, as well as be able to store logs in a specific folder.

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

We can enable automatic login by adding three entries to the registry.

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

This is unsafe. The username and password are specified here in plain text, so a separate user should be created for running the server, who has user-level access or belongs to an even narrower group. Using the standard administrator for this purpose is strongly not recommended.

We have dealt with auto-login. Now we 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:\minecraft\stdout.txt' -MinecraftPath 'C:\minecraft'"
Register-ScheduledTask -TaskName "StartSSMS" -Trigger $Trigger -User $User -Action $PS -RunLevel Highest

Let's compile the module

Now let's organize everything into modules that can be used later. The entire code of the prepared scripts is here; import and use.

Everything described above can be used separately if you don't want to bother with modules.

Start-Minecraft

First, we will create a module that will only run a script that listens to and logs standard output.

In the parameters block, it asks from which folder to launch Minecraft and where to store 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 to run Minecraft, you will need to do it like this:

Start-Minecraft -Type Forge -LogFile 'C:\minecraft\stdout.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, a parameters block must also be specified. Note that it launches Oracle Java; if you are using another distribution, you will need to change the path to the executable.

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 almost identical to Start-Minecraft, except it only registers a new task. It takes the same arguments. The username, if not specified, is taken from the current user.

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 parameters block, the script takes the Username and Password parameters. If the Username is not provided, 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."
 
}

The execution of this script looks like this:

Set-Autologon -Password "PlaintextPassword"

How to use

Now let's see how the author uses all of this. How to correctly set up a public Minecraft server on Windows. We'll start from the beginning.

1. Create a user

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

2. Register the script execution task

You can register using the module like this:

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:\minecraft\stdout.txt' -MinecraftPath 'C:\minecraft'"
Register-ScheduledTask -TaskName "StartSSMS" -Trigger $Trigger -User $User -Action $PS -RunLevel Highest

3. Enable auto logon and restart the machine

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

Completion

The author created the script, including for personal use, and is open to suggestions for improvements. The author hopes that this code has been at least minimally useful to you, and that the article is interesting.

The Ideal Script for Launching a Minecraft Server

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster