diff --git a/README.md b/README.md index c60f9e6..9cb4af8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Au-delà du core amont, le royaume apporte ses propres systèmes : - **CMake 3.31+** - **Boost 1.84.0** -- **MySQL 8.0** +- **MySQL 8.0** ou **MariaDB 10.6+** (le royaume tourne sur MariaDB 11.4) - **OpenSSL 3.x** - **GCC / Clang / MSVC** (Visual Studio 2022 recommandé) diff --git a/sql/updates/characters/2026_08_25_00_characters.sql b/sql/updates/characters/2026_08_25_00_characters.sql new file mode 100644 index 0000000..cbf69ef --- /dev/null +++ b/sql/updates/characters/2026_08_25_00_characters.sql @@ -0,0 +1,85 @@ +-- +-- Siege des Capitales : les deux tables d etat vivent dans la base +-- characters, mais n existaient que dans sql/sylvania/capital_siege.sql, +-- hors du mecanisme de mise a jour automatique. Une installation neuve se +-- retrouvait donc avec un schema characters incomplet : le module ecrivait +-- dans des tables absentes. +-- +-- Ce fichier reprend le patch a l identique. Il est idempotent +-- (CREATE TABLE IF NOT EXISTS + INSERT IGNORE) : sans effet sur une base ou +-- le patch a deja ete passe a la main. +-- + +-- +-- SylvaniaCore - Module "Siege des Capitales" +-- Base : dc_characters (etat mutable ecrit par le worldserver, donc hors de la +-- base de contenu dc_world qui est reimportee a chaque mise a jour). +-- +-- Deux tables : +-- capital_siege_state : ligne unique, l ordonnancement du module. Elle porte +-- le verrou anti-rejeu quotidien et l alternance de +-- faction, qui doivent survivre a un redemarrage. +-- capital_siege_history : une ligne par evenement, ouverte au declenchement et +-- refermee a la fin (ou au demarrage suivant du core +-- si le serveur s est arrete en plein siege). +-- +-- Toutes les ecritures passent par des requetes preparees declarees dans +-- src/server/database/Database/Implementation/CharacterDatabase.{h,cpp}. +-- + +-- --------------------------------------------------------------------------- +-- Etat de l ordonnanceur (une seule ligne, id = 1) +-- --------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `capital_siege_state` ( + `id` TINYINT(3) UNSIGNED NOT NULL DEFAULT 1 + COMMENT 'Toujours 1 : la table ne contient qu une ligne', + `last_event_day` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Jour serveur (epoch local / 86400) du dernier evenement consomme. Verrou anti-rejeu quotidien', + `last_attacker_team` TINYINT(4) NOT NULL DEFAULT -1 + COMMENT 'Derniere faction attaquante : 0 = Alliance, 1 = Horde, -1 = aucune. Sert a l alternance stricte', + `scheduled_day` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Jour serveur du tirage courant', + `scheduled_time` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Horodatage unix du declenchement tire pour scheduled_day', + `scheduled_team` TINYINT(4) NOT NULL DEFAULT -1 + COMMENT 'Faction attaquante tiree pour scheduled_day : 0 = Alliance, 1 = Horde, -1 = aucune', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci + COMMENT='Siege des Capitales : ordonnancement persistant'; + +-- --------------------------------------------------------------------------- +-- Historique des evenements +-- --------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `capital_siege_history` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `start_time` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Horodatage unix du declenchement', + `end_time` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Horodatage unix de la fin, 0 tant que l evenement est en cours', + `attacker_team` TINYINT(4) NOT NULL DEFAULT -1 + COMMENT '0 = Alliance, 1 = Horde', + `target_map` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Carte de la capitale assaillie (0 = Hurlevent, 1 = Orgrimmar)', + `boss_entry` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'creature_template du dirigeant vise', + `outcome` TINYINT(4) NOT NULL DEFAULT 0 + COMMENT '0 = en cours, 1 = victoire, 2 = temps ecoule, 3 = annule GM, 4 = arret d urgence (charge), 5 = interrompu (arret du serveur)', + `duration` INT(10) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Duree effective en secondes', + `bots_spawned` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Nombre de bots effectivement deployes', + `bots_lost` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0 + COMMENT 'Nombre de bots tues pendant l assaut', + `triggered_by` VARCHAR(32) NOT NULL DEFAULT 'auto' + COMMENT 'auto = ordonnanceur, sinon nom du GM declencheur', + PRIMARY KEY (`id`), + KEY `idx_outcome` (`outcome`), + KEY `idx_start_time` (`start_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci + COMMENT='Siege des Capitales : historique des evenements'; + +-- Ligne d etat initiale. Aucune faction n a encore attaque : le premier +-- evenement sera donc mene par l Alliance. +INSERT IGNORE INTO `capital_siege_state` + (`id`, `last_event_day`, `last_attacker_team`, `scheduled_day`, `scheduled_time`, `scheduled_team`) + VALUES (1, 0, -1, 0, 0, -1); diff --git a/sylvania/scripts/db-release-github.sh b/sylvania/scripts/db-release-github.sh new file mode 100755 index 0000000..59ffb6d --- /dev/null +++ b/sylvania/scripts/db-release-github.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +# +# Publie les bases de contenu du royaume (dc_world + dc_hotfixes) comme assets +# d une Release GitHub sur BlaMacfly/SylvaniaCore. +# +# Pourquoi une Release et pas le depot : dc_world pese ~880 Mo en base (~78 Mo +# une fois dumpe et gzippe) et dc_hotfixes ~126 Mo. Git ne sait pas differencier +# une archive gzip : chaque rafraichissement alourdirait l historique de ~80 Mo +# a jamais, et GitHub refuse tout fichier de plus de 100 Mo. Les assets de +# Release, eux, vivent hors de l historique et montent a 2 Go piece. +# +# Contrairement a ~/scripts/db-backup-world.sh (sauvegarde chiffree, depot +# prive), ce script produit des dumps EN CLAIR destines a etre publics. Il ne +# touche jamais a dc_characters ni a dc_auth : ces bases contiennent des donnees +# de comptes et de personnages, et leurs schemas vierges sont deja fournis dans +# sql/base/ du depot. +# +# Prerequis : un jeton GitHub a portee fine (Contents: Read and write sur le +# seul depot SylvaniaCore) depose dans ~/.config/sylvania-github-token, en 600. +# +# Usage : +# db-release-github.sh --dry-run # dumpe et verifie, ne publie rien +# db-release-github.sh # dumpe et publie la Release +# db-release-github.sh --tag DB-SYLVANIA-20260901 +# +set -euo pipefail + +REPO_SLUG="${SYLVANIA_REPO_SLUG:-BlaMacfly/SylvaniaCore}" +TOKEN_FILE="${SYLVANIA_GH_TOKEN_FILE:-$HOME/.config/sylvania-github-token}" +WORKDIR="${SYLVANIA_RELEASE_WORKDIR:-$HOME/db-release}" +MIN_FREE_MB=2048 + +DATE="$(date '+%Y%m%d')" +TAG="DB-SYLVANIA-${DATE}" +DRY_RUN=0 + +while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=1; shift ;; + --tag) TAG="${2:?--tag attend une valeur}"; shift 2 ;; + -h|--help) sed -n '2,24p' "$0"; exit 0 ;; + *) echo "Option inconnue : $1" >&2; exit 2 ;; + esac +done + +die() { echo "ERREUR: $*" >&2; exit 1; } + +# --- Verifications preliminaires --------------------------------------------- +command -v mysqldump >/dev/null || die "mysqldump introuvable" +command -v curl >/dev/null || die "curl introuvable" +command -v gzip >/dev/null || die "gzip introuvable" + +mkdir -p "$WORKDIR" +free_mb=$(df -Pm "$WORKDIR" | awk 'NR==2 {print $4}') +[ "$free_mb" -ge "$MIN_FREE_MB" ] || die "espace disque insuffisant sur $WORKDIR : ${free_mb} Mo libres, ${MIN_FREE_MB} Mo requis" + +if [ "$DRY_RUN" -eq 0 ]; then + [ -s "$TOKEN_FILE" ] || die "jeton GitHub absent : $TOKEN_FILE (voir l en-tete de ce script)" + perms=$(stat -c '%a' "$TOKEN_FILE") + [ "$perms" = "600" ] || die "$TOKEN_FILE doit etre en 600 (actuellement $perms)" +fi + +# --- Dumps -------------------------------------------------------------------- +# Flags alignes sur ~/scripts/db-backup-world.sh, deja eprouves sur le serveur +# en production. Les tables `updates` et `updates_include` sont volontairement +# conservees : elles portent l historique deja applique, ce qui evite au core de +# rejouer les ~330 fichiers de sql/updates/world sur une base qui les contient +# deja. +declare -A DUMPS=( [world]=dc_world [hotfixes]=dc_hotfixes ) +ASSETS=() + +for name in world hotfixes; do + src="${DUMPS[$name]}" + out="${WORKDIR}/sylvania_${name}_${DATE}.sql.gz" + echo "==> dump ${src} -> $(basename "$out")" + + # Les tables de travail laissees par d anciennes maintenances (bak_*, tmp_*) + # ne sont pas du contenu : inutile de les publier. + ignore=() + while IFS= read -r t; do + [ -n "$t" ] && ignore+=( "--ignore-table=${src}.${t}" ) + done < <(sudo mysql -N -B -e "select table_name from information_schema.tables \ + where table_schema='${src}' \ + and (table_name like 'bak\\_%' or table_name like 'tmp\\_%');" 2>/dev/null) + [ ${#ignore[@]} -gt 0 ] && echo " ${#ignore[@]} table(s) de travail exclue(s)" + + # Portabilite MySQL : le serveur tourne sous MariaDB 11.4, dont la collation + # par defaut (utf8mb4_uca1400_*) n existe pas sous MySQL 8 -- un import s y + # arreterait sur "Unknown collation". utf8mb4_unicode_ci est reconnue par les + # deux moteurs et partage la meme semantique (UCA, insensible a la casse et + # aux accents). + if ! sudo mysqldump --single-transaction --quick --no-tablespaces \ + ${ignore[@]+"${ignore[@]}"} "$src" \ + | sed -E 's/utf8mb4_uca1400_[a-z_]+/utf8mb4_unicode_ci/g' \ + | gzip -c > "$out"; then + rm -f "$out"; die "le dump de ${src} a echoue" + fi + [ -s "$out" ] || die "dump vide pour ${src}" + # Un dump tronque (mysqldump interrompu) ne porte pas sa ligne de cloture. + if ! gzip -dc "$out" | tail -5 | grep -q "Dump completed"; then + rm -f "$out"; die "dump de ${src} tronque : ligne de cloture absente" + fi + if gzip -dc "$out" | grep -q "uca1400"; then + rm -f "$out"; die "collation MariaDB residuelle dans le dump de ${src}" + fi + ASSETS+=("$out") + echo " $(du -h "$out" | cut -f1)" +done + +# --- Empreintes --------------------------------------------------------------- +SUMS="${WORKDIR}/SHA256SUMS-${DATE}.txt" +( cd "$WORKDIR" && sha256sum "$(basename "${ASSETS[0]}")" "$(basename "${ASSETS[1]}")" ) > "$SUMS" +ASSETS+=("$SUMS") +cat "$SUMS" + +# --- Corps de la Release ------------------------------------------------------ +BODY_FILE="${WORKDIR}/body-${DATE}.md" +cat > "$BODY_FILE" < creation de la release ${TAG}" +payload=$(python3 - "$TAG" "$BODY_FILE" <<'PYEOF' +import json, sys, io +tag = sys.argv[1] +body = io.open(sys.argv[2], encoding="utf-8").read() +print(json.dumps({"tag_name": tag, "name": "Bases Sylvania " + tag.split("-")[-1], + "body": body, "draft": True, "prerelease": False})) +PYEOF +) +release_id=$(curl -sf "${auth[@]}" -X POST "${API}/releases" -d "$payload" \ + | sed -n 's/.*"id": \([0-9]*\).*/\1/p' | head -1) +[ -n "$release_id" ] || die "creation de la release impossible" + +for a in "${ASSETS[@]}"; do + echo "==> envoi de $(basename "$a")" + curl -sf "${auth[@]}" -H "Content-Type: application/octet-stream" \ + --data-binary @"$a" \ + "${UPLOAD}/releases/${release_id}/assets?name=$(basename "$a")" >/dev/null \ + || die "envoi de $(basename "$a") echoue" +done + +echo +echo "Release ${TAG} creee en BROUILLON : https://github.com/${REPO_SLUG}/releases" +echo "Relisez-la puis publiez-la depuis l interface GitHub."