diff --git a/.env.example b/.env.example index 4303144..6ea5fd8 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,19 @@ -# Скопируйте в .env (install.sh создаст .env сам). -# В паролях для docker compose символ $ нужно удваивать: pa$$word +# PostgreSQL POSTGRES_USER=l2user POSTGRES_PASSWORD=change_me_strong_password POSTGRES_DB=l2essence POSTGRES_PORT=5432 -# Образ игрового сервера (когда будет свой реестр/локальная сборка) -# L2_SERVER_IMAGE=your-registry/l2-essence-542:latest +# Клиент Essence protocol 520 +L2_PROTOCOL=520 + +# Порты (профиль docker compose --profile l2) +L2_LOGIN_PORT=2106 +L2_LOGIN_GS_PORT=9014 +L2_GAME_PORT=7777 + +# Образы Java для Login/Game (если используете profile l2) +# L2_LOGIN_IMAGE=eclipse-temurin:21-jre +# L2_GAME_IMAGE=eclipse-temurin:21-jre + +# В паролях для docker compose символ $ удваивайте: pa$$word diff --git a/config/game/Server.properties b/config/game/Server.properties new file mode 100644 index 0000000..6b9f98f --- /dev/null +++ b/config/game/Server.properties @@ -0,0 +1,24 @@ +# Lineage 2 Essence — Game Server (шаблон, клиент protocol 520) +# Скопируйте в dist/game/config/ вашей сборки. + +LoginHost = login +LoginPort = 9014 +GameserverHostname = * +GameserverPort = 7777 + +RequestServerID = 1 +AcceptAlternateID = True +MaximumOnlineUsers = 500 +DatapackRoot = . + +# Только клиент 520 (Essence) +AllowedProtocolRevisions = 520 + +Driver = org.postgresql.Driver +URL = jdbc:postgresql://postgres:5432/l2essence +Login = l2user +Password = change_me_strong_password +MaximumDbConnections = 100 + +CharMaxNumber = 7 +CnameTemplate = .* diff --git a/config/game/hexid.txt b/config/game/hexid.txt new file mode 100644 index 0000000..de35cb1 --- /dev/null +++ b/config/game/hexid.txt @@ -0,0 +1,3 @@ +# HexID = RequestServerID 1 (должен совпадать с gameservers.hexid в БД) +HexID=a1b2c3d4e5f6789012345678901234ef +ServerID=1 diff --git a/config/game/ipconfig.xml b/config/game/ipconfig.xml new file mode 100644 index 0000000..22c27b9 --- /dev/null +++ b/config/game/ipconfig.xml @@ -0,0 +1,4 @@ + + + + diff --git a/config/login/LoginServer.properties b/config/login/LoginServer.properties new file mode 100644 index 0000000..b08ec16 --- /dev/null +++ b/config/login/LoginServer.properties @@ -0,0 +1,24 @@ +# Lineage 2 Essence — Login Server (шаблон под Docker + PostgreSQL 17) +# Скопируйте в dist/login/config/ вашей сборки Samurai Crow / Mobius Essence. + +LoginserverHostname = * +LoginserverPort = 2106 +LoginHostname = 0.0.0.0 +LoginPort = 9014 + +LoginTryBeforeBan = 5 +LoginBlockAfterBan = 900 +AcceptNewGameServer = True +EnableFloodProtection = True +AutoCreateAccounts = True +ShowLicence = False + +# PostgreSQL (хост postgres — имя сервиса в docker-compose) +Driver = org.postgresql.Driver +URL = jdbc:postgresql://postgres:5432/l2essence +Login = l2user +Password = change_me_strong_password +MaximumDbConnections = 50 + +DatapackRoot = . +Debug = False diff --git a/config/login/ipconfig.xml b/config/login/ipconfig.xml new file mode 100644 index 0000000..0040237 --- /dev/null +++ b/config/login/ipconfig.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/config/login/servername.xml b/config/login/servername.xml new file mode 100644 index 0000000..0773643 --- /dev/null +++ b/config/login/servername.xml @@ -0,0 +1,4 @@ + + + + diff --git a/docker-compose.yml b/docker-compose.yml index 594621d..b22e01d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ -# Lineage 2 Essence 542 (Samurai Crow) — локальная среда -# Замените сервис l2-server на свой образ/сборку, когда будет готов Dockerfile. +# Lineage 2 Essence — клиент protocol 520, PostgreSQL 17 +# БД: login + game в одной БД. JAR Login/Game — в ./server (см. config/). services: postgres: @@ -22,23 +22,42 @@ services: timeout: 5s retries: 10 - # Заглушка: подключите свой сервер (Auth/Game) или замените build: на реальный Dockerfile. - l2-server: - image: ${L2_SERVER_IMAGE:-ubuntu:24.04} - container_name: l2_server_placeholder - restart: "no" + # Подключите сборку Samurai Crow / Mobius Essence (520): + # положите login/game в ./server/login и ./server/game, раскомментируйте сервисы. + login: + profiles: ["l2"] + image: ${L2_LOGIN_IMAGE:-eclipse-temurin:21-jre} + container_name: l2_login + restart: unless-stopped depends_on: postgres: condition: service_healthy - environment: - DATABASE_URL: postgresql://${POSTGRES_USER:-l2user}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-l2essence} - # Раскомментируйте при готовности бинарей/скриптов: - # volumes: - # - ./server:/opt/l2server:ro - # command: ["/opt/l2server/start.sh"] - command: ["bash", "-lc", "echo 'L2 server placeholder: замените сервис l2-server в docker-compose.yml'; sleep infinity"] - profiles: - - placeholder + working_dir: /opt/l2/login + volumes: + - ./server/login:/opt/l2/login + - ./config/login:/opt/l2/login/config:ro + ports: + - "${L2_LOGIN_PORT:-2106}:2106" + - "${L2_LOGIN_GS_PORT:-9014}:9014" + command: ["bash", "-lc", "test -f ./LoginServer.jar && java -jar LoginServer.jar || echo 'Нет LoginServer.jar в server/login'; sleep infinity"] + + game: + profiles: ["l2"] + image: ${L2_GAME_IMAGE:-eclipse-temurin:21-jre} + container_name: l2_game + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + login: + condition: service_started + working_dir: /opt/l2/game + volumes: + - ./server/game:/opt/l2/game + - ./config/game:/opt/l2/game/config:ro + ports: + - "${L2_GAME_PORT:-7777}:7777" + command: ["bash", "-lc", "test -f ./GameServer.jar && java -jar GameServer.jar || echo 'Нет GameServer.jar в server/game'; sleep infinity"] volumes: postgres_data: diff --git a/docker/initdb/01_login_essence520.sql b/docker/initdb/01_login_essence520.sql new file mode 100644 index 0000000..3f9f3b3 --- /dev/null +++ b/docker/initdb/01_login_essence520.sql @@ -0,0 +1,38 @@ +-- Login DB: L2J Mobius / Essence (совместимость с клиентом protocol 520). +-- Пароль: Base64(SHA1(UTF-8 пароль)) — см. LoginController Mobius. + +CREATE TABLE IF NOT EXISTS accounts ( + login VARCHAR(45) NOT NULL PRIMARY KEY, + password VARCHAR(45), + lastactive BIGINT, + "accessLevel" INTEGER NOT NULL DEFAULT 0, + "lastIP" VARCHAR(20), + "lastServer" INTEGER DEFAULT 1, + "pcIp" VARCHAR(20), + hop1 VARCHAR(20), + hop2 VARCHAR(20), + hop3 VARCHAR(20), + hop4 VARCHAR(20) +); + +CREATE TABLE IF NOT EXISTS account_data ( + account_name VARCHAR(45) NOT NULL, + var VARCHAR(20) NOT NULL, + value VARCHAR(255), + PRIMARY KEY (account_name, var), + CONSTRAINT fk_account_data_login FOREIGN KEY (account_name) + REFERENCES accounts (login) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS accounts_ipauth ( + login VARCHAR(45) NOT NULL, + ip VARCHAR(15) NOT NULL, + type VARCHAR(15) NOT NULL, + PRIMARY KEY (login, ip) +); + +CREATE TABLE IF NOT EXISTS gameservers ( + server_id INTEGER NOT NULL PRIMARY KEY, + hexid VARCHAR(50) NOT NULL, + host VARCHAR(50) NOT NULL +); diff --git a/docker/initdb/01_login_minimal.sql b/docker/initdb/01_login_minimal.sql deleted file mode 100644 index 08f0db5..0000000 --- a/docker/initdb/01_login_minimal.sql +++ /dev/null @@ -1,49 +0,0 @@ --- Минимальные таблицы под логин-сервер в стиле L2J (accounts / gameservers / account_data). --- Подходит для многих форков; у Samurai Crow/Essence могут отличаться имена колонок или хеш пароля — --- тогда сверьте с SQL из вашей сборки и поправьте этот файл. --- --- Тестовый логин: admin / admin --- Хеш: Base64(SHA1 от UTF-8 строки «admin») — типично для L2J/Mobius. --- Альтернатива (если сборка ждёт hex SHA1): замените поле password на --- d033e22ae348aeb5660fc2140aec35850c4da997 - -CREATE TABLE IF NOT EXISTS accounts ( - login VARCHAR(45) NOT NULL PRIMARY KEY, - password VARCHAR(128), - lastactive BIGINT, - accesslevel INTEGER NOT NULL DEFAULT 0, - lastip VARCHAR(45), - lastserver SMALLINT DEFAULT 1 -); - -CREATE TABLE IF NOT EXISTS account_data ( - account_name VARCHAR(45) NOT NULL, - var VARCHAR(50) NOT NULL, - value VARCHAR(255), - PRIMARY KEY (account_name, var), - CONSTRAINT fk_account_data_account FOREIGN KEY (account_name) - REFERENCES accounts (login) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS gameservers ( - hexid VARCHAR(255) NOT NULL, - server_id INTEGER NOT NULL PRIMARY KEY, - host VARCHAR(255) NOT NULL -); - -INSERT INTO accounts (login, password, lastactive, accesslevel, lastip, lastserver) -VALUES ( - 'admin', - '0DPiKuNIrrVmD8IUCuw1hQxNqZc=', - (EXTRACT(EPOCH FROM NOW()) * 1000)::bigint, - 0, - '127.0.0.1', - 1 -) -ON CONFLICT (login) DO NOTHING; - --- Игровой сервер при первом подключении к логину часто сам делает INSERT в gameservers. --- Если нужна явная запись (hexid должен совпасть с RequestServerID / hex в конфиге GS): --- INSERT INTO gameservers (hexid, server_id, host) --- VALUES ('ВАШ_32_СИМВОЛА_HEX', 1, '127.0.0.1') --- ON CONFLICT (server_id) DO UPDATE SET hexid = EXCLUDED.hexid, host = EXCLUDED.host; diff --git a/docker/initdb/02_game_essence520_minimal.sql b/docker/initdb/02_game_essence520_minimal.sql new file mode 100644 index 0000000..2bd4976 --- /dev/null +++ b/docker/initdb/02_game_essence520_minimal.sql @@ -0,0 +1,139 @@ +-- Game DB (та же БД): минимум для экрана выбора/создания персонажа и входа в мир. +-- Полный дамп datapack ставьте поверх, если сборка Samurai Crow требует больше таблиц. + +CREATE TABLE IF NOT EXISTS global_data ( + var VARCHAR(255) NOT NULL PRIMARY KEY, + value TEXT +); + +CREATE TABLE IF NOT EXISTS characters ( + account_name VARCHAR(45), + charId INTEGER NOT NULL PRIMARY KEY, + char_name VARCHAR(35) NOT NULL, + level SMALLINT DEFAULT 1, + maxHp INTEGER DEFAULT 100, + curHp INTEGER DEFAULT 100, + maxCp INTEGER DEFAULT 100, + curCp INTEGER DEFAULT 100, + maxMp INTEGER DEFAULT 100, + curMp INTEGER DEFAULT 100, + face SMALLINT DEFAULT 0, + hairStyle SMALLINT DEFAULT 0, + hairColor SMALLINT DEFAULT 0, + sex SMALLINT DEFAULT 0, + reputation INTEGER DEFAULT 0, + fame INTEGER DEFAULT 0, + raidbossPoints INTEGER DEFAULT 0, + pvpkills INTEGER DEFAULT 0, + pkkills INTEGER DEFAULT 0, + clanid INTEGER DEFAULT 0, + race SMALLINT DEFAULT 0, + classid INTEGER DEFAULT 0, + deletetime BIGINT DEFAULT 0, + title VARCHAR(21) DEFAULT '', + accesslevel INTEGER DEFAULT 0, + online SMALLINT DEFAULT 0, + onlinetime INTEGER DEFAULT 0, + char_slot SMALLINT DEFAULT 0, + lastAccess BIGINT DEFAULT 0, + clan_privs INTEGER DEFAULT 0, + wantspeace SMALLINT DEFAULT 0, + base_class INTEGER DEFAULT 0, + x INTEGER DEFAULT -114520, + y INTEGER DEFAULT -249704, + z INTEGER DEFAULT -2984, + heading INTEGER DEFAULT 0, + createDate DATE DEFAULT CURRENT_DATE, + isIn7sDungeon SMALLINT DEFAULT 0, + in_jail SMALLINT DEFAULT 0, + jail_timer INTEGER DEFAULT 0, + powerGrade SMALLINT DEFAULT 0, + apprentice INTEGER DEFAULT 0, + sponsor INTEGER DEFAULT 0, + clan_join_expiry_time TIMESTAMP NULL, + clan_create_expiry_time TIMESTAMP NULL, + bookmarkslot SMALLINT DEFAULT 0, + vitality_points INTEGER DEFAULT 35000, + language VARCHAR(5) DEFAULT 'en' +); + +CREATE INDEX IF NOT EXISTS idx_characters_account ON characters (account_name); +CREATE UNIQUE INDEX IF NOT EXISTS idx_characters_name ON characters (char_name); + +CREATE TABLE IF NOT EXISTS character_subclasses ( + charId INTEGER NOT NULL, + class_id INTEGER NOT NULL DEFAULT 0, + exp BIGINT DEFAULT 0, + sp BIGINT DEFAULT 0, + level INTEGER DEFAULT 1, + class_index INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (charId, class_index), + CONSTRAINT fk_subclasses_char FOREIGN KEY (charId) + REFERENCES characters (charId) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS character_skills ( + charId INTEGER NOT NULL, + skill_id INTEGER NOT NULL, + skill_level INTEGER NOT NULL DEFAULT 1, + class_index INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (charId, skill_id, class_index), + CONSTRAINT fk_skills_char FOREIGN KEY (charId) + REFERENCES characters (charId) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS character_shortcuts ( + charId INTEGER NOT NULL, + slot INTEGER NOT NULL, + page INTEGER NOT NULL, + type INTEGER DEFAULT 0, + shortcut_id INTEGER DEFAULT 0, + level INTEGER DEFAULT 0, + class_index INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (charId, slot, page, class_index), + CONSTRAINT fk_shortcuts_char FOREIGN KEY (charId) + REFERENCES characters (charId) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS character_variables ( + charId INTEGER NOT NULL, + var VARCHAR(255) NOT NULL, + val TEXT, + PRIMARY KEY (charId, var), + CONSTRAINT fk_variables_char FOREIGN KEY (charId) + REFERENCES characters (charId) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS items ( + object_id INTEGER NOT NULL PRIMARY KEY, + owner_id INTEGER NOT NULL, + item_id INTEGER NOT NULL, + count BIGINT NOT NULL DEFAULT 1, + enchant_level INTEGER DEFAULT 0, + loc VARCHAR(10) NOT NULL DEFAULT 'INVENTORY', + loc_data INTEGER DEFAULT 0 +); + +CREATE INDEX IF NOT EXISTS idx_items_owner ON items (owner_id); + +CREATE TABLE IF NOT EXISTS clan_data ( + clan_id INTEGER NOT NULL PRIMARY KEY, + clan_name VARCHAR(45) NOT NULL, + clan_level INTEGER DEFAULT 0, + reputation_score INTEGER DEFAULT 0, + hasCastle INTEGER DEFAULT 0, + blood_alliance INTEGER DEFAULT 0, + blood_oath INTEGER DEFAULT 0, + ally_id INTEGER DEFAULT 0, + ally_name VARCHAR(45) DEFAULT '', + leader_id INTEGER DEFAULT 0, + crest_id INTEGER DEFAULT 0, + crest_large_id INTEGER DEFAULT 0, + ally_crest_id INTEGER DEFAULT 0, + auction_bid_at INTEGER DEFAULT 0, + ally_penalty_expiry_time BIGINT DEFAULT 0, + ally_penalty_type SMALLINT DEFAULT 0, + char_penalty_expiry_time BIGINT DEFAULT 0, + dissolving_expiry_time BIGINT DEFAULT 0, + new_leader_id INTEGER DEFAULT 0 +); diff --git a/docker/initdb/02_grant_app_user.sh b/docker/initdb/02_grant_app_user.sh deleted file mode 100644 index 7940f5d..0000000 --- a/docker/initdb/02_grant_app_user.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# Выдаёт владельца таблиц логина роли из POSTGRES_USER (см. docker-compose). -set -eu -psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "$POSTGRES_DB" <<-EOSQL -ALTER TABLE IF EXISTS accounts OWNER TO "${POSTGRES_USER}"; -ALTER TABLE IF EXISTS account_data OWNER TO "${POSTGRES_USER}"; -ALTER TABLE IF EXISTS gameservers OWNER TO "${POSTGRES_USER}"; -EOSQL diff --git a/docker/initdb/03_seed_essence520.sql b/docker/initdb/03_seed_essence520.sql new file mode 100644 index 0000000..e365709 --- /dev/null +++ b/docker/initdb/03_seed_essence520.sql @@ -0,0 +1,33 @@ +-- Тестовые данные: Essence protocol 520 (логин + регистрация GS). +-- Логины: admin/admin, test/test +-- Hexid совпадает с server/hexid/default.txt + +INSERT INTO global_data (var, value) +VALUES ('essence_protocol', '520') +ON CONFLICT (var) DO UPDATE SET value = EXCLUDED.value; + +INSERT INTO accounts (login, password, lastactive, "accessLevel", "lastIP", "lastServer") +VALUES + ( + 'admin', + '0DPiKuNIrrVmD8IUCuw1hQxNqZc=', + (EXTRACT(EPOCH FROM NOW()) * 1000)::bigint, + 100, + '127.0.0.1', + 1 + ), + ( + 'test', + 'qUqP5cyxm6YcTAhz05Hph5gvu9M=', + (EXTRACT(EPOCH FROM NOW()) * 1000)::bigint, + 0, + '127.0.0.1', + 1 + ) +ON CONFLICT (login) DO NOTHING; + +-- RequestServerID = 1, hex из server/hexid/default.txt +INSERT INTO gameservers (server_id, hexid, host) +VALUES (1, 'a1b2c3d4e5f6789012345678901234ef', '127.0.0.1') +ON CONFLICT (server_id) DO UPDATE + SET hexid = EXCLUDED.hexid, host = EXCLUDED.host; diff --git a/docker/initdb/99_grant_app_user.sh b/docker/initdb/99_grant_app_user.sh new file mode 100644 index 0000000..6fd3469 --- /dev/null +++ b/docker/initdb/99_grant_app_user.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Владелец всех таблиц public → POSTGRES_USER (после всех *.sql). +set -eu +psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "$POSTGRES_DB" <<-EOSQL +DO \$\$ +DECLARE + t text; +BEGIN + FOR t IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' + LOOP + EXECUTE format('ALTER TABLE IF EXISTS %I OWNER TO %I', t, '${POSTGRES_USER}'); + END LOOP; +END \$\$; +EOSQL diff --git a/install.sh b/install.sh index bffd6de..fcf8a77 100644 --- a/install.sh +++ b/install.sh @@ -50,8 +50,8 @@ read_env_val() { # --- Шаг 0: приветствие --- step_header "Шаг 0 — Обзор" -echo "Проект: Lineage 2 Essence 542 (Samurai Crow), окружение в Docker." -echo "Будет поднят PostgreSQL 17; игровой сервис — по профилю placeholder (заглушка), пока нет своего образа." +echo "Проект: Lineage 2 Essence, клиент protocol 520, PostgreSQL 17." +echo "БД: login + game (минимум для входа). Login/Game JAR — профиль l2 (server/login, server/game)." if ! ask_yes_no "Продолжить?" "y"; then info "Выход." exit 0 @@ -161,6 +161,7 @@ fi step_header "Шаг 5 — Запуск PostgreSQL 17" if ask_yes_no "Поднять только PostgreSQL (рекомендуется)?" "y"; then chmod +x "$SCRIPT_DIR"/docker/initdb/*.sh 2>/dev/null || true + chmod +x "$SCRIPT_DIR"/scripts/*.sh 2>/dev/null || true docker compose up -d postgres info "Ожидание готовности PostgreSQL (pg_isready)..." EV_USER="$(read_env_val POSTGRES_USER)" @@ -174,11 +175,17 @@ if ask_yes_no "Поднять только PostgreSQL (рекомендуетс docker compose ps fi -# --- Шаг 6: заглушка L2 --- -step_header "Шаг 6 — Игровой контейнер (заглушка)" -warn "Сервис l2-server в compose помечен profile 'placeholder'. Замените образ/команду под Samurai Crow." -if ask_yes_no "Запустить контейнер-заглушку l2-server (для проверки сети к БД)?" "n"; then - docker compose --profile placeholder up -d l2-server +# --- Шаг 6: конфиг и Login/Game --- +step_header "Шаг 6 — Конфиг Essence 520" +if [[ -f "$SCRIPT_DIR/scripts/sync-config-password.sh" ]]; then + if ask_yes_no "Подставить пароль БД из .env в config/login и config/game?" "y"; then + bash "$SCRIPT_DIR/scripts/sync-config-password.sh" + fi +fi +step_header "Шаг 7 — Login / Game (Docker profile l2)" +warn "Положите LoginServer.jar и GameServer.jar в server/login и server/game." +if ask_yes_no "Запустить контейнеры login + game (profile l2)?" "n"; then + docker compose --profile l2 up -d login game docker compose ps fi @@ -189,8 +196,11 @@ EV_DB="$(read_env_val POSTGRES_DB)" EV_PORT="$(read_env_val POSTGRES_PORT)" info "Строка подключения с хоста Ubuntu: postgresql://${EV_USER}:***@127.0.0.1:${EV_PORT}/${EV_DB}" info "Из контейнеров в этой compose-сети: хост postgres, порт 5432." +info "Тест-аккаунты: admin/admin и test/test." +info "Клиент: protocol 520, логин 127.0.0.1:2106, мир 127.0.0.1:7777." echo "" info "Полезные команды:" echo " docker compose logs -f postgres" +echo " docker compose --profile l2 up -d login game" +echo " ./scripts/apply-essence-schema.sh # если БД уже была без новых таблиц" echo " docker compose down" -echo " docker compose --profile placeholder down" diff --git a/manage.sh b/manage.sh index 5dc5817..6b483d7 100644 --- a/manage.sh +++ b/manage.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# Интерактивное управление: остановка, запуск, перезапуск Docker-сервисов проекта. -# Запуск: chmod +x manage.sh && ./manage.sh +# Управление: PostgreSQL + Login/Game (Essence 520, profile l2). set -euo pipefail @@ -13,10 +12,10 @@ YELLOW='\033[1;33m' NC='\033[0m' ENV_FILE="$SCRIPT_DIR/.env" -PROFILE_PLACEHOLDER=(--profile placeholder) -# Имена из docker-compose.yml (для надёжной проверки состояния) +PROFILE_L2=(--profile l2) PG_CONTAINER="l2_postgres" -L2_CONTAINER="l2_server_placeholder" +LOGIN_CONTAINER="l2_login" +GAME_CONTAINER="l2_game" read_env_val() { local key="$1" @@ -50,144 +49,107 @@ step_header() { echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" } -require_cmd() { - command -v "$1" >/dev/null 2>&1 -} - ensure_docker() { - if ! require_cmd docker || ! docker compose version >/dev/null 2>&1; then - err "Нужны Docker и плагин «docker compose». Установите через ./install.sh" + if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then + err "Нужны Docker и docker compose. Запустите ./install.sh" exit 1 fi if ! docker info >/dev/null 2>&1; then - err "Docker недоступен (sudo или перелогин после группы docker)." + err "Docker недоступен." exit 1 fi } ensure_env() { if [[ ! -f "$ENV_FILE" ]]; then - err "Нет файла .env. Сначала выполните ./install.sh" + err "Нет .env. Сначала ./install.sh" exit 1 fi } -postgres_running() { - [[ "$(docker inspect -f '{{.State.Running}}' "$PG_CONTAINER" 2>/dev/null || echo false)" == "true" ]] +container_running() { + [[ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null || echo false)" == "true" ]] } -l2_running() { - [[ "$(docker inspect -f '{{.State.Running}}' "$L2_CONTAINER" 2>/dev/null || echo false)" == "true" ]] -} +postgres_running() { container_running "$PG_CONTAINER"; } +login_running() { container_running "$LOGIN_CONTAINER"; } +game_running() { container_running "$GAME_CONTAINER"; } -# --- Шаг 0 --- -step_header "Шаг 0 — Управление сервером (Docker Compose)" -echo "Проект: PostgreSQL 17 + опционально l2-server (profile placeholder)." +step_header "Шаг 0 — Essence 520 (PostgreSQL + Login/Game)" if ! ask_yes_no "Продолжить?" "y"; then - info "Выход." exit 0 fi ensure_docker ensure_env -# --- Шаг 1: статус --- -step_header "Шаг 1 — Текущий статус" +step_header "Шаг 1 — Статус" if ask_yes_no "Показать docker compose ps?" "y"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" ps -a || true + docker compose ps -a || true + docker compose "${PROFILE_L2[@]}" ps -a 2>/dev/null || true fi -# --- Шаг 2: остановка --- step_header "Шаг 2 — Остановка" -if ask_yes_no "Остановить контейнер l2-server (если был запущен с profile placeholder)?" "n"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" stop l2-server 2>/dev/null || warn "l2-server не остановлен (возможно не запускался)." +if ask_yes_no "Остановить game и login (profile l2)?" "n"; then + docker compose "${PROFILE_L2[@]}" stop game login 2>/dev/null || true fi - -if ask_yes_no "Остановить только PostgreSQL (docker compose stop postgres)?" "n"; then - docker compose stop postgres 2>/dev/null || warn "PostgreSQL уже остановлен или не создавался." +if ask_yes_no "Остановить PostgreSQL?" "n"; then + docker compose stop postgres 2>/dev/null || true fi - -if ask_yes_no "Полная остановка проекта: docker compose down (все сервисы, сеть; тома БД не удаляются)?" "n"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" down - info "Контейнеры остановлены. Данные БД в томе postgres_data сохранены." +if ask_yes_no "docker compose down (все сервисы, том БД сохраняется)?" "n"; then + docker compose "${PROFILE_L2[@]}" down + docker compose down fi - -if ask_yes_no "Удалить том с данными PostgreSQL (docker compose down -v)? ОПАСНО: потеря БД." "n"; then - warn "Будет выполнено: docker compose --profile placeholder down -v" - if ask_yes_no "Точно удалить тома?" "n"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" down -v - info "Тома удалены." - else - info "Отмена удаления томов." +if ask_yes_no "Удалить том БД (down -v)? ОПАСНО." "n"; then + if ask_yes_no "Точно удалить?" "n"; then + docker compose "${PROFILE_L2[@]}" down -v + docker compose down -v fi fi -# --- Шаг 3: запуск --- step_header "Шаг 3 — Запуск" -if ask_yes_no "Запустить / обновить PostgreSQL (up -d postgres)?" "n"; then +if ask_yes_no "Запустить PostgreSQL?" "n"; then docker compose up -d postgres - info "Ожидание pg_isready..." EV_USER="$(read_env_val POSTGRES_USER)" EV_DB="$(read_env_val POSTGRES_DB)" for _ in $(seq 1 60); do - if docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1; then - break - fi + docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1 && break sleep 1 done fi - -if ask_yes_no "Запустить l2-server (placeholder, нужен здоровый PostgreSQL)?" "n"; then - if ! postgres_running; then - warn "PostgreSQL не запущен. Сначала поднимите postgres." +if ask_yes_no "Запустить login + game (profile l2)?" "n"; then + if postgres_running; then + docker compose "${PROFILE_L2[@]}" up -d login game else - docker compose "${PROFILE_PLACEHOLDER[@]}" up -d l2-server + warn "Сначала поднимите postgres." fi fi -# --- Шаг 4: перезапуск --- step_header "Шаг 4 — Перезапуск" -if ask_yes_no "Перезапустить PostgreSQL (restart postgres)?" "n"; then +if ask_yes_no "Перезапустить PostgreSQL?" "n"; then if postgres_running; then docker compose restart postgres - info "PostgreSQL перезапущен." else - warn "PostgreSQL не в состоянии running. Выполняю up -d postgres." docker compose up -d postgres fi fi - -if ask_yes_no "Перезапустить l2-server (restart l2-server)?" "n"; then - if l2_running; then - docker compose "${PROFILE_PLACEHOLDER[@]}" restart l2-server - info "l2-server перезапущен." - else - warn "l2-server не запущен. Пропуск." - fi +if ask_yes_no "Перезапустить login?" "n"; then + login_running && docker compose "${PROFILE_L2[@]}" restart login || warn "login не запущен." fi - -if ask_yes_no "Перезапустить весь стек: сначала down, затем up postgres (+ опционально l2)?" "n"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" down +if ask_yes_no "Перезапустить game?" "n"; then + game_running && docker compose "${PROFILE_L2[@]}" restart game || warn "game не запущен." +fi +if ask_yes_no "Полный перезапуск: down → postgres → login → game?" "n"; then + docker compose "${PROFILE_L2[@]}" down + docker compose down docker compose up -d postgres - EV_USER="$(read_env_val POSTGRES_USER)" - EV_DB="$(read_env_val POSTGRES_DB)" - for _ in $(seq 1 60); do - if docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1; then - break - fi - sleep 1 - done - if ask_yes_no "Сразу поднять l2-server (placeholder)?" "n"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" up -d l2-server - fi - info "Стек перезапущен." + sleep 3 + docker compose "${PROFILE_L2[@]}" up -d login game fi -# --- Итог --- step_header "Готово" -if ask_yes_no "Показать итоговый статус?" "y"; then - docker compose "${PROFILE_PLACEHOLDER[@]}" ps -a || true +if ask_yes_no "Показать статус?" "y"; then + docker compose ps -a + docker compose "${PROFILE_L2[@]}" ps -a 2>/dev/null || true fi - -info "Логи: docker compose logs -f postgres" -info "Выход из проекта без удаления томов: docker compose --profile placeholder down" +info "Логи: docker compose logs -f login game postgres" diff --git a/scripts/apply-essence-schema.sh b/scripts/apply-essence-schema.sh new file mode 100644 index 0000000..ea582c9 --- /dev/null +++ b/scripts/apply-essence-schema.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Применить SQL к уже существующей БД (если том postgres не пересоздавали). +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" +ENV_FILE="${ROOT}/.env" +[[ -f "$ENV_FILE" ]] || { echo "Нет .env — сначала ./install.sh"; exit 1; } +read_env() { grep -m1 "^${1}=" "$ENV_FILE" | sed "s/^${1}=//"; } +U="$(read_env POSTGRES_USER)" +DB="$(read_env POSTGRES_DB)" +export PGPASSWORD="$(read_env POSTGRES_PASSWORD)" +for f in "$ROOT"/docker/initdb/0*.sql; do + echo "==> $(basename "$f")" + docker compose exec -T postgres psql -v ON_ERROR_STOP=1 -U "$U" -d "$DB" -f - <"$f" +done +echo "Готово." diff --git a/scripts/sync-config-password.sh b/scripts/sync-config-password.sh new file mode 100644 index 0000000..fbf9606 --- /dev/null +++ b/scripts/sync-config-password.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Подставить POSTGRES_* из .env в config/login и config/game (без коммита .env). +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ROOT}/.env" +[[ -f "$ENV_FILE" ]] || { echo "Нет .env"; exit 1; } +read_env() { grep -m1 "^${1}=" "$ENV_FILE" | sed "s/^${1}=//"; } +U="$(read_env POSTGRES_USER)" +P="$(read_env POSTGRES_PASSWORD)" +DB="$(read_env POSTGRES_DB)" +for f in "$ROOT/config/login/LoginServer.properties" "$ROOT/config/game/Server.properties"; do + [[ -f "$f" ]] || continue + sed -i "s|^Login = .*|Login = $U|" "$f" + sed -i "s|^Password = .*|Password = $P|" "$f" + sed -i "s|jdbc:postgresql://postgres:5432/[^?]*|jdbc:postgresql://postgres:5432/$DB|" "$f" + echo "Обновлён: $f" +done diff --git a/server/game/.gitkeep b/server/game/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/hexid/default.txt b/server/hexid/default.txt new file mode 100644 index 0000000..1e757aa --- /dev/null +++ b/server/hexid/default.txt @@ -0,0 +1 @@ +a1b2c3d4e5f6789012345678901234ef diff --git a/server/login/.gitkeep b/server/login/.gitkeep new file mode 100644 index 0000000..e69de29