Add Essence 520 minimal DB, configs, and login/game Docker profile.

This commit is contained in:
test
2026-05-18 12:54:47 +03:00
parent a4a3a2aa09
commit 6fdbc9b103
21 changed files with 438 additions and 172 deletions
+14 -4
View File
@@ -1,9 +1,19 @@
# Скопируйте в .env (install.sh создаст .env сам). # PostgreSQL
# В паролях для docker compose символ $ нужно удваивать: pa$$word
POSTGRES_USER=l2user POSTGRES_USER=l2user
POSTGRES_PASSWORD=change_me_strong_password POSTGRES_PASSWORD=change_me_strong_password
POSTGRES_DB=l2essence POSTGRES_DB=l2essence
POSTGRES_PORT=5432 POSTGRES_PORT=5432
# Образ игрового сервера (когда будет свой реестр/локальная сборка) # Клиент Essence protocol 520
# L2_SERVER_IMAGE=your-registry/l2-essence-542:latest 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
+24
View File
@@ -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 = .*
+3
View File
@@ -0,0 +1,3 @@
# HexID = RequestServerID 1 (должен совпадать с gameservers.hexid в БД)
HexID=a1b2c3d4e5f6789012345678901234ef
ServerID=1
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../data/xsd/ipconfig.xsd">
<host subnet="0.0.0.0/0" address="127.0.0.1" />
</list>
+24
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../data/xsd/ipconfig.xsd">
<!-- Внешний IP для клиента (замените на IP вашего VPS) -->
<host subnet="0.0.0.0/0" address="127.0.0.1" />
</list>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<servers>
<server id="1" name="Essence 520" />
</servers>
+35 -16
View File
@@ -1,5 +1,5 @@
# Lineage 2 Essence 542 (Samurai Crow) — локальная среда # Lineage 2 Essence — клиент protocol 520, PostgreSQL 17
# Замените сервис l2-server на свой образ/сборку, когда будет готов Dockerfile. # БД: login + game в одной БД. JAR Login/Game — в ./server (см. config/).
services: services:
postgres: postgres:
@@ -22,23 +22,42 @@ services:
timeout: 5s timeout: 5s
retries: 10 retries: 10
# Заглушка: подключите свой сервер (Auth/Game) или замените build: на реальный Dockerfile. # Подключите сборку Samurai Crow / Mobius Essence (520):
l2-server: # положите login/game в ./server/login и ./server/game, раскомментируйте сервисы.
image: ${L2_SERVER_IMAGE:-ubuntu:24.04} login:
container_name: l2_server_placeholder profiles: ["l2"]
restart: "no" image: ${L2_LOGIN_IMAGE:-eclipse-temurin:21-jre}
container_name: l2_login
restart: unless-stopped
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
environment: working_dir: /opt/l2/login
DATABASE_URL: postgresql://${POSTGRES_USER:-l2user}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-l2essence} volumes:
# Раскомментируйте при готовности бинарей/скриптов: - ./server/login:/opt/l2/login
# volumes: - ./config/login:/opt/l2/login/config:ro
# - ./server:/opt/l2server:ro ports:
# command: ["/opt/l2server/start.sh"] - "${L2_LOGIN_PORT:-2106}:2106"
command: ["bash", "-lc", "echo 'L2 server placeholder: замените сервис l2-server в docker-compose.yml'; sleep infinity"] - "${L2_LOGIN_GS_PORT:-9014}:9014"
profiles: command: ["bash", "-lc", "test -f ./LoginServer.jar && java -jar LoginServer.jar || echo 'Нет LoginServer.jar в server/login'; sleep infinity"]
- placeholder
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: volumes:
postgres_data: postgres_data:
+38
View File
@@ -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
);
-49
View File
@@ -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;
@@ -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
);
-8
View File
@@ -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
+33
View File
@@ -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;
+14
View File
@@ -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
+18 -8
View File
@@ -50,8 +50,8 @@ read_env_val() {
# --- Шаг 0: приветствие --- # --- Шаг 0: приветствие ---
step_header "Шаг 0 — Обзор" step_header "Шаг 0 — Обзор"
echo "Проект: Lineage 2 Essence 542 (Samurai Crow), окружение в Docker." echo "Проект: Lineage 2 Essence, клиент protocol 520, PostgreSQL 17."
echo удет поднят PostgreSQL 17; игровой сервис — по профилю placeholder (заглушка), пока нет своего образа." echo Д: login + game (минимум для входа). Login/Game JAR — профиль l2 (server/login, server/game)."
if ! ask_yes_no "Продолжить?" "y"; then if ! ask_yes_no "Продолжить?" "y"; then
info "Выход." info "Выход."
exit 0 exit 0
@@ -161,6 +161,7 @@ fi
step_header "Шаг 5 — Запуск PostgreSQL 17" step_header "Шаг 5 — Запуск PostgreSQL 17"
if ask_yes_no "Поднять только PostgreSQL (рекомендуется)?" "y"; then if ask_yes_no "Поднять только PostgreSQL (рекомендуется)?" "y"; then
chmod +x "$SCRIPT_DIR"/docker/initdb/*.sh 2>/dev/null || true 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 docker compose up -d postgres
info "Ожидание готовности PostgreSQL (pg_isready)..." info "Ожидание готовности PostgreSQL (pg_isready)..."
EV_USER="$(read_env_val POSTGRES_USER)" EV_USER="$(read_env_val POSTGRES_USER)"
@@ -174,11 +175,17 @@ if ask_yes_no "Поднять только PostgreSQL (рекомендуетс
docker compose ps docker compose ps
fi fi
# --- Шаг 6: заглушка L2 --- # --- Шаг 6: конфиг и Login/Game ---
step_header "Шаг 6 — Игровой контейнер (заглушка)" step_header "Шаг 6 — Конфиг Essence 520"
warn "Сервис l2-server в compose помечен profile 'placeholder'. Замените образ/команду под Samurai Crow." if [[ -f "$SCRIPT_DIR/scripts/sync-config-password.sh" ]]; then
if ask_yes_no "Запустить контейнер-заглушку l2-server (для проверки сети к БД)?" "n"; then if ask_yes_no "Подставить пароль БД из .env в config/login и config/game?" "y"; then
docker compose --profile placeholder up -d l2-server 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 docker compose ps
fi fi
@@ -189,8 +196,11 @@ EV_DB="$(read_env_val POSTGRES_DB)"
EV_PORT="$(read_env_val POSTGRES_PORT)" EV_PORT="$(read_env_val POSTGRES_PORT)"
info "Строка подключения с хоста Ubuntu: postgresql://${EV_USER}:***@127.0.0.1:${EV_PORT}/${EV_DB}" info "Строка подключения с хоста Ubuntu: postgresql://${EV_USER}:***@127.0.0.1:${EV_PORT}/${EV_DB}"
info "Из контейнеров в этой compose-сети: хост postgres, порт 5432." info "Из контейнеров в этой compose-сети: хост postgres, порт 5432."
info "Тест-аккаунты: admin/admin и test/test."
info "Клиент: protocol 520, логин 127.0.0.1:2106, мир 127.0.0.1:7777."
echo "" echo ""
info "Полезные команды:" info "Полезные команды:"
echo " docker compose logs -f postgres" 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 down"
echo " docker compose --profile placeholder down"
+49 -87
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Интерактивное управление: остановка, запуск, перезапуск Docker-сервисов проекта. # Управление: PostgreSQL + Login/Game (Essence 520, profile l2).
# Запуск: chmod +x manage.sh && ./manage.sh
set -euo pipefail set -euo pipefail
@@ -13,10 +12,10 @@ YELLOW='\033[1;33m'
NC='\033[0m' NC='\033[0m'
ENV_FILE="$SCRIPT_DIR/.env" ENV_FILE="$SCRIPT_DIR/.env"
PROFILE_PLACEHOLDER=(--profile placeholder) PROFILE_L2=(--profile l2)
# Имена из docker-compose.yml (для надёжной проверки состояния)
PG_CONTAINER="l2_postgres" PG_CONTAINER="l2_postgres"
L2_CONTAINER="l2_server_placeholder" LOGIN_CONTAINER="l2_login"
GAME_CONTAINER="l2_game"
read_env_val() { read_env_val() {
local key="$1" local key="$1"
@@ -50,144 +49,107 @@ step_header() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
} }
require_cmd() {
command -v "$1" >/dev/null 2>&1
}
ensure_docker() { ensure_docker() {
if ! require_cmd docker || ! docker compose version >/dev/null 2>&1; then if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
err "Нужны Docker и плагин «docker compose». Установите через ./install.sh" err "Нужны Docker и docker compose. Запустите ./install.sh"
exit 1 exit 1
fi fi
if ! docker info >/dev/null 2>&1; then if ! docker info >/dev/null 2>&1; then
err "Docker недоступен (sudo или перелогин после группы docker)." err "Docker недоступен."
exit 1 exit 1
fi fi
} }
ensure_env() { ensure_env() {
if [[ ! -f "$ENV_FILE" ]]; then if [[ ! -f "$ENV_FILE" ]]; then
err "Нет файла .env. Сначала выполните ./install.sh" err "Нет .env. Сначала ./install.sh"
exit 1 exit 1
fi fi
} }
postgres_running() { container_running() {
[[ "$(docker inspect -f '{{.State.Running}}' "$PG_CONTAINER" 2>/dev/null || echo false)" == "true" ]] [[ "$(docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null || echo false)" == "true" ]]
} }
l2_running() { postgres_running() { container_running "$PG_CONTAINER"; }
[[ "$(docker inspect -f '{{.State.Running}}' "$L2_CONTAINER" 2>/dev/null || echo false)" == "true" ]] login_running() { container_running "$LOGIN_CONTAINER"; }
} game_running() { container_running "$GAME_CONTAINER"; }
# --- Шаг 0 --- step_header "Шаг 0 — Essence 520 (PostgreSQL + Login/Game)"
step_header "Шаг 0 — Управление сервером (Docker Compose)"
echo "Проект: PostgreSQL 17 + опционально l2-server (profile placeholder)."
if ! ask_yes_no "Продолжить?" "y"; then if ! ask_yes_no "Продолжить?" "y"; then
info "Выход."
exit 0 exit 0
fi fi
ensure_docker ensure_docker
ensure_env ensure_env
# --- Шаг 1: статус --- step_header "Шаг 1Статус"
step_header "Шаг 1 — Текущий статус"
if ask_yes_no "Показать docker compose ps?" "y"; then 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 fi
# --- Шаг 2: остановка ---
step_header "Шаг 2 — Остановка" step_header "Шаг 2 — Остановка"
if ask_yes_no "Остановить контейнер l2-server (если был запущен с profile placeholder)?" "n"; then if ask_yes_no "Остановить game и login (profile l2)?" "n"; then
docker compose "${PROFILE_PLACEHOLDER[@]}" stop l2-server 2>/dev/null || warn "l2-server не остановлен (возможно не запускался)." docker compose "${PROFILE_L2[@]}" stop game login 2>/dev/null || true
fi fi
if ask_yes_no "Остановить PostgreSQL?" "n"; then
if ask_yes_no "Остановить только PostgreSQL (docker compose stop postgres)?" "n"; then docker compose stop postgres 2>/dev/null || true
docker compose stop postgres 2>/dev/null || warn "PostgreSQL уже остановлен или не создавался."
fi fi
if ask_yes_no "docker compose down (все сервисы, том БД сохраняется)?" "n"; then
if ask_yes_no "Полная остановка проекта: docker compose down (все сервисы, сеть; тома БД не удаляются)?" "n"; then docker compose "${PROFILE_L2[@]}" down
docker compose "${PROFILE_PLACEHOLDER[@]}" down docker compose down
info "Контейнеры остановлены. Данные БД в томе postgres_data сохранены."
fi fi
if ask_yes_no "Удалить том БД (down -v)? ОПАСНО." "n"; then
if ask_yes_no "Удалить том с данными PostgreSQL (docker compose down -v)? ОПАСНО: потеря БД." "n"; then if ask_yes_no "Точно удалить?" "n"; then
warn "Будет выполнено: docker compose --profile placeholder down -v" docker compose "${PROFILE_L2[@]}" down -v
if ask_yes_no "Точно удалить тома?" "n"; then docker compose down -v
docker compose "${PROFILE_PLACEHOLDER[@]}" down -v
info "Тома удалены."
else
info "Отмена удаления томов."
fi fi
fi fi
# --- Шаг 3: запуск ---
step_header "Шаг 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 docker compose up -d postgres
info "Ожидание pg_isready..."
EV_USER="$(read_env_val POSTGRES_USER)" EV_USER="$(read_env_val POSTGRES_USER)"
EV_DB="$(read_env_val POSTGRES_DB)" EV_DB="$(read_env_val POSTGRES_DB)"
for _ in $(seq 1 60); do 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 docker compose exec -T postgres pg_isready -U "$EV_USER" -d "$EV_DB" >/dev/null 2>&1 && break
break
fi
sleep 1 sleep 1
done done
fi fi
if ask_yes_no "Запустить login + game (profile l2)?" "n"; then
if ask_yes_no "Запустить l2-server (placeholder, нужен здоровый PostgreSQL)?" "n"; then if postgres_running; then
if ! postgres_running; then docker compose "${PROFILE_L2[@]}" up -d login game
warn "PostgreSQL не запущен. Сначала поднимите postgres."
else else
docker compose "${PROFILE_PLACEHOLDER[@]}" up -d l2-server warn "Сначала поднимите postgres."
fi fi
fi fi
# --- Шаг 4: перезапуск ---
step_header "Шаг 4 — Перезапуск" step_header "Шаг 4 — Перезапуск"
if ask_yes_no "Перезапустить PostgreSQL (restart postgres)?" "n"; then if ask_yes_no "Перезапустить PostgreSQL?" "n"; then
if postgres_running; then if postgres_running; then
docker compose restart postgres docker compose restart postgres
info "PostgreSQL перезапущен."
else else
warn "PostgreSQL не в состоянии running. Выполняю up -d postgres."
docker compose up -d postgres docker compose up -d postgres
fi fi
fi fi
if ask_yes_no "Перезапустить login?" "n"; then
if ask_yes_no "Перезапустить l2-server (restart l2-server)?" "n"; then login_running && docker compose "${PROFILE_L2[@]}" restart login || warn "login не запущен."
if l2_running; then
docker compose "${PROFILE_PLACEHOLDER[@]}" restart l2-server
info "l2-server перезапущен."
else
warn "l2-server не запущен. Пропуск."
fi
fi fi
if ask_yes_no "Перезапустить game?" "n"; then
if ask_yes_no "Перезапустить весь стек: сначала down, затем up postgres (+ опционально l2)?" "n"; then game_running && docker compose "${PROFILE_L2[@]}" restart game || warn "game не запущен."
docker compose "${PROFILE_PLACEHOLDER[@]}" down fi
if ask_yes_no "Полный перезапуск: down → postgres → login → game?" "n"; then
docker compose "${PROFILE_L2[@]}" down
docker compose down
docker compose up -d postgres docker compose up -d postgres
EV_USER="$(read_env_val POSTGRES_USER)" sleep 3
EV_DB="$(read_env_val POSTGRES_DB)" docker compose "${PROFILE_L2[@]}" up -d login game
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 "Стек перезапущен."
fi fi
# --- Итог ---
step_header "Готово" step_header "Готово"
if ask_yes_no "Показать итоговый статус?" "y"; then if ask_yes_no "Показать статус?" "y"; then
docker compose "${PROFILE_PLACEHOLDER[@]}" ps -a || true docker compose ps -a
docker compose "${PROFILE_L2[@]}" ps -a 2>/dev/null || true
fi fi
info "Логи: docker compose logs -f login game postgres"
info "Логи: docker compose logs -f postgres"
info "Выход из проекта без удаления томов: docker compose --profile placeholder down"
+16
View File
@@ -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 "Готово."
+17
View File
@@ -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
View File
+1
View File
@@ -0,0 +1 @@
a1b2c3d4e5f6789012345678901234ef
View File