Factorio auto-update script

Enhance your gameplay with these tools. This category is also the right place for tools useful for modders.
Mod databases, calculators, cheatsheets, multiplayer, scripts, libs and other useful stuff that is not strictly in-game mods.
Nidhoegger666
Burner Inserter
Burner Inserter
Posts: 15
Joined: Wed Dec 13, 2017 6:15 pm
Contact:

Factorio auto-update script

Post by Nidhoegger666 »

I have written a script to trigger an auto update on linux, if you are interested. You might need to adjust paths and stuff to fit your needs. To trigger the script, send SIGUSR1 to the bash that executes the script and it will check for updates (currently in the experimental branch), download, extract, stop the old server (with map save) and restart. Its pretty hacky. I created an URL to trigger the updatecheck via a set uid script in /usr/bin.

Have fun :)

Code: Select all

#!/bin/bash

NC="\033[0m"
RED="\033[0;31m"
GREEN="\033[0;32m"
WHITE="\033[1;37m"

function die
{
	echo -e "${RED}$@${NC}"
	exit 1
}

function update
{
	wget -O "factorio_${release}.tar.xz" https://factorio.com/get-download/${release}/headless/linux64 || die "Error downloading new version"
	mv "factorio" "factorio_${current}"
	tar xvJf "factorio_${release}.tar.xz"
	cp "factorio_${current}/data/server-settings.json" "factorio/data/"
	echo "Update successful!"
}

function get_versions
{
	release=$(curl https://factorio.com 2>&1 | grep -A1 Experimental | grep \<dd\> | cut -d'>' -f2 | cut -d'<' -f1)
	current=$(cat ./factorio/data/base/info.json | grep version | cut -d'"' -f4)
}

function trigger_check
{
	check=1
}

function do_update
{
	echo "Released version: $release"
	echo "Current version:  $current"
	if [ "${current}" != "${release}" ]; then
		echo "Needing update"
		update
	else
		echo "Factorio is up-to-date!"
	fi
}

trap trigger_check SIGUSR1

get_versions
do_update

while true; do
	# this is my script to launch factorio!
	./start_mp17.sh &
	update=0
	while [ $update -eq 0 ]; do
		check=0
		while [ $check -eq 0 ]; do
			sleep 5
		done
		get_versions
		if [ "${current}" != "${release}" ]; then
			update=1
		else
			echo "Factorio is up-to-date"
		fi
	done

	pid=$(ps aux | sed 's/  */ /g' | grep "\./factorio/bin" | grep -v grep | cut -d' ' -f2)

	if [ ! -z "${pid}" ]; then
		echo "Sending SIGINT to $pid"

		kill -SIGINT $pid
		echo "Waiting for $pid"
		tail --pid $pid -f /dev/null
		echo "Updating"

		do_update
	fi
done
Takervat
Manual Inserter
Manual Inserter
Posts: 1
Joined: Fri May 10, 2019 10:21 am
Contact:

Factorio auto update script

Post by Takervat »

If the user has adblock enabled the full page script doesnt work? anyone can fix this with a new code?
Orygeunik
Manual Inserter
Manual Inserter
Posts: 2
Joined: Wed May 29, 2019 6:56 pm
Contact:

Re: Factorio auto-update script

Post by Orygeunik »

Then I will allow myself to show my simplest PowerShell script

Code: Select all

#Factorio autoupdate powershell script

$useExperimentalVersion = 1; #0 - Stable; 1 - Experimental;
$baseTempFolderPath = "D:\Temp\";
$gameFolderPath = "D:\Temp\Game\";
$userLogin = "";
$userPassword = "";
$useSeparateGameFolder = $false; #true - create new folder with new game version; false - merge game folder (auto-backup)
$backupFolderPath = "D:\Temp\Backup\" #set if $useSeparateGameFolder = true

#
#
#

if([string]::IsNullOrWhiteSpace($baseTempFolderPath)) 
{
    Write-Output "Base temp folder path - empty!";
    Exit 1;
}

if([string]::IsNullOrWhiteSpace($gameFolderPath)) 
{
    Write-Output "Game folder path - empty!";
    Exit 2;
}

if([string]::IsNullOrWhiteSpace($userLogin)) 
{
    Write-Output "User login - empty!";
    Exit 3;
}

if([string]::IsNullOrWhiteSpace($userPassword)) 
{
    Write-Output "User password - empty!";
    Exit 4;
}

if((!$useSeparateGameFolder) -and [string]::IsNullOrWhiteSpace($backupFolderPath))
{
    Write-Output "User password - empty!";
    Exit 5;
}

$urlMain = "https://www.factorio.com/";
$urlLogin = "https://www.factorio.com/login";
$urlLogout = "https://www.factorio.com/logout";

$lastVersionFilePath = [IO.Path]::Combine($baseTempFolderPath, "lastVersionFilePath.txt");

$regexVersion = New-Object System.Text.RegularExpressions.Regex("<h3>\s+Latest releases\s+<\/h3>\s+<dl>\s+<dt>Stable:<\/dt>\s+<dd>(\d{0,2}\.\d{0,3}\.\d{0,4})<\/dd>\s+<dt>Experimental:<\/dt>\s+<dd>(\d{0,2}\.\d{0,3}\.\d{0,4})<\/dd>");
$regexToken = New-Object System.Text.RegularExpressions.Regex("<input id=""csrf_token"".+value=""(.{91})"">");

$start_time = Get-Date;

Add-Type -AssemblyName System.IO.Compression.FileSystem;

New-Item -ItemType Directory -Force -Path $baseTempFolderPath | Out-Null
New-Item -ItemType Directory -Force -Path $gameFolderPath | Out-Null

$cookieContainer = New-Object System.Net.CookieContainer;

#magic function for implement c# using {}
function Using-Object
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [AllowNull()]
        [Object]
        $InputObject,

        [Parameter(Mandatory = $true)]
        [scriptblock]
        $ScriptBlock
    )

    try
    {
        . $ScriptBlock
    }
    finally
    {
        if ($null -ne $InputObject -and $InputObject -is [System.IDisposable])
        {
            $InputObject.Dispose()
        }
    }
}

