FiveRP: the server from the hosting account, not the earlier one

Wrong server was published before. This is the one that runs on the
hosting account the video was made on: FiveRP - two-step registration
(account, then an identity printed onto a passport), login that returns
you to your resident, and a loading screen driven by the game's own
streaming events.

  resources/[local]/fiverp-auth          NUI + scrypt hashes + oxmysql
  resources/[local]/fiverp-characters    identity, character load, spawn
  resources/[local]/fiverp-loadscreen    loading screen
  sql/schema.sql                         accounts, characters
  docs/DESIGN.md                         the design system every screen follows
  docs/screens/                          shot from the live server

Only our own code is in here. cfx-server-data and oxmysql are fetched by
install.sh instead of being vendored, which keeps the repo at 3.7 MB.

install.sh does the whole box in one command: recommended FXServer
build, cfx-server-data, oxmysql, MariaDB with the schema, resources,
server.cfg (mode 600 - it carries the key and the database password),
boot entry, then it waits for the Cfx registration. Tested end to end on
a spare install root: 29 resources scanned, database and schema created,
and it stopped exactly where a wrong licence key should stop it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Claude Opus 5
2026-08-12 23:42:20 +00:00
co-authored by Claude Opus 5
parent 71856b15e9
commit c857979a55
88 changed files with 3904 additions and 7463 deletions
+303
View File
@@ -0,0 +1,303 @@
# FiveRP — Server Design System
Единый стиль для **всего** сервера: loading screen, авторизация, будущие меню
(инвентарь, телефон, банк, HUD). Основан на белой «стеклянной» теме Filmorum
(`fiveM_design.md`), адаптирован под CEF/NUI FiveM.
> Правило номер один: **ни один компонент не хардкодит цвет, радиус или шрифт.**
> Только `var(--…)` из блока токенов ниже. Смена темы = одна строка.
---
## 0. Шрифт — Unbounded
**Unbounded — основной шрифт всего сервера.** Геометрический дисплейный гротеск,
переменная ось веса 300–800. Никаких других сансов в интерфейсе.
Файлы лежат в каждом ресурсе локально (`html/fonts/`), а НЕ грузятся с
`fonts.googleapis.com`: у части игроков CEF стартует раньше сети, и веб-шрифт
успевает не приехать — интерфейс мигает системным шрифтом. Локальный woff2
рисуется с первого кадра.
```css
@font-face {
font-family: 'Unbounded';
font-style: normal;
font-weight: 300 800; /* вариативный: один файл на все веса */
font-display: block; /* block, не swap — подмены шрифта быть не должно */
src: url('fonts/unbounded-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* + unbounded-latin-ext.woff2 и unbounded-cyrillic.woff2 теми же блоками */
```
```css
--sans: 'Unbounded', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
--mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
'Liberation Mono', 'DejaVu Sans Mono', monospace;
```
**Как набирать Unbounded.** Шрифт широкий и характерный — он требует другой
типографики, чем SF Pro:
| Роль | Размер | Вес | Трекинг |
|---|---|---|---|
| Display (лого, экран загрузки) | 44–72px | 800 | `-0.04em` |
| H1 (заголовок панели) | 26–30px | 700 | `-0.035em` |
| H2 (секция) | 1820px | 600 | `-0.03em` |
| Кнопка / линк | 14px | 600 | `-0.01em` |
| Body / инпут | 1415px | 400 | `-0.005em` |
| Лейбл поля | 11px | 600 | `+0.09em`, `uppercase` |
| Eyebrow / надзаголовок | 10px | 600 | `+0.20em`, `uppercase` |
| Метаданные | 1112px | 400 | `0` |
Правила:
- **Крупный кегль = минусовой трекинг.** У Unbounded широкие апроши; без
`letter-spacing: -0.03em` заголовок расползается.
- **Мелкий кегль = плюсовой трекинг + uppercase.** Ниже 12px строчные буквы
Unbounded слипаются — все подписи только капслоком с разрядкой.
- **Веса 300 и 800 — крайности**, в интерфейсе живут 400/500/600/700.
- **Не набирать Unbounded длинный текст.** Абзац больше двух строк — читать
тяжело. Для многострочных описаний кегль 13px/1.65 и вес 400, не больше.
- **Цифры документов, ID, MRZ — только `--mono`.** Unbounded для них слишком
«дизайнерский», а нужен вид машинного считывания.
---
## 1. Философия
- **Air, light, frosted** — полупрозрачные стеклянные панели поверх живой игры.
- **Кислород** — между карточками 12px, между секциями 40–48px.
- **Один акцент** — синий `#0071e3`, дозированно: кнопка, ссылка, фокус.
- **Сдержанное движение** — fade и подъём на 4px. Анимации ради анимации нет.
- **Один герой на экран** — крупный объект (паспорт, лого), остальное тихое.
---
## 2. Токены
```css
:root {
--bg: #f5f5f7;
--bg-deep: #e8e8ed;
--surface: rgba(255, 255, 255, 0.72);
--surface-solid:#ffffff;
--panel: rgba(255, 255, 255, 0.80);
--header-bg: rgba(255, 255, 255, 0.70);
--field-bg: rgba(0, 0, 0, 0.04);
--field-bg-focus:#ffffff;
--fg: #1d1d1f;
--fg-secondary: #6e6e73;
--fg-tertiary: #a1a1a6;
--accent: #0071e3;
--accent-strong:#0077ed;
--accent-soft: rgba(0, 113, 227, 0.08);
--ring: rgba(0, 113, 227, 0.18);
--line: rgba(0, 0, 0, 0.06);
--line-strong: rgba(0, 0, 0, 0.10);
--hover: rgba(0, 0, 0, 0.04);
--success: #34c759;
--danger: #ff3b30;
--radius-lg: 1.5rem; /* 24px — крупные панели */
--radius-md: 1rem; /* 16px — карточки, инпуты */
--radius-sm: 0.7rem; /* 11px — мелочь */
--ease: cubic-bezier(0.25, 0.8, 0.25, 1);
color-scheme: light;
}
```
Тёмная и бежевая темы — через `data-theme` на `html`, переопределением тех же
токенов (`--bg`, `--surface`, `--fg`, `--accent`, `--success`, `--danger`).
---
## 3. КРИТИЧНО: особенности CEF во FiveM
Это не «хорошо бы», а условия, при нарушении которых интерфейс ломается
в игре, оставаясь идеальным в браузере.
1. **`backdrop-filter` и `box-shadow` не живут на одном элементе.**
Композитор CEF рисует тень такого элемента непрозрачным прямоугольником —
в игре под панелью появляется чёрный квадрат. Если нужны и блюр, и тень —
тень вешать на элемент-обёртку, а блюр на внутренний. По умолчанию
**у стеклянных панелей тени нет вообще**: их отделяет от игры блюр и
светлая граница, этого достаточно.
2. **`html, body { background: transparent }`** — иначе NUI закрашивает игру.
3. **Никаких затемняющих подложек.** Экран за панелью — это игра, её видно
и она красивая. Контраст добирается блюром и белой заливкой стекла, а не
`rgba(0,0,0,…)` поверх кадра. Допустим только **осветляющий** вуаль:
`radial-gradient(…, rgba(255,255,255,0.28), transparent)`.
4. **Шрифты — локальные woff2.** См. раздел 0.
5. **`prefers-reduced-motion`** — обязательный блок с самого начала.
6. **Курсор и фокус**`SetNuiFocus(true, true)` только когда панель открыта,
и обязательно `SetNuiFocus(false, false)` при закрытии, иначе игрок
остаётся без управления.
---
## 4. Стеклянная панель
```css
.glass {
background: var(--surface);
backdrop-filter: saturate(180%) blur(24px);
-webkit-backdrop-filter: saturate(180%) blur(24px);
border: 1px solid rgba(255, 255, 255, 0.55);
border-radius: var(--radius-lg);
/* без box-shadow — см. раздел 3.1 */
}
```
Тень разрешена только элементам **без** `backdrop-filter` (карточки на
непрозрачном фоне, экран загрузки):
```css
.shadow-soft { box-shadow: 0 1px 2px rgba(0,0,0,.03), 0 12px 40px -12px rgba(0,0,0,.12); }
.shadow-lift { box-shadow: 0 2px 4px rgba(0,0,0,.04), 0 24px 50px -16px rgba(0,0,0,.20); }
```
Ховер кликабельной карточки: `transform: translateY(-4px)`, 0.4s `--ease`.
---
## 5. Кнопки
```css
.btn-primary {
border-radius: 999px;
padding: 12px 24px;
font: 600 14px var(--sans);
letter-spacing: -0.01em;
color: #fff;
background-image: linear-gradient(180deg, var(--accent-strong), var(--accent));
box-shadow: 0 1px 1px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.20);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-primary:active { transform: scale(0.97); }
.btn-primary:disabled { opacity: .5; pointer-events: none; }
.btn-ghost {
border-radius: 999px;
padding: 10px 20px;
font: 600 14px var(--sans);
color: var(--accent);
background: var(--accent-soft);
}
.btn-ghost:hover { background: rgba(0,113,227,.14); }
```
Ровно два стиля кнопок на весь сервер. Третий не заводить.
---
## 6. Поля ввода
```css
.field {
width: 100%;
border-radius: var(--radius-md);
padding: 13px 14px;
font: 400 15px var(--sans);
letter-spacing: -0.005em;
color: var(--fg);
background: var(--field-bg);
border: 1px solid transparent;
outline: none;
transition: background-color .2s, border-color .2s, box-shadow .2s;
}
.field::placeholder { color: var(--fg-tertiary); }
.field:focus {
background: var(--field-bg-focus);
border-color: var(--accent);
box-shadow: 0 0 0 4px var(--ring); /* можно: у инпута нет backdrop-filter */
}
.field.is-bad { border-color: var(--danger); box-shadow: 0 0 0 4px rgba(255,59,48,.14); }
```
Лейбл над полем — 11px/600/`+0.09em`/uppercase/`--fg-secondary`.
---
## 7. Компоновка
- Панель по центру экрана, ширина `min(940px, 92vw)`, радиус `--radius-lg`.
- Внутренние отступы панели 32–40px.
- Между полями формы 16px, между группами 24px.
- Двухколоночная схема «форма + герой»: `grid-template-columns: minmax(0,1fr) 420px`.
- Сворачивание в одну колонку — анимацией `grid-template-columns` и `width`,
620ms `--ease`.
---
## 8. Motion
```css
.fade-up { animation: fadeUp .6s cubic-bezier(.22,1,.36,1) both; }
.delay-1 { animation-delay: .06s } .delay-2 { animation-delay: .12s }
.delay-3 { animation-delay: .18s } .delay-4 { animation-delay: .24s }
@keyframes fadeUp {
from { opacity: 0; transform: translateY(18px) scale(.995); filter: blur(6px); }
to { opacity: 1; transform: none; filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .001ms !important;
transition-duration: .001ms !important;
}
}
```
---
## 9. Фокус и доступность
```css
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
::selection { background: var(--accent-soft); color: var(--accent-strong); }
```
Контраст текста к стеклу — не ниже 4.5:1. `--fg-tertiary` только для
плейсхолдеров и декоративных подписей, никогда для смысловых.
---
## 10. Существующие интерфейсы
| Ресурс | Что это | Файлы |
|---|---|---|
| `fiverp-loadscreen` | экран загрузки сервера | `html/index.html`, `style.css`, `app.js` |
| `fiverp-auth` | регистрация и вход, паспорт США | `html/index.html`, `style.css`, `app.js` |
`fiverp-auth` использует один «герой» — разворот паспорта США (страница с
данными: фото, поля, MRZ). Паспорт — единственный насыщенный цветом объект
на экране; форма рядом с ним остаётся полностью нейтральной.
---
## 11. Чек-лист нового интерфейса
1. Скопировать `html/fonts/` и блок `:root` из `fiverp-auth`.
2. `html, body { background: transparent }`.
3. Панель — `.glass`, **без** `box-shadow`.
4. Ни одного затемняющего слоя поверх игры.
5. Шрифт Unbounded, трекинг по таблице из раздела 0.
6. Акцент только `--accent`; кнопки только `.btn-primary` / `.btn-ghost`.
7. Подключить `prefers-reduced-motion`.
8. `SetNuiFocus(false, false)` на закрытии — проверить руками.
9. Отрендерить в headless Chrome и **посмотреть глазами** до выката.
-99
View File
@@ -1,99 +0,0 @@
# Los Santos RP — server notes
Everything here was written for this server. No ESX, no QBCore, no downloaded
resources: `cfx-server-data` is not used at all, and the bundled `chat` and
`monitor` resources are not started.
## Layout
/opt/fivem/server FXServer artefacts (build 25770, the recommended one)
/opt/fivem/data server-data root; run FXServer with this as cwd
server.cfg main config
secrets.cfg licence key + DB credentials, mode 600
resources/[rp]/ our resources
/opt/fivem/bin supervise.sh (keep-alive), rcon (console pipe)
/opt/fivem/logs server.log, rotated at 200MB
/opt/fivem/etc/schema.sql the database schema
## Resources
| resource | what it is |
|---------------|------------|
| `rp_db` | MariaDB client speaking the MySQL wire protocol directly over a TCP socket — handshake, `mysql_native_password`, text result sets, connection pool, transactions. No npm packages. Lua front end in `lib/db.lua`. |
| `rp_core` | Config, shared validation, appearance model, scrypt password hashing, the player registry, and the client/server request bridge. |
| `rp_session` | Connection gate (bans, capacity), accounts, characters, spawn. Every decision is made here; the client can only ask. |
| `rp_loading` | Loading screen: procedural night-city flyover on canvas, progress bound to the game's real load events, ambience synthesised with the Web Audio API. |
| `rp_ui` | Sign in / register, character creation with a live ped preview, spawn picker. Scripted cameras live in `client/camera.lua`. |
| `rp_selftest` | Checks the account/character data path on every boot and proves it survived the restart. |
| `rp_dbtest` | Driver test suite. Not started by default; `ensure rp_dbtest` to run it. |
## The look
The interface is the paperwork side of a life in Los Santos: every screen is a
sheet from the city's Office of Vital Records, lying on a dark desk while the
game runs on behind it. Paper is the only surface we draw; the game is the room
the desk is in. That is why the NUI body stays transparent and the sheets are
opaque - an opaque sheet stays legible over a bright daylight city, where a dark
glass panel would not.
`html/tokens.css` is shared verbatim by `rp_ui` and `rp_loading` and holds the
whole system. Three typographic voices, and the rule between them is literal:
| voice | face | what it is |
|-------|------|------------|
| preprinted | Archivo, expanded, caps | what the form was printed with: headings, field captions, buttons |
| typed | IBM Plex Mono | what somebody entered on it: values, serials, names, user input |
| prose | IBM Plex Sans | the plain-English notes in the margin: blurbs and hints |
Three inks, each with exactly one job and never used decoratively: `--canary`
(municipal form yellow) is what is selected right now, `--stamp` (oxblood) is
filed / refused / destroyed, `--verdi` (municipal teal) is checked and valid.
The one loud thing is the rubber stamp, and it only ever marks a real change of
state: the file opened, the application approved, the placement issued. It is
masked with SVG turbulence so the ink lands unevenly, and the paper on the desk
takes the hit with it.
Fonts are bundled, never fetched: a NUI page has no guaranteed internet. Any
glyph used in `content:` must exist in the shipped latin subset, which is why
the validation tick is drawn from borders rather than U+2713.
Screens can be looked at without launching the game: `python3 /opt/preview/build.py`
stubs the NUI bridge, pushes each screen into a representative state, and writes
standalone pages to `/opt/preview/out` for headless Chrome to shoot.
## Operating it
service fivem start|stop|restart|status
rcon <command> # e.g. rcon refresh, rcon status
tail -f /opt/fivem/logs/server.log
`supervise.sh` restarts FXServer if it dies, backing off from 5s up to 5min so
a server that cannot start (bad key, database down) does not hammer Cfx. It
also holds a FIFO open on the server's stdin — FXServer quits the moment stdin
reaches EOF, and that FIFO is what `rcon` writes to.
## Starting on boot
There is no systemd in this container. The platform runs a login shell as PID 1,
which sources `/etc/profile.d/redl-autostart.sh`, which starts everything marked
in `/etc/redl/enabled`. Both `mariadb` and `fivem` are marked, and `/etc/init.d/fivem`
waits for MariaDB to answer before starting the server.
## Database
MariaDB, database `rp`, application user `rp@127.0.0.1` with only
SELECT/INSERT/UPDATE/DELETE. Passwords are scrypt with a per-password salt and
the cost parameters stored in the hash. Tables: `accounts`, `auth_attempts`,
`characters`, `transactions`, `inventory`, `vehicles`.
Every query goes through `DB.Query`/`DB.Insert`/... with `?` parameters.
`CLIENT_MULTI_STATEMENTS` is deliberately not negotiated, so even a failure of
the escaper could not turn a parameter into a second statement.
## Not yet built
The pre-spawn experience and the framework underneath it are done. Gameplay on
top of it — chat, HUD, inventory, jobs, vehicles, policing, medical — is not.
`characters`, `transactions`, `inventory` and `vehicles` already have their
tables and the money API in `rp_core/server/player.lua` is journalled and ready.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB