added current files to dev branch.

This commit is contained in:
Marcel Naeve 2024-11-10 03:40:05 +01:00
parent 808be57871
commit bf2b7d9f36
Signed by: manae
GPG Key ID: CBB7B21A2EBDC6A9
6 changed files with 592 additions and 0 deletions

5
factorio-settings.env Executable file
View File

@ -0,0 +1,5 @@
FACTORIO_SAVE_PATH="/home/factorio/saves"
FACTORIO_SAVE_NAME="spaceage-01-2024-11-01-v2"
FACTORIO_MOD_PATH="/home/factorio/mods/default"
FACTORIO_SETTINGS_JSON="/home/factorio/server-settings.json"
FACTORIO_VERSION="2.0.14"

276
factorio_update.php Normal file
View File

@ -0,0 +1,276 @@
<?PHP
require __DIR__.'/scripts/func.downloadFile.php';
require __DIR__.'/scripts/class.FactorioVersion.php';
require __DIR__.'/scripts/class.Systemctl.php';
require __DIR__.'/scripts/class.ValveRcon.php';
// Welches Build ist gewünscht?
$target_build = "stable";
$target_edition = "headless";
// Wo wird der Server dieses Builds heruntergeladen?
$download_source = "https://factorio.com/get-download/stable/headless/linux64";
// In welcher Datei soll der Download gespeichert werden?
// Wichtig da Factorio eine .tar.gz datei als linux64 Datei anbietet.
$download_output = __DIR__."/factorio.tar";
// Welche Dateien oder Ordner sollen vom alten Server übernommen werden?
$copy_paths = [
"server-adminlist.json",
"player-data.json",
"config",
"config-path.cfg"
];
// In welcher Datei steht die aktuelle Versionsnummer?
$current_version_str = (function(){
$env = file_get_contents(__DIR__."/factorio-settings.env");
$version = preg_replace("/.*FACTORIO_VERSION\=\"((\d+\.)+\d+)\".*/su","$1",$env);
return $version;
})();
$current_version = new \NAE\Factorio\FactorioVersion($current_version_str);
echo "CURRENT_VERSION: ".$current_version->getVersion().PHP_EOL;
// Aktuelle Versionsnummern aus der Factorio-API abrufen.
$live_versions = json_decode(file_get_contents("https://factorio.com/api/latest-releases"), true);
// Anhand von Build und Edition die richtige Version auswählen.
$latest_version = new \NAE\Factorio\FactorioVersion($live_versions[$target_build][$target_edition]);
echo "LATEST_VERSION: ".$latest_version->getVersion().PHP_EOL.PHP_EOL;
/**
* Schreibt die neue Versionsnummer in die environment (env) datei des servers
* @return bool Schreiben erfolgreich?
*/
function writeBackVersion() : bool {
global $latest_version;
$env_file = __DIR__."/factorio-settings.env";
$env = file_get_contents($env_file);
$new_env = preg_replace("/FACTORIO_VERSION\=\"((\d+\.)+\d+)\"/su", "FACTORIO_VERSION=\"".$latest_version->getVersion()."\"", $env);
return ( file_put_contents($env_file, $new_env) !== false );
}
// Variablen zum speichern des Fortschrittes bzw. Erfolgs.
$download_success = false;
$extract_success = false;
echo "# [ PRÜFE AUF FACTORIO UPDATES ]".PHP_EOL;
if($latest_version->isNewer($current_version)) { // neue Version verfügbar?
echo "## [ NEUE VERSION GEFUNDEN : UPDATE STARTET ]".PHP_EOL;
if(file_exists($download_output)) { // liegt noch ein altes Server Archiv im Vertzeichnis?
echo PHP_EOL."[ ALTE SERVER FILES GEFUNDEN : STARTE LÖSCHVORGANG ]".PHP_EOL;
if( unlink($download_output) ) { // altes Archiv löschen
echo "-> ALTE SERVER FILES ERFOLGREICH GELÖSCHT!".PHP_EOL;
} else {
echo "-> ERR : ALTE SERVER FILES ERFOLGREICH GELÖSCHT!".PHP_EOL;
die(1);
}
}
echo PHP_EOL."[ STARTE DOWNLOAD DER SERVER FILES ]".PHP_EOL;
if(\NAE\Functions\Curl\downloadFile($download_output, $download_source)) { // Download der neuen Server Files
echo "-> DOWNLOAD SUCCESS!".PHP_EOL;
$download_success = true;
} else {
echo "-> ERR : DOWNLOAD FEHLGESCHLAGEN!".PHP_EOL;
die(1);
}
if($download_success) {
echo PHP_EOL."[ STARTE DAS ENTPACKEN DER NEUEN SERVER FILES ]".PHP_EOL;
exec("tar -xvf \"$download_output\" > /dev/null", $output, $return); // Entpacken
if($return === 0) {
if( rename(__DIR__."/factorio", __DIR__."/factorio-".$latest_version->getVersion()) ) { // Umbenennen mit Versionsnummer
echo "-> Neue Server Files wurden Erfolgreich entpackt!".PHP_EOL;
$extract_success = true;
} else {
echo "-> ERR : Umbenennen des Factorio Ordners mit Versionsnummer Fehlgeschlagen!".PHP_EOL;
die(1);
}
} else {
echo "-> ERR : Entpacken der Server Files ist Fehlgeschlagen!".PHP_EOL;
die(1);
}
}
if($extract_success) {
// RCON Kommunikation zum Server starten
$rcon_data = (function() : ?array {
global $current_version;
$cfg = parse_ini_file(__DIR__."/factorio-".$current_version->getVersion()."/config/config.ini", true);
if(!$cfg) return null;
$cd = [];
if(isset($cfg["other"])) {
$parts = explode(":", $cfg["other"]["local-rcon-socket"]??"127.0.0.1:27015");
$cd["ip"] = $parts[0];
$cd["port"] = $parts[1];
if(isset($cfg["other"]["local-rcon-socket"])) {
$cd["password"] = $cfg["other"]["local-rcon-password"]??"";
}
}
return $cd;
})();
$rcon = null;
if($rcon_data) {
try {
$rcon = new \NAE\Valve\Rcon\ValveRcon($rcon_data["ip"], $rcon_data["port"], $rcon_data["password"]);
$rcon->sendCommand("ACHTUNG: Server wird in kürze für ein Update gestoppt, bitte trennen Sie Ihre Verbindung zum Server. Der Server wird in kürze wieder verfügbar sein. Zeit bis zum Stopp: 60 Sekunden.", false);
if($rcon->getError() !== null) {
echo "[!] ERR : RCON Verbindung konnte nicht aufgebaut werden!".PHP_EOL;
$rcon = null;
}
} catch(\Exception $e) {
echo "[!] ERR : RCON Verbindung konnte nicht aufgebaut werden!".PHP_EOL;
$rcon = null;
}
}
if($rcon) sleep(60); // Falls die Meldung geklappt hat 60s warten, damit Spieler den Server verlassen können.
// inititialize Systemctl Service interface
$service = new \NAE\Service\Systemctl("factorio");
// Server Stoppen
if($service->stop()) {
echo "[+] FACTORIO SERVER WURDE GESTOPPT!".PHP_EOL;
}
// neue Versionsnummer in .env Datei übernehmen
if(writeBackVersion()) {
echo "[+] Version in Environment File ist aktualisiert".PHP_EOL;
}
// Dateien aus alten Server Files kopieren..
if( is_dir(__DIR__."/factorio-".$current_version->getVersion()) && is_dir(__DIR__."/factorio-".$latest_version->getVersion()) ) {
echo PHP_EOL."[ Kopiere Konfigurations-Dateien in den neuen Server Pfad ]".PHP_EOL;
foreach($copy_paths as $copy_id => $copy_name) {
$copy_source = __DIR__."factorio-/".$current_version->getVersion().$copy_name;
$copy_dest = __DIR__."factorio-/".$latest_version->getVersion().$copy_name;
if(file_exists($copy_source)) {
echo "[ Kopierenvorgang Nr. ".($copy_id+1)." von ".count($copy_paths)." - $copy_name ] ";
if(copy($copy_source, $copy_dest)) {
echo "-> Kopiervorgang Erfolgreich!".PHP_EOL;
} else {
echo "-> ERR : Kopiervorgang Fehlgeschlagen!".PHP_EOL;
}
} else {
echo " - Quelle nicht vorhanden!".PHP_EOL;
}
}
}
// Fix file permissions..
(function(){
global $latest_version;
exec("chown -R factorio:factorio \"".__DIR__."/factorio-".$latest_version->getVersion()."\"", $output, $return);
if($return === 0) {
echo "[+] Rechte im neuen Server verzeichnis korrigiert.".PHP_EOL;
} else {
echo "[!] ERR : Rechte im neuen Server verzeichnis konnten nicht korrigiert werden.".PHP_EOL;
}
})();
if($service->start()) {
echo "[+] FACTORIO SERVER WURDE WIEDER GESTARTET!".PHP_EOL;
}
}
} else {
// Keine aktuellere Version verfügbar!
echo "-> Kein Update verfügbar.".PHP_EOL;
}

View File

@ -0,0 +1,72 @@
<?php
namespace NAE\Factorio;
class FactorioVersion {
private $version;
private $intVersion;
/**
* Füllt eine Zeichenkette am anfang mit Nullen auf bis die Ziellänge erreicht ist.
* @param string Zu füllende Zeichenkette
* @param int Ziellänge der gefüllten Zeichenkette
* @return string die Zeichenkette mit Nullen vorne angefügt um die Ziellänge zu erreichen
*/
private function zero_fill(string $str, int $target_length) {
if(strlen($str) >= $target_length) {
return $str;
}
$missing = ( $target_length - strlen($str) );
for($i=0;$i<$target_length;$i++) {
$str = "0".$str;
}
return $str;
}
/**
* Formatiert die Factorio Versionsnummern um zu einem Vergleichbaren Integer.
* @param string Factorio Versions Zeichenkette Beispiel: 2.0.14
* @return int zum einfachen Vergleich formatierter Versions-Integer. Beispiel: versionsFormat("2.0.14") -> "000200000014" -> int(200000014)
*/
private function versionFormat(string $vs) : int {
$parts = explode(".", $vs);
$new = "";
foreach($parts as $i => $v) {
$new .= $this->zero_fill($v, 4);
}
return intval($new);
}
public function __construct(string $version) {
$this->version = $version;
$this->intVersion = $this->versionFormat($version);
}
public function getVersion() : string {
return $this->version;
}
public function getIntVersion() : int {
return $this->intVersion;
}
public function isNewer(FactorioVersion $other) : bool {
return $this->intVersion > $other->getIntVersion();
}
public function isOlder(FactorioVersion $other) : bool {
return $this->intVersion < $other->getIntVersion();
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace NAE\Service;
/**
* Systemctl Service Management Interface
* @author "Marcel Naeve" <php@naeve.info>
* @license MIT License (http://www.opensource.org/licenses/mit)
*/
class Systemctl {
/**
* @var string Name of the service to manage
*/
private $service_name = "";
/**
* Constructor
*
* @param string $service_name Name of the service to manage
*/
public function __construct(string $service_name) {
$this->service_name = $service_name;
}
/**
* Start the Service
*
* @return bool True if the service was started successfully, false otherwise
*/
public function start () : bool {
exec("systemctl start ".$this->service_name." > /dev/null", $output, $return);
return ( $return === 0 );
}
/**
* Stop the Service
*
* @return bool True if the service was stopped successfully
*/
public function stop () : bool {
exec("systemctl stop ".$this->service_name." > /dev/null", $output, $return);
return ( $return === 0 );
}
/**
* Restart the Service
*
* @return bool True if the service was restarted successfully, false otherwise
*/
public function restart () : bool {
exec("systemctl restart ".$this->service_name." > /dev/null", $output, $return);
return ( $return === 0 );
}
/**
* Check if the Service is running
*
* @return bool True if the service is running, false otherwise
*/
public function isRunning () : bool {
exec("systemctl status ".$this->service_name, $output, $return);
$search_string = implode(";", $output);
return ( strpos($search_string, "Active: active (running)") >= 0 );
}
}

139
scripts/class.ValveRcon.php Normal file
View File

@ -0,0 +1,139 @@
<?php
namespace NAE\Valve\Rcon;
class ValveRcon {
private $ip;
private $port;
private $password;
private $timeout;
private $socket;
private $err;
public function __construct(string $ip, int $port, string $password, int $timeout=2) {
$this->ip = $ip;
$this->port = $port;
$this->password = $password;
$this->timeout = $timeout;
}
private function connect() : bool {
$errno = null;
$errmsg = "";
$this->socket = fsockopen(
"udp://{$this->ip}",
$this->port,
$errno,
$errmsg,
$this->timeout
);
if(!$this->socket) {
$this->err = [
"code" => $errno,
"message" => "Socket connection failed: $errmsg"
];
return false;
}
return true;
}
private function disconnect() : void {
if ( $this->isConnected() ) {
fclose($this->socket);
$this->socket = NULL;
}
}
private function isConnected() : bool {
return ( get_resource_type($this->socket) !== null ); // check if socket is resource type, as it should be
}
private function read() : ?string {
if (!$this->isConnected()) {
return null;
}
stream_set_timeout($this->socket, 0, $this->timeout * 100000); // set timeout
$response = '';
while ($buffer = fread($this->socket, 4096)) { // loop while there's more data
list($header, $content) = explode("\n", $buffer, 2); // split into header and content
$response .= $content; // append content to response
}
$response = trim($response); // remove whitespaces around the response
if (empty($response)) // no data received, return null
return null;
return preg_replace("/\^./","", $response); // return response without prefix
}
private function write(string $cmd) : bool {
if (!$this->isConnected()) {
return false;
}
if( false === fwrite($this->socket, str_repeat(chr(255), 4) . "rcon {$this->password} {$cmd}\n") ) {
return false;
}
return true;
}
public function sendCommand(string $cmd, bool $read=false) : ?string {
$this->connect();
if(!$this->write($cmd)) { // sending the command
$this->err = [
"code" => 0,
"message" => "Command could not be sent"
];
$this->disconnect();
return null;
}
if (!$read) {
$this->disconnect();
return null;
}
if(!$res = $this->read()) { // reading the response
$this->err = [
"code" => 0,
"message" => "Response could not be read"
];
}
$this->disconnect();
return $res;
}
public function getError() :?object {
if($this->err === null) {
return null; // no error has occurred, return null
}
return json_decode(json_encode($this->err)); // return last error as object
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace NAE\Functions\Curl;
/**
* Lädt eine Dei herunter mit Hilfe von php-curl.
* @param string Zielpfad
* @param string Quellpfad/URL
* @return bool Download Erfolgreich?
*/
function downloadFile(string $fileDestination, string $fileSource) : bool {
$ch = curl_init($fileSource);
$fp = fopen($fileDestination, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
fclose($fp);
return ( $info["download_content_length"]==$info["size_download"] );
}