function Get-DownloadPage
{
    Param ([string]$url,  [string]$outputPath)

    Using-Object ([System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::Create($url)){
        $req.method = "GET";    
        $req.Headers.Add("Accept-Charset: utf-8;");
        $req.AllowAutoRedirect = $true;
        $req.ContentType = "application/x-www-form-urlencoded";
        $req.TimeOut = 50000;
        $req.KeepAlive = $true;
        $req.Headers.Add("Keep-Alive: 300");
        $req.CookieContainer = $cookieContainer;
        
        Using-Object ([System.Net.HttpWebResponse] $res = $req.GetResponse()){
            Using-Object ($fs = [System.IO.File]::Open($outputPath, [System.IO.FileMode]::Create)){
                $res.GetResponseStream().CopyTo($fs);
            }
        }
    }     
}

function Get-InternetPage
{
    Param ([string]$url)

    Using-Object ([System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::Create($url)){
        $req.method = "GET";    
        $req.Headers.Add("Accept-Charset: utf-8;");
        $req.AllowAutoRedirect = $true;
        $req.ContentType = "application/x-www-form-urlencoded";
        $req.TimeOut = 50000;
        $req.KeepAlive = $true;
        $req.Headers.Add("Keep-Alive: 300");
        $req.CookieContainer = $cookieContainer;

        Using-Object ([System.Net.HttpWebResponse] $res = $req.GetResponse()){
            Using-Object ($reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $res.GetResponseStream()){
                return $reader.ReadToEnd();
            }
        }
    }     
}

function Send-Payload
{
    Param ([string]$url, [string]$payload)

    $buffer = [System.Text.Encoding]::UTF8.GetBytes($payload);
    
    Using-Object ([System.Net.HttpWebRequest] $req = [System.Net.WebRequest]::Create($url)){
        $req.method = "POST";    
        $req.Headers.Add("Accept-Charset: utf-8;");
        $req.AllowAutoRedirect = $true;
        $req.ContentType = "application/x-www-form-urlencoded";
        $req.TimeOut = 50000;
        $req.KeepAlive = $true;
        $req.Headers.Add("Keep-Alive: 300");
        $req.CookieContainer = $cookieContainer;

        Using-Object ($reqst = $req.GetRequestStream()){
            $reqst.Write($buffer, 0, $buffer.Length);
            $reqst.Flush()
            $reqst.Close();
        }

        Using-Object ([System.Net.HttpWebResponse] $res = $req.GetResponse()){
            Using-Object ($reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $res.GetResponseStream()){
                return $reader.ReadToEnd();
            }
        }
    }  
}

function Unzip
{
    Param([string]$zipFile, [string]$outputPath, [string]$version)
    
    Using-Object ($archive = [System.IO.Compression.ZipFile]::OpenRead($zipFile)){
        foreach ($entry in $archive.Entries)
        {
            $entryTargetFilePath = [System.IO.Path]::Combine($outputPath, $entry.FullName);

            if(!$useSeparateGameFolder)
            {                
                $entryTargetFilePath = $entryTargetFilePath.Replace("Factorio_$($version)","");
            }
            
            $entryDir = [System.IO.Path]::GetDirectoryName($entryTargetFilePath);

            if(!(Test-Path $entryDir ))
            {
                New-Item -ItemType Directory -Path $entryDir | Out-Null 
            }
            
            if(!$entryTargetFilePath.EndsWith("\"))
            {
                [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $entryTargetFilePath, $true);
            }
        }
        $archive.Dispose();
    }
}

function Zip
{
    Param([string]$toZipDirectory)

    $entryDir = [System.IO.Path]::GetDirectoryName($backupFolderPath);

    if(!(Test-Path $entryDir ))
    {
        New-Item -ItemType Directory -Path $entryDir | Out-Null 
    }

    $outputPath = [IO.Path]::Combine($backupFolderPath, (Get-Date -UFormat("{0:yyyy.MM.dd HH.mm}"))  + "_backup.zip");
    
    [System.IO.Compression.ZipFile]::CreateFromDirectory($toZipDirectory, $outputPath);
}

try
{
    $lastVersion = "";

    if(Test-Path -Path $lastVersionFilePath)
    {   
        $lastVersion = Get-Content -Path $lastVersionFilePath;
    }

    $result = Get-InternetPage -url $urlMain

    $mathes = $regexVersion.Match($result);

    if(!$mathes.Success)
    {
        Write-Output "Get game version failed!";
        Exit 6;
    }

    if ($lastVersion -eq $mathes.Groups[$useExperimentalVersion + 1].Value)
    {
        Write-Output "Time: $((Get-Date).Subtract($start_time))";
        Exit 0;   
    }

    $lastVersion = $mathes.Groups[$useExperimentalVersion + 1].Value;

    $urlFile = "https://www.factorio.com/get-download/" + ($mathes.Groups[$useExperimentalVersion + 1].Value) + "/alpha/win64-manual";
    $outputFilePath = [IO.Path]::Combine($baseTempFolderPath, "Factorio_" + ($mathes.Groups[$useExperimentalVersion + 1].Value) + ".zip");

    $result = Get-InternetPage -url $urlLogin   
    
    $mathes = $regexToken.Match($result);

    if(!$mathes.Success)
    {
        Write-Output "Get csrf_token failed!";
        Exit 7;
    }

    $csrf_token = $mathes.Groups[1].Value;

    $loginPayload = "csrf_token="+ $csrf_token + "&username_or_email=" + $userLogin + "&password=" + $userPassword + "&action=Login";    

    $result = Send-Payload -url $urlLogin -payload $loginPayload

    #download game zip
    Get-DownloadPage -url $urlFile -outputPath $outputFilePath

    #logout.Because we good people :)
    $result =  Get-InternetPage -url $urlLogout

    $lastVersion | Out-File $lastVersionFilePath;

    if(!$useSeparateGameFolder)
    {
        Zip -toZipDirectory $gameFolderPath
    }
    #unzip arhive
    Unzip -zipFile $outputFilePath -outputPath $gameFolderPath -version $lastVersion
}
catch [Exception]
{
    Write-Output Get-Date + " Global Exception! Panic!";
    Write-Output $error;
    Exit -1;
}

Write-Output "Time: $((Get-Date).Subtract($start_time))";
Exit 0;
How to use:
  • Open powershell script
  • Find option "useExperimentalVersion"
  • Select type of game (stable / experimental)
  • Set 0 or 1 to set selected game type (0 - Stable; 1 - Experimental;)
  • Find option "baseTempFolderPath"
  • Set path to temporal folder (folder will be created automated). In this folder will be stored file with last downloaded version and archive of game
  • Find option "gameFolderPath"
  • Set path to game folder
  • Find option "userLogin"
  • Set your login from site https://factorio.com
  • Find option "userPassword"
  • Set your password from site https://factorio.com
  • Find option "useSeparateGameFolder"
  • Select how you store game, as separate folder or union folder. (true - create new folder with new game version; false - merge game folder (auto-backup))
  • Find option "backupFolderPath"
  • Set path to backup folder (folder will be created automated). Note. Relevant if you set "$false" in option "useSeparateGameFolder"
  • And run script

---
Upd 2019.05.30 01:30

Also, just in case, the script to remove duplicate mods (Deleted earlier mods).

Code: Select all

#Factorio auto-trim duplicate mods powershell script

$modFolderPath = "";

#
#
#

if([string]::IsNullOrWhiteSpace($modFolderPath)) 
{
    Write-Output "Mod folder path - empty!";
    Exit 1;
}

$start_time = Get-Date;

$modNameList = New-Object 'System.Collections.Generic.Dictionary[string,string]';
$modNameDuplicateList = New-Object 'System.Collections.Generic.Dictionary[string,string]';

$modNames = Get-ChildItem -Path $modFolderPath -Name *.zip | Sort

$regexVersion = New-Object System.Text.RegularExpressions.Regex("(.+)_(\d{0,2}\.\d{0,3}\.\d{0,4})\.zip");

foreach ($name in $modNames)
{
    $mathes = $regexVersion.Match($name);

    if($mathes.Success)
    {        
        if($modNameList.ContainsKey($mathes.Groups[1].Value))
        {            
            $modNameDuplicateList.Add($mathes.Groups[1].Value, $modNameList[($mathes.Groups[1].Value)]);
        }
        else
        {
            $modNameList.Add($mathes.Groups[1].Value, $mathes.Groups[2].Value);
        }
    }
}

foreach ($name in $modNameDuplicateList.GetEnumerator())
{    
    $fullPath = [System.IO.Path]::Combine($modFolderPath, ($name.Key + "_" + $name.Value + ".zip"));

    Write-Output "Trim mod: $($fullPath)";

    Remove-Item -Path $fullPath
}

Write-Output "Time: $((Get-Date).Subtract($start_time))";
Exit 0;

How to use:
  • Open powershell script
  • Find option "modFolderPath"
  • Set path to mods folder
  • And run script
Post Reply

Return to “Tools”