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>
@@ -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 (секция) | 18–20px | 600 | `-0.03em` |
|
||||
| Кнопка / линк | 14px | 600 | `-0.01em` |
|
||||
| Body / инпут | 14–15px | 400 | `-0.005em` |
|
||||
| Лейбл поля | 11px | 600 | `+0.09em`, `uppercase` |
|
||||
| Eyebrow / надзаголовок | 10px | 600 | `+0.20em`, `uppercase` |
|
||||
| Метаданные | 11–12px | 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 и **посмотреть глазами** до выката.
|
||||
@@ -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.
|
||||
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 823 KiB |
|
After Width: | Height: | Size: 645 KiB |
|
After Width: | Height: | Size: 790 KiB |
|
After Width: | Height: | Size: 669 KiB |
|
Before Width: | Height: | Size: 122 KiB |