diff --git a/minecraft/Scripts/minecraft-template-backup-routine.sh b/minecraft/Scripts/minecraft-template-backup-routine.sh index c46b616..46b3fb0 100644 --- a/minecraft/Scripts/minecraft-template-backup-routine.sh +++ b/minecraft/Scripts/minecraft-template-backup-routine.sh @@ -72,6 +72,10 @@ FORMAT="tar" COMPRESSION="gzip" PROGRESS_INTERVAL=5 # default to 5 seconds +CHECK_USER=false +USER_ACTIVITY_FILE="UserActivity" + + # Usage usage() { cat <] [--full] [--destination ] [--pure --pure Use rsync to copy files (no compression, symlinks resolved) --format X Archive format: tar or zip (ignored if --pure) --compression X Compression for tar (default: gzip) + --check-user Only run backup if unbackuped user activity exists + EOF exit 1 } @@ -102,6 +108,8 @@ while [[ $# -gt 0 ]]; do --format) echo "[DEBUG] Flag: --format $2"; FORMAT="$2"; shift 2 ;; --compression) echo "[DEBUG] Flag: --compression $2"; COMPRESSION="$2"; shift 2 ;; --progressInterval) echo "[DEBUG] Flag: --progressInterval $2"; PROGRESS_INTERVAL="$2"; shift 2 ;; + --check-user) echo "[DEBUG] Flag: --check-user"; CHECK_USER=true; shift ;; + --help) usage ;; *) echo "[ERROR] Unknown option: $1"; usage ;; esac @@ -193,6 +201,37 @@ countdown() { } + +check_user_activity() { + local activity_file="$DATA_DIR/$SERVER_NAME/$USER_ACTIVITY_FILE" + + if [[ ! -f "$activity_file" ]]; then + echo "[WARN] User activity file not found: $activity_file" + return 1 + fi + + # Find unbackuped login lines + if ! grep -E 'was logged in' "$activity_file" | grep -vq '\[backuped\]'; then + echo "[INFO] No unbackuped user activity detected." + return 1 + fi + + echo "[INFO] Unbackuped user activity detected." + return 0 +} + +mark_user_activity_backuped() { + local activity_file="$DATA_DIR/$SERVER_NAME/$USER_ACTIVITY_FILE" + + # Append [backuped] to all unbackuped login lines + sed -i \ + -e '/was logged in/{ + /\[backuped\]/! s/$/ [backuped]/ + }' \ + "$activity_file" +} + + do_backup() { local backup_source="" local backup_destination="" @@ -347,6 +386,15 @@ do_backup() { #echo "[DEBUG] FULL=$FULL" +if [[ "$CHECK_USER" == true ]]; then + echo "[INFO] Running in --check-user mode" + if ! check_user_activity; then + echo "[INFO] Skipping backup due to no user activity." + exit 0 + fi +fi + + if [[ "$FULL" == true ]]; then BACKUP_SOURCE="${SERVER_NAME}" BACKUP_MODE="full server directory" @@ -374,6 +422,11 @@ if do_backup \ --compression "$COMPRESSION" \ --format "$FORMAT"; then echo "[INFO] Backup finished successfully." + if [[ "$CHECK_USER" == true ]]; then + mark_user_activity_backuped + echo "[INFO] User activity marked as backuped." + fi + else echo "[ERROR] Backup failed!" exit 1 diff --git a/minecraft/Scripts/minecraft-template-user-activity.sh b/minecraft/Scripts/minecraft-template-user-activity.sh new file mode 100644 index 0000000..b7381f8 --- /dev/null +++ b/minecraft/Scripts/minecraft-template-user-activity.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +DATA_DIR="@DATA_DIR@" +SERVER_NAME="@SERVER_NAME@" + +# Provided by systemd Environment +QUERY_BIN="${QUERY_BIN:-minecraft-${SERVER_NAME}-query}" + +ACTIVITY_FILE="$DATA_DIR/$SERVER_NAME/UserActivity" +TIMESTAMP="$(date '+%Y-%m-%d %H:%M:%S')" + +mkdir -p "$(dirname "$ACTIVITY_FILE")" +touch "$ACTIVITY_FILE" + +OUTPUT="$($QUERY_BIN || true)" + +PLAYER_LINE="$(echo "$OUTPUT" | grep '^players:' || true)" + +ONLINE="$(echo "$PLAYER_LINE" | awk '{print $2}' | cut -d/ -f1)" + +if [[ -z "$ONLINE" || "$ONLINE" == "0" ]]; then + echo "[$TIMESTAMP] No user detected" >> "$ACTIVITY_FILE" + exit 0 +fi + +PLAYERS="$(echo "$PLAYER_LINE" | sed -n 's/.*\[\(.*\)\]/\1/p')" + +IFS=',' read -ra PLAYER_ARRAY <<< "$PLAYERS" + +for player in "${PLAYER_ARRAY[@]}"; do + player="$(echo "$player" | xargs)" + [[ -n "$player" ]] && echo "[$TIMESTAMP] $player was logged in" >> "$ACTIVITY_FILE" +done diff --git a/minecraft/minecraft.nix b/minecraft/minecraft.nix index 4ae0c27..7250d56 100644 --- a/minecraft/minecraft.nix +++ b/minecraft/minecraft.nix @@ -291,6 +291,30 @@ in { description = "Declarative Minecraft server.properties values."; }; + # userActivity = { + # enable = mkOption { + # type = types.bool; + # default = false; + # description = '' + # Enable periodic user activity logging for this server. + # Writes to //UserActivity and is used by + # backup --check-user. + # ''; + # }; + + # interval = mkOption { + # type = types.str; + # default = "5min"; + # example = "1min"; + # description = '' + # How often user activity should be logged. + # Uses systemd.time format (e.g. 30s, 1min, 5min). + # ''; + # }; + # }; + + + schedules = mkOption { type = types.attrsOf (types.submodule ({name, ...}: { options = { @@ -457,6 +481,46 @@ in { cfg.servers ); + +# systemd.services = lib.mkMerge ( +# lib.mapAttrsToList (serverName: serverCfg: +# lib.mkIf serverCfg.userActivity.enable { +# "minecraft-${serverName}-user-activity" = { +# description = "Minecraft ${serverName} user activity logger"; +# serviceConfig = { +# Type = "oneshot"; +# User = cfg.user; +# Group = cfg.group; +# Environment = [ +# "QUERY_BIN=${mkScript serverName serverCfg "query"}/bin/minecraft-${serverName}-query" +# ]; +# ExecStart = +# "${mkScript serverName serverCfg "user-activity"}/bin/minecraft-${serverName}-user-activity"; +# }; +# }; +# } +# ) cfg.servers +# ); + + +# systemd.timers = lib.mkMerge ( +# lib.mapAttrsToList (serverName: serverCfg: +# lib.mkIf serverCfg.userActivity.enable { +# "minecraft-${serverName}-user-activity" = { +# description = "Timer for Minecraft ${serverName} user activity logging"; +# wantedBy = [ "timers.target" ]; +# timerConfig = { +# OnBootSec = "2min"; +# OnUnitActiveSec = serverCfg.userActivity.interval; +# AccuracySec = "30s"; +# }; +# }; +# } +# ) cfg.servers +# ); + + + # this is building the scripts for the user # Those are the prewritten scripts from the ./Script dir environment.systemPackages = lib.flatten ( @@ -466,6 +530,7 @@ in { (mkScript serverName serverCfg "backup") (mkScript serverName serverCfg "say") (mkScript serverName serverCfg "backup-routine") + (mkScript serverName serverCfg "user-activity") ]) cfg.servers );