diff --git a/resources/[Developer]/[Max]/Fx_autoupdate/client.lua b/resources/[Developer]/[Max]/Fx_autoupdate/client.lua new file mode 100644 index 000000000..7638499cd --- /dev/null +++ b/resources/[Developer]/[Max]/Fx_autoupdate/client.lua @@ -0,0 +1,6 @@ +-- Client-seitige Funktionen (falls benötigt) +RegisterNetEvent('fx-updater:showUpdateNotification') +AddEventHandler('fx-updater:showUpdateNotification', function(message) + -- Hier könntest du eine schöne UI-Benachrichtigung anzeigen + TriggerEvent('chatMessage', '[FX-UPDATER]', {255, 165, 0}, message) +end) diff --git a/resources/[Developer]/[Max]/Fx_autoupdate/config.lua b/resources/[Developer]/[Max]/Fx_autoupdate/config.lua new file mode 100644 index 000000000..78001b1c9 --- /dev/null +++ b/resources/[Developer]/[Max]/Fx_autoupdate/config.lua @@ -0,0 +1,35 @@ +Config = {} + +-- Update Einstellungen +Config.CheckInterval = 60 * 60 * 1000 -- Prüfe alle 60 Minuten (in ms) +Config.AutoUpdate = false -- Automatisches Update (false = nur benachrichtigen) +Config.RequiredArtifact = 18214 -- Gewünschte FX Version +Config.BackupEnabled = true -- Backup vor Update erstellen + +-- Berechtigungen +Config.AllowedGroups = { + 'god', + 'admin', + 'superadmin' +} + +-- Pfade (relativ zum Server-Root) +Config.ServerPath = './' -- Server Hauptordner +Config.BackupPath = './backups/' -- Backup Ordner + +-- Benachrichtigungen +Config.Notifications = { + updateAvailable = 'Neues FXServer Update verfügbar! Artifact: %s', + updateStarted = 'FXServer Update wird gestartet...', + updateCompleted = 'FXServer Update abgeschlossen! Server wird neu gestartet.', + updateFailed = 'FXServer Update fehlgeschlagen: %s', + noPermission = 'Du hast keine Berechtigung für diesen Befehl!', + currentVersion = 'Aktuelle FXServer Version: %s' +} + +-- Discord Webhook (optional) +Config.DiscordWebhook = { + enabled = false, + url = '', -- Deine Discord Webhook URL + botName = 'FXServer Updater' +} diff --git a/resources/[Developer]/[Max]/Fx_autoupdate/fxmanifest.lua b/resources/[Developer]/[Max]/Fx_autoupdate/fxmanifest.lua new file mode 100644 index 000000000..4042c9a9f --- /dev/null +++ b/resources/[Developer]/[Max]/Fx_autoupdate/fxmanifest.lua @@ -0,0 +1,21 @@ +fx_version 'cerulean' +game 'gta5' + +author 'Duck' +description 'FXServer Auto-Update' +version '1.0.0' + +shared_scripts { + 'config.lua' +} + +server_scripts { + 'main.lua' +} + +client_scripts { + 'client.lua' +} + +-- Nur Admins können das Script verwenden +server_only 'yes' diff --git a/resources/[Developer]/[Max]/Fx_autoupdate/main.lua b/resources/[Developer]/[Max]/Fx_autoupdate/main.lua new file mode 100644 index 000000000..945c19a8d --- /dev/null +++ b/resources/[Developer]/[Max]/Fx_autoupdate/main.lua @@ -0,0 +1,265 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local currentArtifact = GetConvar('version', 'unknown') +local updateInProgress = false + +-- Hilfsfunktionen +local function hasPermission(source) + local Player = QBCore.Functions.GetPlayer(source) + if not Player then return false end + + for _, group in pairs(Config.AllowedGroups) do + if QBCore.Functions.HasPermission(source, group) then + return true + end + end + return false +end + +local function sendDiscordLog(message) + if not Config.DiscordWebhook.enabled then return end + + PerformHttpRequest(Config.DiscordWebhook.url, function(err, text, headers) end, 'POST', json.encode({ + username = Config.DiscordWebhook.botName, + content = message + }), { ['Content-Type'] = 'application/json' }) +end + +local function notifyAdmins(message) + local players = QBCore.Functions.GetPlayers() + for _, playerId in pairs(players) do + if hasPermission(playerId) then + TriggerClientEvent('QBCore:Notify', playerId, message, 'primary', 10000) + end + end +end + +local function getCurrentArtifact() + -- Versuche aktuelle Version aus verschiedenen Quellen zu ermitteln + local version = GetConvar('version', '') + if version and version ~= '' then + local artifact = string.match(version, 'v1%.0%.0%.(%d+)') + if artifact then + return tonumber(artifact) + end + end + return nil +end + +local function checkForUpdates() + if updateInProgress then return end + + print('[FX-Updater] Prüfe auf Updates...') + + PerformHttpRequest('https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/', function(errorCode, resultData, resultHeaders) + if errorCode == 200 then + -- Parse die neueste Artifact-Nummer aus der HTML-Antwort + local latestArtifact = nil + for artifact in string.gmatch(resultData, '(%d+)%-[a-f0-9]+/') do + local num = tonumber(artifact) + if not latestArtifact or num > latestArtifact then + latestArtifact = num + end + end + + if latestArtifact and latestArtifact >= Config.RequiredArtifact then + local currentArt = getCurrentArtifact() + if not currentArt or latestArtifact > currentArt then + local message = string.format(Config.Notifications.updateAvailable, latestArtifact) + print('[FX-Updater] ' .. message) + notifyAdmins(message) + sendDiscordLog('🔄 ' .. message) + + if Config.AutoUpdate then + Citizen.SetTimeout(5000, function() + performUpdate(latestArtifact) + end) + end + end + end + else + print('[FX-Updater] Fehler beim Prüfen auf Updates: ' .. errorCode) + end + end, 'GET', '', {}) +end + +local function createBackup() + if not Config.BackupEnabled then return true end + + local timestamp = os.date('%Y%m%d_%H%M%S') + local backupDir = Config.BackupPath .. 'backup_' .. timestamp + + -- Erstelle Backup-Verzeichnis + os.execute('mkdir -p "' .. backupDir .. '"') + + -- Sichere wichtige Dateien + os.execute('cp server.cfg "' .. backupDir .. '/"') + os.execute('cp -r resources "' .. backupDir .. '/"') + os.execute('cp -r server-data "' .. backupDir .. '/" 2>/dev/null || true') + + print('[FX-Updater] Backup erstellt: ' .. backupDir) + return true +end + +function performUpdate(targetArtifact) + if updateInProgress then return end + updateInProgress = true + + print('[FX-Updater] Starte Update auf Artifact ' .. targetArtifact) + notifyAdmins(Config.Notifications.updateStarted) + sendDiscordLog('🔄 Update gestartet auf Artifact ' .. targetArtifact) + + -- Warnung an alle Spieler + TriggerClientEvent('chatMessage', -1, '[SERVER]', {255, 165, 0}, 'Server wird in 30 Sekunden für ein Update neu gestartet!') + + Citizen.SetTimeout(30000, function() + -- Backup erstellen + if not createBackup() then + print('[FX-Updater] Backup fehlgeschlagen!') + updateInProgress = false + return + end + + -- Download-URL erstellen + local downloadUrl = string.format( + 'https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/%d-*/fx.tar.xz', + targetArtifact + ) + + -- Update-Script erstellen und ausführen + local updateScript = [[ +#!/bin/bash +echo "FXServer Update gestartet..." + +# Download neue Version +wget -O fx-new.tar.xz "]] .. downloadUrl .. [[" + +if [ $? -eq 0 ]; then + # Entpacken + mkdir -p temp-update + cd temp-update + tar -xf ../fx-new.tar.xz + cd .. + + # Installation + cp temp-update/run.sh ./ + cp -r temp-update/alpine ./ + chmod +x run.sh + chmod +x alpine/opt/cfx-server/FXServer + + # Aufräumen + rm -rf temp-update fx-new.tar.xz + + echo "Update abgeschlossen, starte Server..." + # Server neu starten + ./run.sh +exec server.cfg & +else + echo "Download fehlgeschlagen!" + exit 1 +fi +]] + + -- Script in Datei schreiben + local file = io.open('update_fx.sh', 'w') + if file then + file:write(updateScript) + file:close() + + -- Script ausführbar machen und ausführen + os.execute('chmod +x update_fx.sh') + + -- Server beenden und Update starten + print('[FX-Updater] Server wird beendet für Update...') + notifyAdmins(Config.Notifications.updateCompleted) + sendDiscordLog('✅ Update wird durchgeführt, Server startet neu...') + + Citizen.SetTimeout(2000, function() + os.execute('./update_fx.sh') + os.exit() -- Server beenden + end) + else + print('[FX-Updater] Konnte Update-Script nicht erstellen!') + updateInProgress = false + end + end) +end + +-- Events +RegisterNetEvent('fx-updater:checkUpdate', function() + local src = source + if not hasPermission(src) then + TriggerClientEvent('QBCore:Notify', src, Config.Notifications.noPermission, 'error') + return + end + + checkForUpdates() +end) + +RegisterNetEvent('fx-updater:forceUpdate', function(targetArtifact) + local src = source + if not hasPermission(src) then + TriggerClientEvent('QBCore:Notify', src, Config.Notifications.noPermission, 'error') + return + end + + targetArtifact = targetArtifact or Config.RequiredArtifact + performUpdate(targetArtifact) +end) + +RegisterNetEvent('fx-updater:getVersion', function() + local src = source + if not hasPermission(src) then + TriggerClientEvent('QBCore:Notify', src, Config.Notifications.noPermission, 'error') + return + end + + local current = getCurrentArtifact() or 'Unbekannt' + local message = string.format(Config.Notifications.currentVersion, current) + TriggerClientEvent('QBCore:Notify', src, message, 'primary') +end) + +-- Commands +QBCore.Commands.Add('fxupdate', 'Prüfe auf FXServer Updates', {}, false, function(source, args) + if not hasPermission(source) then + TriggerClientEvent('QBCore:Notify', source, Config.Notifications.noPermission, 'error') + return + end + + TriggerEvent('fx-updater:checkUpdate') +end, Config.AllowedGroups) + +QBCore.Commands.Add('fxforceupdate', 'Erzwinge FXServer Update', {{name = 'artifact', help = 'Artifact Nummer (optional)'}}, false, function(source, args) + if not hasPermission(source) then + TriggerClientEvent('QBCore:Notify', source, Config.Notifications.noPermission, 'error') + return + end + + local targetArtifact = args[1] and tonumber(args[1]) or Config.RequiredArtifact + TriggerEvent('fx-updater:forceUpdate', targetArtifact) +end, Config.AllowedGroups) + +QBCore.Commands.Add('fxversion', 'Zeige aktuelle FXServer Version', {}, false, function(source, args) + if not hasPermission(source) then + TriggerClientEvent('QBCore:Notify', source, Config.Notifications.noPermission, 'error') + return + end + + TriggerEvent('fx-updater:getVersion') +end, Config.AllowedGroups) + +-- Auto-Check Timer +if Config.CheckInterval > 0 then + Citizen.CreateThread(function() + while true do + Citizen.Wait(Config.CheckInterval) + checkForUpdates() + end + end) +end + +-- Beim Server-Start prüfen +Citizen.CreateThread(function() + Citizen.Wait(10000) -- Warte 10 Sekunden nach Server-Start + checkForUpdates() +end) + +print('[FX-Updater] Resource gestartet!')