Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
|
e575381459 | |
|
07525cd06c | |
|
2b1271ec6e | |
|
a1ce18f324 | |
|
7d005f14b9 | |
|
f7d3b31262 | |
|
85a1beee12 | |
|
20cd7d51c6 | |
|
8e2c931ad5 | |
|
b7939127e1 | |
|
51b8e30b84 | |
|
bf2b7d9f36 |
|
@ -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"
|
|
@ -0,0 +1,341 @@
|
|||
<?PHP
|
||||
|
||||
define("INDEX_DIR", __DIR__);
|
||||
|
||||
require __DIR__.'/scripts/class.TerminalMessage.php';
|
||||
require __DIR__.'/scripts/func.downloadFile.php';
|
||||
require __DIR__.'/scripts/class.FactorioVersion.php';
|
||||
require __DIR__.'/scripts/class.FactorioMod.php';
|
||||
require __DIR__.'/scripts/class.Systemctl.php';
|
||||
require __DIR__.'/scripts/class.ValveRcon.php';
|
||||
require __DIR__.'/scripts/func.rrmdir.php';
|
||||
require __DIR__.'/scripts/func.rcopy.php';
|
||||
|
||||
|
||||
$cli = new \NAE\Terminal\TerminalMessage();
|
||||
|
||||
|
||||
// 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";
|
||||
|
||||
// sha256 hash Source
|
||||
$download_hash_source = "https://www.factorio.com/download/sha256sums/";
|
||||
|
||||
// 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);
|
||||
$cli->sendInfo( "CURRENT_VERSION: ".$current_version->getVersion() );
|
||||
|
||||
|
||||
|
||||
// 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]);
|
||||
$cli->sendInfo( "LATEST_VERSION: ".$latest_version->getVersion() );
|
||||
echo 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;
|
||||
|
||||
$cli->sendTitle( "PRÜFE AUF FACTORIO UPDATES" );
|
||||
|
||||
if($latest_version->isNewer($current_version)) { // neue Version verfügbar?
|
||||
|
||||
$cli->sendTitle( "NEUE VERSION GEFUNDEN : UPDATE STARTET" );
|
||||
|
||||
if(file_exists($download_output)) { // liegt noch ein altes Server Archiv im Vertzeichnis?
|
||||
|
||||
$cli->sendTitle( "ALTE SERVER FILES GEFUNDEN : STARTE LÖSCHVORGANG", 1 );
|
||||
|
||||
if( unlink($download_output) ) { // altes Archiv löschen
|
||||
|
||||
$cli->sendSuccess( "ALTE SERVER FILES ERFOLGREICH GELÖSCHT!" );
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendError( "ALTE SERVER FILES KONNTEN NICHT GELÖSCHT WERDEN!", true );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
echo PHP_EOL;
|
||||
$cli->sendTitle( "STARTE DOWNLOAD DER SERVER FILES", 1 );
|
||||
|
||||
if(\NAE\Functions\Curl\downloadFile($download_output, $download_source)) { // Download der neuen Server Files
|
||||
|
||||
$dl_real_out = $download_output;
|
||||
$sha256file = file_get_contents($download_hash_source); // sha256 hash liste von factorio
|
||||
$fileHash = hash_file("sha256", $download_output);
|
||||
|
||||
$cli->sendInfo("Umbenennen des Heruntergeladenen Archivs..");
|
||||
$realFilename = (function(array $lst, string $hash) : ?string {
|
||||
$hashLen = strlen($hash);
|
||||
foreach($lst as $file) {
|
||||
if(substr($file, 0, $hashLen) == $hash && substr($file, $hashLen, 1) == " ") {
|
||||
|
||||
return trim(substr($file, $hashLen+1));
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})(explode("\n", $sha256file), $fileHash);
|
||||
if($realFilename === null) {
|
||||
|
||||
$cli->sendError( "FEHLGESCHLAGEN: Kann den richtigen Dateinamen aus der sha256 hash liste nicht ermitteln!", true );
|
||||
|
||||
} else {
|
||||
|
||||
$orig_download_output = $download_output;
|
||||
$download_output = __DIR__."/$realFilename";
|
||||
|
||||
if(@rename( $orig_download_output, $download_output )) {
|
||||
|
||||
$cli->sendSuccess( "DOWNLOAD SUCCESS!" );
|
||||
$download_success = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendError( "DOWNLOAD FEHLGESCHLAGEN!", true );
|
||||
|
||||
}
|
||||
|
||||
|
||||
if($download_success) {
|
||||
|
||||
echo PHP_EOL;
|
||||
$cli->sendTitle( "STARTE DAS ENTPACKEN DER NEUEN SERVER FILES", 1 );
|
||||
|
||||
$unxz_download_output = preg_replace("/.tar.xz$/", ".tar", $download_output);
|
||||
|
||||
exec("unxz -vfd \"$download_output\" && tar -xvf \"$unxz_download_output\" > /dev/null", $output, $return); // Entpacken
|
||||
|
||||
if($return === 0) {
|
||||
|
||||
if( @rename(__DIR__."/factorio", __DIR__."/factorio-".$latest_version->getVersion()) ) { // Umbenennen mit Versionsnummer
|
||||
|
||||
$cli->sendSuccess( "Neue Server Files wurden Erfolgreich entpackt!" );
|
||||
$extract_success = true;
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendError( "Umbenennen des Factorio Ordners mit Versionsnummer Fehlgeschlagen!", true );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendError( "Entpacken der Server Files ist Fehlgeschlagen!", true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
$cli->sendWarning( "RCON Verbindung konnte nicht aufgebaut werden!" );
|
||||
$rcon = null;
|
||||
}
|
||||
|
||||
} catch(\Exception $e) {
|
||||
|
||||
$cli->sendWarning( "RCON Verbindung konnte nicht aufgebaut werden!" );
|
||||
$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()) {
|
||||
$cli->sendInfo( "FACTORIO SERVER WURDE GESTOPPT!" );
|
||||
}
|
||||
|
||||
// neue Versionsnummer in .env Datei übernehmen
|
||||
if(writeBackVersion()) {
|
||||
$cli->sendInfo( "Version in Environment File wurde aktualisiert" );
|
||||
}
|
||||
|
||||
// Dateien aus alten Server Files kopieren..
|
||||
if( is_dir(__DIR__."/factorio-".$current_version->getVersion()) && is_dir(__DIR__."/factorio-".$latest_version->getVersion()) ) {
|
||||
|
||||
echo PHP_EOL;
|
||||
$cli->sendTitle( "Kopiere Konfigurations-Dateien in den neuen Server Pfad", 1 );
|
||||
|
||||
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)) {
|
||||
|
||||
if(is_dir($copy_source)) {
|
||||
|
||||
$cli->sendTitle( "Kopierenvorgang Nr. ".($copy_id+1)." von ".count($copy_paths)." - $copy_name", 2 );
|
||||
try {
|
||||
|
||||
\NAE\Functions\Folder\rcopy($copy_source, $copy_dest);
|
||||
$cli->sendSuccess( "Kopiervorgang Erfolgreich!" );
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
$cli->sendError( "Kopiervorgang Fehlgeschlagen!" );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendTitle( "Kopierenvorgang Nr. ".($copy_id+1)." von ".count($copy_paths)." - $copy_name", 2 );
|
||||
if(@copy($copy_source, $copy_dest)) {
|
||||
|
||||
$cli->sendSuccess( "Kopiervorgang Erfolgreich!" );
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendError( "Kopiervorgang Fehlgeschlagen!" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$cli->sendWarning( "Quelle nicht vorhanden!" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fix file permissions..
|
||||
(function(){
|
||||
|
||||
global $latest_version, $cli;
|
||||
|
||||
$cli->sendTitle( "Rechte im neuen Server-Verzeichnis werden korrigiert", 1 );
|
||||
exec("chown -R factorio:factorio \"".__DIR__."/factorio-".$latest_version->getVersion()."\"", $output, $return);
|
||||
if($return === 0) {
|
||||
$cli->sendSuccess( "Rechte erfolgreich korrigiert!" );
|
||||
} else {
|
||||
$cli->sendError( "Rechte konnten nicht korrigiert werden!", true );
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
|
||||
if($service->start()) {
|
||||
$cli->sendSuccess( "FACTORIO SERVER WURDE WIEDER GESTARTET!" );
|
||||
} else {
|
||||
$cli->sendError( "FACTORIO SERVER KONNTE NICHT WIEDER GESTARTET WERDEN!", true );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// Keine aktuellere Version verfügbar!
|
||||
$cli->sendInfo( "Kein Update verfügbar." );
|
||||
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for classes and functions related to the game Factorio.
|
||||
* @package NAE\Factorio
|
||||
*/
|
||||
namespace NAE\Factorio;
|
||||
|
||||
/**
|
||||
* Class for managing Factorio mods.
|
||||
* @author "Marcel Naeve" <php@naeve.info>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit)
|
||||
*/
|
||||
class FactorioMod {
|
||||
|
||||
/**
|
||||
* @var int INIT_TYPE_PATH - Mod is initialized from a zip file.
|
||||
*/
|
||||
const INIT_TYPE_PATH = 1;
|
||||
|
||||
/**
|
||||
* @var int INIT_TYPE_NAME - Mod is initialized from a mod name.
|
||||
*/
|
||||
const INIT_TYPE_NAME = 3;
|
||||
|
||||
|
||||
/**
|
||||
* @var object|null $info - Mod information from Zip file.
|
||||
*/
|
||||
private $info = null;
|
||||
|
||||
/**
|
||||
* @var object|null $api - Mod information from Factorio API.
|
||||
*/
|
||||
private $api = null;
|
||||
|
||||
/**
|
||||
* @var object|null $mod_settings - Mod settings enabled/disabled from json in mods folder.
|
||||
*/
|
||||
private $mod_settings = null;
|
||||
|
||||
/**
|
||||
* @var string|null $mod_folder - Path to the mods folder.
|
||||
*/
|
||||
private $mod_folder = null;
|
||||
|
||||
/**
|
||||
* @var string $game_folder - Path to the Factorio game folder.
|
||||
*/
|
||||
private $game_folder = "factorio";
|
||||
|
||||
|
||||
/**
|
||||
* Reads all Mods as FactorioMod objects to an array.
|
||||
* @param string $modFolder Path to the mods folder.
|
||||
* @return array Array of FactorioMod objects.
|
||||
*/
|
||||
static function readModFolder(string $modFolder) : array {
|
||||
|
||||
$mods = [];
|
||||
|
||||
$dh = opendir($modFolder); // open the directory handle
|
||||
|
||||
while ( false !== ($entry = readdir($dh)) ) { // read directory entries
|
||||
|
||||
if(preg_match("/\.zip$/ui", $entry)) { // if the file is a zip file
|
||||
|
||||
$mods[] = new FactorioMod(
|
||||
$modFolder. "/". $entry,
|
||||
FactorioMod::INIT_TYPE_PATH
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
closedir($dh); // close the directory handle
|
||||
|
||||
return $mods;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads mod information from info.json file in mod Zip file.
|
||||
* @param string $zipPath Path to the mod Zip file.
|
||||
* @return object|null Mod information from info.json file.
|
||||
*/
|
||||
private function readInfo(string $zipPath) : ?object {
|
||||
|
||||
if(!file_exists($zipPath)) { // file does not exist?
|
||||
return null;
|
||||
}
|
||||
|
||||
$za = new \ZipArchive();
|
||||
$res = $za->open($zipPath); // open the zip file
|
||||
|
||||
if($res !== true) { // open the zip file failed?
|
||||
return null;
|
||||
}
|
||||
|
||||
$c = null;
|
||||
for( $i = 0; $i < $za->numFiles; $i++ ){ // Iterate over all files in the zip file
|
||||
|
||||
$name = $za->getNameIndex( $i ); // get the name of current file
|
||||
|
||||
if(basename($name) == "info.json") { // if the file is info.json
|
||||
$c = $za->getFromIndex($i); // get the content of info.json
|
||||
$i = $za->numFiles; // break the loop by condition
|
||||
break; // break the loop by force (double tap)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$za->close(); // close the zip file
|
||||
|
||||
return json_decode( $c, false ); // return decoded json object or null on failure
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a GET request to Factorio API to get mod information.
|
||||
* @param string $name Mod name to get information from Factorio API.
|
||||
* @return object|null Mod information from Factorio API.
|
||||
*/
|
||||
private function callApi(string $name="") : ?object {
|
||||
|
||||
if($name === "") { // no name given?
|
||||
if($this->info === null) { // also no path to module for info.json?
|
||||
return null;
|
||||
}
|
||||
$name = $this->info->name; // get name from info.json
|
||||
}
|
||||
|
||||
$api = "https://mods.factorio.com/api/mods/" . $name;
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt($curl, CURLOPT_URL, $api);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$cri = curl_getinfo($curl);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if($cri["http_code"] != 200) { // request failed?
|
||||
return null;
|
||||
}
|
||||
|
||||
if(trim($response) < 1) { // response body empty?
|
||||
return null;
|
||||
}
|
||||
|
||||
$r = json_decode($response, false); // decode json object or null on failure
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $init Path to the mod Zip file or mod name.
|
||||
* @param int $init_type Initialization type (FactorioMod::INIT_TYPE_PATH or FactorioMod::INIT_TYPE_NAME).
|
||||
* @param string $mod_folder Path to the mods folder.
|
||||
*/
|
||||
public function __construct(string $init, int $init_type=self::INIT_TYPE_PATH, string $mod_folder="") {
|
||||
|
||||
if($init_type === self::INIT_TYPE_PATH) {
|
||||
|
||||
$this->info = $this->readInfo($init);
|
||||
if($this->info !== null) {
|
||||
$this->mod_folder = dirname($init);
|
||||
$this->callApi();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($init_type === self::INIT_TYPE_NAME) {
|
||||
|
||||
$this->mod_folder = $mod_folder;
|
||||
$this->callApi($init);
|
||||
|
||||
// TODO: look if mod is installed..
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last release version information of the mod.
|
||||
* @return object|null Last release version information of the mod.
|
||||
*/
|
||||
public function getLastVersionInfo() : ?object {
|
||||
|
||||
$target = count($this->api->releases) - 1;
|
||||
|
||||
return $this->api->releases[ $target ];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the latest release mod version is newer than the installed version.
|
||||
* @return bool True if the latest release mod version is newer, false otherwise.
|
||||
*/
|
||||
public function isUpdateable() : bool {
|
||||
|
||||
$lv = new \NAE\Factorio\FactorioVersion(
|
||||
$this->getLastVersionInfo()->version
|
||||
);
|
||||
|
||||
$cv = new \NAE\Factorio\FactorioVersion(
|
||||
$this->info->version
|
||||
);
|
||||
|
||||
return $lv->isNewer($cv);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the mod from the mods folder file.
|
||||
* @return bool True if the mod was deleted, false otherwise.
|
||||
*/
|
||||
private function delete() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the latest release mod version to the mods folder.
|
||||
* @return bool True if the mod was downloaded, false otherwise.
|
||||
*/
|
||||
private function download() : bool {
|
||||
|
||||
// TODO: continue implementing.. where player-data.json..
|
||||
$player_data = json_decode(file_get_contents($this->game_folder. "/player-data.json"), true);
|
||||
$token = $player_data["service-token"];
|
||||
$username = $player_data["service-username"];
|
||||
|
||||
$url = "https://mods.factorio.com" . $this->getLastVersionInfo()->download_url . "?username=$username&token=$token";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls the mod from the mods folder and the settings.json file.
|
||||
* @return bool True if the mod was uninstalled, false otherwise.
|
||||
*/
|
||||
public function uninstall() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the mod in the mods folder and adds it to the settings.json file as enabled.
|
||||
* @return bool True if the mod was installed, false otherwise.
|
||||
*/
|
||||
public function install() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the mod to the latest release mod version.
|
||||
* @return bool True if the mod was updated, false otherwise.
|
||||
*/
|
||||
public function update() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the mod settings for all mods (enabled/disabled) from the settings.json file.
|
||||
* @return bool True if the mod settings have been loaded successfully, false otherwise.
|
||||
*/
|
||||
private function loadModSettings() : bool {
|
||||
|
||||
$this->mod_settings = json_decode(
|
||||
file_get_contents($this->mod_folder. "/settings.json")
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mod settings for all mods (enabled/disabled) from settings.json.
|
||||
* @return object|null Mod settings for all mods.
|
||||
*/
|
||||
public function getModSettings() :?object {
|
||||
return $this->mod_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the game folder.
|
||||
* @param string $game_folder Path to the game folder.
|
||||
* @return FactorioMod The instance of FactorioMod for chaining.
|
||||
*/
|
||||
public function setGameFolder(string $game_folder) : FactorioMod {
|
||||
|
||||
$this->game_folder = $game_folder;
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the game folder.
|
||||
* @return string Path to the game folder.
|
||||
*/
|
||||
public function getGameFolder() : string {
|
||||
|
||||
return $this->game_folder;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the changes in the mod settings for all mods (enabled/disabled) to the settings.json file.
|
||||
* @return bool True if the mod settings have been saved successfully, false otherwise.
|
||||
*/
|
||||
private function saveModSettings() : bool {
|
||||
|
||||
$j = json_encode($this->mod_settings);
|
||||
|
||||
$r = file_put_contents($this->mod_folder. "/settings.json", $j);
|
||||
|
||||
if($r === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( $r === strlen($j) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mod settings for all mods (enabled/disabled) in the settings.json file.
|
||||
* @param object $mod_settings Mod settings for all mods.
|
||||
* @return FactorioMod The instance of FactorioMod for chaining.
|
||||
*/
|
||||
public function setModSettings(object $mod_settings) : FactorioMod {
|
||||
|
||||
$this->mod_settings = $mod_settings;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this mod is enabled in the settings.json.
|
||||
* @return bool True if this mod is enabled, false otherwise.
|
||||
*/
|
||||
public function isEnabled() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this mod is disabled in the settings.json.
|
||||
* @return bool True if this mod is disabled, false otherwise.
|
||||
*/
|
||||
public function isDisabled() : bool {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this mod to enabled in the settings.json
|
||||
*/
|
||||
public function enable() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this mod to disabled in the settings.json
|
||||
*/
|
||||
public function disable() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for classes and functions related to the game Factorio.
|
||||
* @package NAE\Factorio
|
||||
*/
|
||||
namespace NAE\Factorio;
|
||||
|
||||
/**
|
||||
* Class for managing a headless server for the game Factorio.
|
||||
* @author "Marcel Naeve" <php@naeve.info>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit)
|
||||
*/
|
||||
class FactorioServer {
|
||||
|
||||
/**
|
||||
* @var FactorioVersion Current Version of Server
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* @var FactorioMod For Managing Server Mods
|
||||
*/
|
||||
private $mods;
|
||||
|
||||
/**
|
||||
* @var ValveRcon for handling Valve Rcon to server
|
||||
*/
|
||||
private $rcon;
|
||||
|
||||
/**
|
||||
* @var string Name of the Save to load when starting the server
|
||||
*/
|
||||
private $save_name;
|
||||
|
||||
/**
|
||||
* @var string Path to the Server Directory
|
||||
*/
|
||||
private $server_dir = INDEX_DIR . "/factorio";
|
||||
|
||||
/**
|
||||
* @var string Path to the Mods Directory
|
||||
*/
|
||||
private $mod_dir = INDEX_DIR . "/factorio/mods";
|
||||
|
||||
/**
|
||||
* @var string Path to the Save-File Directory
|
||||
*/
|
||||
private $save_dir = INDEX_DIR . "/factorio/saves";
|
||||
|
||||
/**
|
||||
* @var string Name of the user who owns the files and runs this server
|
||||
*/
|
||||
private $user_name = "factorio";
|
||||
|
||||
/**
|
||||
* @var string Name of the user-group who owns the files and runs this server
|
||||
*/
|
||||
private $user_group = "factorio";
|
||||
|
||||
|
||||
/**
|
||||
* @var string Name of the Service in systemd/systemctl
|
||||
*/
|
||||
private $service_name = "factorio";
|
||||
|
||||
/**
|
||||
* @var callcable Custom function for stopping the server (return bool)
|
||||
*/
|
||||
private $customStop = null;
|
||||
|
||||
/**
|
||||
* @var callable Custom function for starting the server (return bool)
|
||||
*/
|
||||
private $customStart = null;
|
||||
|
||||
/**
|
||||
* @var callable Custom function for restarting the server (return bool)
|
||||
*/
|
||||
private $customReStart = null;
|
||||
|
||||
/**
|
||||
* @var callable Custom function for checking the server status (return bool - true means server is running, false means server is not running)
|
||||
*/
|
||||
private $customIsRunning = null;
|
||||
|
||||
/**
|
||||
* @var array Server Settings
|
||||
*/
|
||||
private $settings = [];
|
||||
|
||||
/**
|
||||
* @var array Environment Variables for the Server
|
||||
*/
|
||||
private $env = [];
|
||||
|
||||
/**
|
||||
* @var array Server Admin List
|
||||
*/
|
||||
private $admin_list = [];
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $settings Settings for the server
|
||||
*/
|
||||
public function __construct(array $settings) {
|
||||
// TODO: Implement
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Server with optional feedback on cli.
|
||||
* @param bool $silent Feedback on cli wanted? (default: false)
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
public function updateCli(bool $silent=false) : bool {
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a new Server with the current Settings with optional feedback on cli.
|
||||
* @param string $version Which version should be installed? (default: latest)
|
||||
* @param bool $silent Feedback on cli wanted? (default: false)
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
public function installCli(string $version="latest", bool $silent=false) : bool {
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom function for stopping the server.
|
||||
* @param callable function that is beeing called for stopping the server
|
||||
* @return FactorioServer $this for chaining.
|
||||
*/
|
||||
public function setCustomStop(callable $customStop) : FactorioServer {
|
||||
|
||||
$this->customStop = $customStop;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom function for starting the server.
|
||||
* @param callable function that is beeing called for starting the server
|
||||
* @return FactorioServer $this for chaining.
|
||||
*/
|
||||
public function setCustomStart(callable $customStart) : FactorioServer {
|
||||
|
||||
$this->customStart = $customStart;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server.
|
||||
* @return bool true on success, false on failure
|
||||
*/
|
||||
public function stop() : bool {
|
||||
|
||||
if(is_callable($this->customStop)) {
|
||||
return call_user_func($this->customStop);
|
||||
}
|
||||
|
||||
$command = "systemctl stop factorio-server";
|
||||
exec($command, $output, $return);
|
||||
|
||||
return ( $return === 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
* @return bool true on success, false on failure
|
||||
*/
|
||||
public function start() : bool {
|
||||
|
||||
if(is_callable($this->customStart)) {
|
||||
return call_user_func($this->customStart);
|
||||
}
|
||||
|
||||
$command = "systemctl start ".$this->getServiceName();
|
||||
exec($command, $output, $return);
|
||||
|
||||
return ( $return === 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the server.
|
||||
* @return bool true on success, false on failure
|
||||
*/
|
||||
public function restart() : bool {
|
||||
|
||||
if(is_callable($this->customReStart)) {
|
||||
return call_user_func($this->customReStart);
|
||||
}
|
||||
|
||||
exec("systemctl restart ".$this->getServiceName()." > /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 {
|
||||
|
||||
if(is_callable($this->customIsRunning)) {
|
||||
return call_user_func($this->customIsRunning);
|
||||
}
|
||||
|
||||
exec("systemctl is-active ".$this->getServiceName(), $output, $return);
|
||||
|
||||
return ( $return === 0 );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Name of the Service in systemd/systemctl.
|
||||
* @return string Name of the service
|
||||
*/
|
||||
public function getServiceName() : string {
|
||||
|
||||
return $this->service_name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Name of the Service in systemd/systemctl.
|
||||
* @param string $serviceName Name of the Service
|
||||
* @return FactorioServer $this for chaining.
|
||||
*/
|
||||
public function setServiceName(string $serviceName) : FactorioServer {
|
||||
|
||||
$this->service_name = $serviceName;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for classes and functions related to the game Factorio.
|
||||
* @package NAE\Factorio
|
||||
*/
|
||||
namespace NAE\Factorio;
|
||||
|
||||
/**
|
||||
* Class for parsing and comparing Factorio versions.
|
||||
* @author "Marcel Naeve" <php@naeve.info>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit)
|
||||
*/
|
||||
class FactorioVersion {
|
||||
|
||||
/**
|
||||
* @var string The Factorio Version
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* @var int The Factorio version converted to an comparable integer.
|
||||
*/
|
||||
private $intVersion;
|
||||
|
||||
|
||||
/**
|
||||
* Fills a string with zeros at the beginning till string length matches the target length.
|
||||
* @param string $str string to fill
|
||||
* @param int $target_length Target length of the string
|
||||
* @return string string with zeros at the beginning till string length matches
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the Factorio version string to a comparable integer.
|
||||
* @param string $vs Factorio version string (example: 2.0.14)
|
||||
* @return int factorio version as comparable integer (example: versionFormat("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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $version Factorio version string (example: 2.0.14)
|
||||
*/
|
||||
public function __construct(string $version) {
|
||||
$this->version = $version;
|
||||
$this->intVersion = $this->versionFormat($version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Factorio version string (example: 2.0.14).
|
||||
* @return string Factorio version string (example: 2.0.14)
|
||||
*/
|
||||
public function getVersion() : string {
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Factorio version as a comparable integer (example: versionFormat("2.0.14") -> int(200000014)).
|
||||
* @return integer Factorio version as a comparable integer (example: versionFormat("2.0.14") -> int(200000014)).
|
||||
*/
|
||||
public function getIntVersion() : int {
|
||||
return $this->intVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current Factorio version is newer than another version.
|
||||
* @param FactorioVersion $other Another FactorioVersion object
|
||||
*/
|
||||
public function isNewer(FactorioVersion $other) : bool {
|
||||
return $this->intVersion > $other->getIntVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current Factorio version is older than another version.
|
||||
* @param FactorioVersion $other Another FactorioVersion object
|
||||
*/
|
||||
public function isOlder(FactorioVersion $other) : bool {
|
||||
return $this->intVersion < $other->getIntVersion();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for classes and functions related to managing services.
|
||||
* @package NAE\Service
|
||||
*/
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for Terminal related classes.
|
||||
* @package NAE\Terminal.
|
||||
*/
|
||||
namespace NAE\Terminal;
|
||||
|
||||
/**
|
||||
* A Class to print messages to the terminal with different styles and colors.
|
||||
* @author "Marcel Naeve" <php@naeve.info>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit)
|
||||
*/
|
||||
class TerminalMessage {
|
||||
|
||||
/**
|
||||
* Send a message to the terminal ending with a line break at the end.
|
||||
* @param string $msg The message to send.
|
||||
*/
|
||||
public function send(string $msg) {
|
||||
echo $msg . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a title to the terminal with a specified layer and and a double line break at the end.
|
||||
* @param string $msg The title message to send.
|
||||
* @param string $layer The layer (default: 0)
|
||||
*/
|
||||
public function sendTitle(string $msg, int $layer=0) {
|
||||
$layerString = "#";
|
||||
for($i=0; $i<$layer; $i++) {
|
||||
$layerString .= "#";
|
||||
}
|
||||
self::send( "\033[1m[$layerString] $msg\033[0m" . PHP_EOL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a success message to the terminal with a green color and a line break at the end.
|
||||
* @param string $msg The success message to send.
|
||||
*/
|
||||
public function sendSuccess(string $msg) {
|
||||
self::send( "\033[1;32m[SUCCESS] $msg\033[0m" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an info message to the terminal with a blue color and a line break at the end.
|
||||
* @param string $msg The info message to send.
|
||||
*/
|
||||
public function sendInfo(string $msg) {
|
||||
self::send( "\033[1;34m[INFO] $msg\033[0m" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an error message to the terminal with a red color and a line break at the end.
|
||||
* @param string $msg The error message to send.
|
||||
* @param bool $critical If true, the script will terminate with a non-zero exit code (default: false).
|
||||
*/
|
||||
public function sendError(string $msg, bool $critical=false) {
|
||||
self::send( "\033[1;31m[ERROR] $msg\033[0m" );
|
||||
if ($critical) {
|
||||
die(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a warning message to the terminal with a yellow color and a line break at the end.
|
||||
* @param string $msg The warning message to send.
|
||||
* @param bool $critical If true, the script will terminate with a non-zero exit code (default: false).
|
||||
*/
|
||||
public function sendWarning(string $msg, bool $critical=false) {
|
||||
self::send( "\033[1;33m[WARNING] $msg\033[0m" );
|
||||
if ($critical) {
|
||||
die(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for Valve RCON related classes and functions.
|
||||
* @package NAE\Valve\Rcon
|
||||
*/
|
||||
namespace NAE\Valve\Rcon;
|
||||
|
||||
/**
|
||||
* A Class to interact with a Valve RCON server.
|
||||
* @author "Marcel Naeve" <php@naeve.info>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit)
|
||||
*/
|
||||
class ValveRcon {
|
||||
|
||||
/**
|
||||
* @var string IP address of the RCON server
|
||||
*/
|
||||
private $ip;
|
||||
|
||||
/**
|
||||
* @var int Port number of the RCON server
|
||||
*/
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @var string Password to authenticate with the RCON server
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* @var int Timeout for socket operations in seconds
|
||||
*/
|
||||
private $timeout;
|
||||
|
||||
/**
|
||||
* @var resource Socket resource for RCON communication
|
||||
*/
|
||||
private $socket;
|
||||
|
||||
/**
|
||||
* @var array Error details if any occurs during communication
|
||||
*/
|
||||
private $err;
|
||||
|
||||
/**
|
||||
* Constructor, setting variables.
|
||||
* @param string $ip IP address of the RCON server
|
||||
* @param int $port Port number of the RCON server
|
||||
* @param string $password Password to authenticate with the RCON server
|
||||
* @param int $timeout Timeout for socket operations in seconds (default: 2 seconds)
|
||||
*/
|
||||
public function __construct(string $ip, int $port, string $password, int $timeout=2) {
|
||||
$this->ip = $ip;
|
||||
$this->port = $port;
|
||||
$this->password = $password;
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the RCON server.
|
||||
* @return bool Connection established successfully?
|
||||
*/
|
||||
private function connect() : bool {
|
||||
|
||||
// prepare variables for error handling
|
||||
$errno = null;
|
||||
$errmsg = "";
|
||||
|
||||
$this->socket = fsockopen( // connect to RCON server
|
||||
"udp://{$this->ip}",
|
||||
$this->port,
|
||||
$errno,
|
||||
$errmsg,
|
||||
$this->timeout
|
||||
);
|
||||
|
||||
if(!$this->socket) { // connection failed?
|
||||
$this->err = [
|
||||
"code" => $errno,
|
||||
"message" => "Socket connection failed: $errmsg"
|
||||
];
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the RCON server
|
||||
*/
|
||||
private function disconnect() : void {
|
||||
|
||||
if ( $this->isConnected() ) {
|
||||
fclose($this->socket);
|
||||
$this->socket = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the connection to the RCON server is established.
|
||||
*/
|
||||
private function isConnected() : bool {
|
||||
|
||||
return ( get_resource_type($this->socket) !== null ); // check if socket is resource type, as it should be
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives data from the RCON server.
|
||||
* @return string|null Received data or null if no data is available
|
||||
*/
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a command to the RCON server.
|
||||
* @return bool $cmd Command writen successfully?
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a command to the RCON server and optionally reads the response.
|
||||
* @param string $cmd Command to send to the RCON server
|
||||
* @param bool $read Should the response be read from the RCON server? (default: false)
|
||||
* @return string|null Response from the RCON server or null if no response is expected or read fails
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last error details if any occurred during communication.
|
||||
* @return object|null Last error details or null if no error has occurred (error object contains code and message as properties)
|
||||
*/
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for PHP-CURL related classes and functions.
|
||||
* @package NAE\Functions\Curl
|
||||
*/
|
||||
namespace NAE\Functions\Curl;
|
||||
|
||||
/**
|
||||
* Lädt eine Dei herunter mit Hilfe von php-curl.
|
||||
* @param string $fileDestination Zielpfad
|
||||
* @param string $fileSource Quellpfad/URL
|
||||
* @return bool Download Erfolgreich? (only works when the file size is known upfront from headers)
|
||||
*/
|
||||
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"] ); // only works when the file size is known upfront from headers
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for Folder related classes and functions.
|
||||
* @package NAE\Functions\Folder
|
||||
*/
|
||||
namespace NAE\Functions\Folder;
|
||||
|
||||
/**
|
||||
* Recursive copy a directory and its contents to another directory (overwrites existing structures).
|
||||
* @param string $source Quellpfad/URL
|
||||
* @param string $destination Zielpfad
|
||||
*/
|
||||
function rcopy(string $source, string $destination) {
|
||||
|
||||
if (file_exists($destination)) {
|
||||
|
||||
rrmdir($destination);
|
||||
return;
|
||||
|
||||
}
|
||||
if (is_dir($source)) {
|
||||
|
||||
mkdir($destination);
|
||||
$files = scandir($source);
|
||||
foreach ($files as $file) {
|
||||
if ($file != "." && $file != "..") {
|
||||
rcopy("$source/$file", "$destination/$file");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
if (file_exists($source)) {
|
||||
|
||||
copy($source, $destination);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Namespace for Folder related classes and functions.
|
||||
* @package NAE\Functions\Folder
|
||||
*/
|
||||
namespace NAE\Functions\Folder;
|
||||
|
||||
/**
|
||||
* Recursive removes a directory and its contents
|
||||
* @param string $dir Target directory to be removed
|
||||
*/
|
||||
function rrmdir(string $dir) {
|
||||
|
||||
if (is_dir($dir)) {
|
||||
$files = scandir($dir);
|
||||
foreach ($files as $file)
|
||||
if ($file != "." && $file != "..") rrmdir("$dir/$file");
|
||||
rmdir($dir);
|
||||
|
||||
return;
|
||||
}
|
||||
if (file_exists($dir)) {
|
||||
unlink($dir);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue