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