commit 070b02ea62b79ac473a7a33487cc5d8cc0f15079 Author: Brunobrno Date: Thu Oct 2 00:51:42 2025 +0200 init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f0e495 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +data/* +backups/*.tar.gz \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae01473 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +**Struktura** + +mc-server/ +├── data/ # Svět a soubory serveru (svázané přes Docker volume) +├── backups/ # Automatické zálohy +├── docker-compose.yml # Minecraft server v Dockeru +├── backup.sh # 🔁 Pravidelné zálohy světa +├── restore.sh # ♻️ Obnova zálohy podle výběru +├── start.sh # ▶️ Spuštění serveru +├── stop.sh # ⛔ Zastavení serveru (volitelné) +└── restart.sh # 🔄 Restart serveru (volitelné) + + + +**cron job:** + +*/15 * * * * /cesta/mc-server/backup.sh >> /cesta/mc-server/backup.log 2>&1 diff --git a/backup.sh b/backup.sh new file mode 100644 index 0000000..aea64f2 --- /dev/null +++ b/backup.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Absolutní cesta ke skriptu +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +CONTAINER="mc-vanilla" +BACKUP_DIR="/mnt/backups/minecraft_backups/frankstein" +WORLD_DIR="$DIR/data/world" + +DATE_FULL=$(date +%F_%H-%M) +DATE_HOUR=$(date +%F_%H) +DATE_DAY=$(date +%F) + +ARCHIVE_NAME="$BACKUP_DIR/world_$DATE_FULL.tar.gz" + +mkdir -p "$BACKUP_DIR" + +# Zakázání ukládání ve hře +docker exec -i $CONTAINER rcon-cli save-off +docker exec -i $CONTAINER rcon-cli save-all +sleep 3 + +# Vytvoření zálohy +tar -czf "$ARCHIVE_NAME" "$WORLD_DIR" + +# Znovupovolení ukládání +docker exec -i $CONTAINER rcon-cli save-on + +# Smazání všech záloh ze stejné hodiny, kromě nejnovější +find "$BACKUP_DIR" -name "world_${DATE_HOUR}_*.tar.gz" ! -newer "$ARCHIVE_NAME" -delete + +# Zachování pouze nejnovější zálohy z každého předchozího dne +find "$BACKUP_DIR" -name "world_*.tar.gz" | while read file; do + file_day=$(basename "$file" | cut -d'_' -f2) + newest=$(find "$BACKUP_DIR" -name "world_${file_day}_*.tar.gz" | sort | tail -n 1) + if [ "$file" != "$newest" ]; then + rm "$file" + fi +done diff --git a/backups/poznamka.md b/backups/poznamka.md new file mode 100644 index 0000000..2df9854 --- /dev/null +++ b/backups/poznamka.md @@ -0,0 +1 @@ +tady se budou nacházet zálohy serveru až se spustí! \ No newline at end of file diff --git a/discord-bot/.env-bot b/discord-bot/.env-bot new file mode 100644 index 0000000..c138ec3 --- /dev/null +++ b/discord-bot/.env-bot @@ -0,0 +1,5 @@ +TOKEN='MTI1ODE0NDgxODk3OTE0Nzg5Ng.GKTl0Q.4vzvSFiPs8tl93B42R7PcqpM70C2Ov0YXHQdVU' +GUILD_ID=938745671870185482 +CHANNEL_ID=1382456524009640057 +MC_HOST='147.185.221.29' +MC_PORT=7272 \ No newline at end of file diff --git a/discord-bot/Dockerfile b/discord-bot/Dockerfile new file mode 100644 index 0000000..e846a6b --- /dev/null +++ b/discord-bot/Dockerfile @@ -0,0 +1,11 @@ +# discord-bot/Dockerfile +FROM python:3.11-slim + +WORKDIR /discord-bot + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["python", "frankstein-bot.py"] diff --git a/discord-bot/frankstein-bot.py b/discord-bot/frankstein-bot.py new file mode 100644 index 0000000..bb3f570 --- /dev/null +++ b/discord-bot/frankstein-bot.py @@ -0,0 +1,52 @@ +import discord +from discord.ext import tasks, commands +from mcstatus import JavaServer + +import os + +TOKEN = os.environ['TOKEN'] +MC_HOST = os.environ.get('MC_HOST', 'localhost') +MC_PORT = int(os.environ.get('MC_PORT', 7272)) +CHANNEL_ID = int(os.environ['CHANNEL_ID']) + + + +intents = discord.Intents.default() +bot = commands.Bot(command_prefix='!', intents=intents) + +@bot.event +async def on_ready(): + print(f'Přihlášen jako {bot.user}') + update_status.start() + +@tasks.loop(seconds=5) +async def update_status(): + channel = bot.get_channel(CHANNEL_ID) + if channel is None: + print("Kanál nenalezen.") + return + + try: + server = JavaServer(MC_HOST, MC_PORT) + status = server.status() + + player_count = status.players.online + name = f'🟢|online|👤:{player_count}' + + except Exception as e: + print("Server offline nebo nelze připojit:", e) + name = '🔴|server down!!!' + + try: + if name != channel.name: + await channel.edit(name=name) + + + except discord.Forbidden: + + print("Bot nemá oprávnění měnit název kanálu.") + + except discord.HTTPException as e: + print("HTTP chyba:", e) + +bot.run(TOKEN) diff --git a/discord-bot/requirements.txt b/discord-bot/requirements.txt new file mode 100644 index 0000000..1bd44eb --- /dev/null +++ b/discord-bot/requirements.txt @@ -0,0 +1,3 @@ +discord.py +mcstatus +python-dotenv \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ab003e3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +services: + mc-vanilla: + image: itzg/minecraft-server + container_name: mc-vanilla + ports: + - "25565:25565" + environment: + TZ: "Europe/Prague" + #TYPE: "PAPER" + VERSION: "1.21.6" + EULA: "TRUE" + ENABLE_RCON: "true" + RCON_PASSWORD: "Revoluce@1989" + RCON_PORT: 25575 + MAX_PLAYERS: 6 + VIEW_DISTANCE: 20 + SIMULATION_DISTANCE: 10 + OVERRIDE_ICON: true + ICON: /icon.png + DIFFICULTY: hard + MEMORY: 10G + INIT_MEMORY: 10G + + volumes: + - ./data:/data + - ./icon.png:/icon.png + restart: unless-stopped + + frankstein-discord-bot: + image: python:3.11-slim + build: + context: ./discord-bot + dockerfile: Dockerfile + volumes: + - ./discord-bot:/discord-bot + env_file: + - discord-bot/.env-bot diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..6ffcdb5 Binary files /dev/null and b/icon.png differ diff --git a/minecraft_env_vars.md b/minecraft_env_vars.md new file mode 100644 index 0000000..46946e5 --- /dev/null +++ b/minecraft_env_vars.md @@ -0,0 +1,70 @@ + +Minecraft Server Docker Environment Variables (itzg/minecraft-server) +===================================================================== + +🧩 Server Configuration +----------------------- +EULA: Must be set to "TRUE" to accept the Minecraft EULA. +VERSION: Specify the Minecraft server version, e.g., "1.20.4". +TYPE: Server type, such as "VANILLA", "FORGE", "FABRIC", "PAPER", etc. +MOTD: Message of the day displayed in the server list. +DIFFICULTY: Game difficulty; options: "peaceful", "easy", "normal", "hard". +MAX_PLAYERS: Max number of players allowed. +VIEW_DISTANCE: Number of chunks sent to players around them. +ALLOW_NETHER: Enable or disable the Nether. +ENABLE_COMMAND_BLOCK: Enable or disable command blocks. +FORCE_GAMEMODE: Force players to join in the default game mode. +GENERATE_STRUCTURES: Enable or disable structure generation. +HARDCORE: Enable or disable hardcore mode. +MAX_BUILD_HEIGHT: Max build height. +SPAWN_ANIMALS: Enable or disable animal spawning. +SPAWN_MONSTERS: Enable or disable monster spawning. +SPAWN_NPCS: Enable or disable NPC spawning. +SPAWN_PROTECTION: Radius of spawn protection for non-ops. +LEVEL_NAME: Name of the world folder. +LEVEL_TYPE: "DEFAULT", "FLAT", "LARGEBIOMES", "AMPLIFIED", "CUSTOMIZED". +LEVEL_SEED: World seed. +PVP: Enable or disable player combat. +ONLINE_MODE: Enable/disable Mojang authentication. +ALLOW_FLIGHT: Allow/disallow flight. + +🛡️ Access Control +----------------- +WHITELIST: Comma-separated list of usernames. +OPS: Comma-separated list of operators. +ENABLE_WHITELIST: Enable/disable whitelist. +ENFORCE_WHITELIST: Always enforce whitelist. + +⚙️ Advanced Settings +-------------------- +ICON: URL or file path to server icon. +OVERRIDE_ICON: TRUE to override existing icon. +ENABLE_RCON: Enable/disable RCON. +RCON_PASSWORD: RCON password. +RCON_PORT: Default is 25575. +ENABLE_QUERY: Enable/disable GameSpy4 query. +QUERY_PORT: Default is 25565. +ENABLE_JMX: Enable/disable JMX. +JMX_PORT: Default is 7091. +USE_AIKAR_FLAGS: Use Aikar's JVM flags. +JVM_OPTS, JVM_XX_OPTS, JVM_DD_OPTS: JVM tuning. +EXTRA_ARGS: Extra server args. +LOG_TIMESTAMP: Include timestamps in logs. +ENABLE_ROLLING_LOGS: Enable rolling logs. + +🧪 Experimental/Modding +------------------------ +MODPACK, MODPACK_VERSION: Modpack configs. +MODRINTH_PROJECT, MODRINTH_VERSION: Modrinth config. +CURSEFORGE_PROJECT, CURSEFORGE_FILE_ID: CurseForge config. +AUTODOWNLOAD: Automatically download mods. + +🕒 Time and Locale +------------------ +TZ: Timezone, e.g., "Europe/Prague". + +🧰 Custom Properties +-------------------- +CUSTOM_SERVER_PROPERTIES: Newline-separated key=value for custom settings. + +See: https://github.com/itzg/docker-minecraft-server diff --git a/restart.sh b/restart.sh new file mode 100644 index 0000000..57690ad --- /dev/null +++ b/restart.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +SERVICE_NAME="mc-vanilla" + +echo "🔄 Restartuji server..." +docker compose up -d --force-recreate --build "$SERVICE_NAME" + + diff --git a/restore.sh b/restore.sh new file mode 100644 index 0000000..300cc08 --- /dev/null +++ b/restore.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +#relative path +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + +CONTAINER="mc-vanilla" +BACKUP_DIR="/mnt/backups/minecraft_backups/frankstein" +WORLD_DIR="$DIR/data/world" + +mapfile -t BACKUPS < <(ls -1t "$BACKUP_DIR"/world_*.tar.gz) +if [ ${#BACKUPS[@]} -eq 0 ]; then + echo "❌ Žádné zálohy nenalezeny!" + exit 1 +fi + +echo "📦 Dostupné zálohy:" +for i in "${!BACKUPS[@]}"; do + echo "[$i] ${BACKUPS[$i]}" +done + +read -p "🔁 Zadej číslo zálohy: " INDEX +SELECTED="${BACKUPS[$INDEX]}" +[ -z "$SELECTED" ] && echo "❌ Neplatný výběr." && exit 1 + +read -p "⚠️ Přepsat svět? (y/N): " CONFIRM +[[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]] && exit 0 + +docker compose stop "$CONTAINER" +rm -rf "$WORLD_DIR" +tar -xzf "$SELECTED" -C . + +read -p "▶️ Spustit server? (y/N): " AUTOSTART +[[ "$AUTOSTART" == "y" || "$AUTOSTART" == "Y" ]] && docker compose up -d --force-recreate "$CONTAINER" diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..4b72367 --- /dev/null +++ b/start.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +SERVICE_NAME="mc-vanilla" + +if docker ps --format '{{.Names}}' | grep -q "$SERVICE_NAME"; then + echo "✅ Server už běží." +else + echo "▶️ Spouštím server..." + docker compose up -d --force-recreate "$SERVICE_NAME" +fi diff --git a/stop.sh b/stop.sh new file mode 100644 index 0000000..c04196a --- /dev/null +++ b/stop.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +SERVICE_NAME="mc-vanilla" + +echo "⛔ Zastavuji server..." +docker compose stop "$SERVICE_NAME" +