Expand local_machine docs and automation for connectivity, games, and ops.
Add proxy/VPN/telegram launchd and emergency runbooks; reorganize apps docs; document JA3 CrossOver runbook and Wine troubleshooting; add GOG/HoMM game scripts, disk cleanup guides, and gitea push-via-proxy helper. Ignore temp/. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -33,6 +33,8 @@
|
||||
# VPN/WireGuard configs with private keys (only under connectivity/vpn)
|
||||
docs/connectivity/vpn/*.conf
|
||||
**/vpn/*.conf
|
||||
# Key pair for Windows proxy tunnel (created by create-windows-proxy-key.sh)
|
||||
docs/connectivity/scripts/windows-proxy-key/
|
||||
|
||||
# --- Python ---
|
||||
__pycache__/
|
||||
@@ -67,6 +69,7 @@ yarn-error.log*
|
||||
*.backup
|
||||
*.cache
|
||||
.cache/
|
||||
temp/
|
||||
|
||||
# --- OS junk ---
|
||||
Thumbs.db
|
||||
|
||||
164
docs/apps/anytype/README.md
Normal file
164
docs/apps/anytype/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Anytype (macOS) — чёрный экран после загрузки, пустое окно
|
||||
|
||||
> **Проверенный кейс (2026-04):** интерфейс успевает показать контент, затем окно уходит в чёрный/пустой экран.
|
||||
> **Что сработало:** обновление клиента до актуальной стабильной версии через Homebrew Cask; данные в `~/Library/Application Support/anytype/data/` не трогались.
|
||||
|
||||
**Связано:** безопасная очистка кэшей и структура каталогов — [ANYTYPE_WORKSPACE_FIX.md](../../disk_cleanup/ANYTYPE_WORKSPACE_FIX.md). Официально про графику — [Troubleshooting (Anytype Docs)](https://doc.anytype.io/anytype-docs/advanced/troubleshooting).
|
||||
|
||||
---
|
||||
|
||||
## Содержание
|
||||
|
||||
1. [Симптомы](#1-симптомы)
|
||||
2. [Диагностика](#2-диагностика)
|
||||
3. [Что нельзя удалять (данные)](#3-что-нельзя-удалять-данные)
|
||||
4. [Шаг 1 (опционально): только кэши UI](#4-шаг-1-опционально-только-кэши-ui)
|
||||
5. [Шаг 2 (основной): обновить приложение](#5-шаг-2-основной-обновить-приложение)
|
||||
6. [Если после обновления снова IncompatibleVersion](#6-если-после-обновления-снова-incompatibleversion)
|
||||
7. [Сброс профиля Electron с сохранением vault](#7-сброс-профиля-electron-с-сохранением-vault)
|
||||
8. [Ссылки](#8-ссылки)
|
||||
|
||||
---
|
||||
|
||||
## 1. Симптомы
|
||||
|
||||
- Окно Anytype **сначала отрисовывается**, затем **чёрный или пустой** экран.
|
||||
- Несколько окон / фон / сон Mac — иногда усугубляют (Electron), но **не всегда** являются первопричиной.
|
||||
- В логах ядра при работе с сетью возможны ошибки **`IncompatibleVersion`** (несовпадение версии клиента с ожиданиями координации Anytype).
|
||||
|
||||
---
|
||||
|
||||
## 2. Диагностика
|
||||
|
||||
### Версия установленного приложения
|
||||
|
||||
```bash
|
||||
/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' /Applications/Anytype.app/Contents/Info.plist
|
||||
```
|
||||
|
||||
### Логи клиента (Electron)
|
||||
|
||||
```bash
|
||||
tail -n 200 "$HOME/Library/Application Support/anytype/logs/log.log"
|
||||
```
|
||||
|
||||
### Логи middleware («сердце» / синхронизация)
|
||||
|
||||
```bash
|
||||
grep -n 'IncompatibleVersion' "$HOME/Library/Application Support/anytype/common/logs/anytype.log" | tail -20
|
||||
```
|
||||
|
||||
Если **`IncompatibleVersion`** повторяется на свежем запуске — в первую очередь **обновите клиент** до версии не старше рекомендуемой сетью (стабильный cask Homebrew обычно достаточен).
|
||||
|
||||
### Место на диске
|
||||
|
||||
Переполненный том ломает **автообновление** (обрыв загрузки, checksum mismatch, `ENOSPC` в логах). Перед установкой DMG/ZIP оставьте запас свободного места на томе с `/Users` (ориентир: не держать системный том постоянно **>~95%** занятым).
|
||||
|
||||
---
|
||||
|
||||
## 3. Что нельзя удалять (данные)
|
||||
|
||||
Локальный vault и ключи лежат под:
|
||||
|
||||
```text
|
||||
~/Library/Application Support/anytype/data/
|
||||
```
|
||||
|
||||
Внутри каталога аккаунта — как минимум `localstore`, `spaceStoreNew`, `objectstore`, `config.json`, `device.key` и т.д. Подробная схема — в [ANYTYPE_WORKSPACE_FIX.md](../../disk_cleanup/ANYTYPE_WORKSPACE_FIX.md).
|
||||
|
||||
**Страховка перед любыми действиями:**
|
||||
|
||||
```bash
|
||||
mkdir -p "$HOME/Desktop/anytype-backups"
|
||||
cp -a "$HOME/Library/Application Support/anytype/data" \
|
||||
"$HOME/Desktop/anytype-backups/data-$(date +%Y%m%d_%H%M%S)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Шаг 1 (опционально): только кэши UI
|
||||
|
||||
Пока Anytype **полностью закрыт** (`Anytype` → Quit или `killall Anytype`), можно удалить **только** кэши Chromium/Electron — **не** трогая `data/`. Готовый набор команд — в [ANYTYPE_WORKSPACE_FIX.md § Безопасная очистка](../../disk_cleanup/ANYTYPE_WORKSPACE_FIX.md#-безопасная-очистка-кэша-anytype).
|
||||
|
||||
Официальная подсказка для графики (Linux-путь в доке; на macOS аналог — те же папки под `Application Support/anytype`): удалить **`GPUCache`** — см. [Troubleshooting](https://doc.anytype.io/anytype-docs/advanced/troubleshooting).
|
||||
|
||||
---
|
||||
|
||||
## 5. Шаг 2 (основной): обновить приложение
|
||||
|
||||
Рекомендуемый способ на macOS — **Homebrew Cask** (версия совпадает с тем, что проверяет сообщество; URL — официальный CDN Anytype в формуле cask).
|
||||
|
||||
### 5.1. Закрыть приложение
|
||||
|
||||
```bash
|
||||
killall Anytype 2>/dev/null || true
|
||||
sleep 1
|
||||
```
|
||||
|
||||
### 5.2. Установка / обновление
|
||||
|
||||
```bash
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask anytype
|
||||
```
|
||||
|
||||
Если Brew сообщает, что **`Anytype.app` уже есть в `/Applications`** и установка **прерывается**, старое приложение нужно **убрать из пути** (переименовать — безопасно, это не данные):
|
||||
|
||||
```bash
|
||||
killall Anytype 2>/dev/null || true
|
||||
sleep 1
|
||||
ts=$(date +%Y%m%d_%H%M%S)
|
||||
mv /Applications/Anytype.app "/Applications/Anytype.app.backup-$ts"
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask anytype
|
||||
```
|
||||
|
||||
Проверка версии:
|
||||
|
||||
```bash
|
||||
/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' /Applications/Anytype.app/Contents/Info.plist
|
||||
```
|
||||
|
||||
Запуск:
|
||||
|
||||
```bash
|
||||
open -a Anytype
|
||||
```
|
||||
|
||||
После проверки резервную копию `Anytype.app.backup-*` можно удалить.
|
||||
|
||||
**Важно:** установка/замена **`.app`** не удаляет `~/Library/Application Support/anytype/data/` — локальные пространства и объекты сохраняются.
|
||||
|
||||
---
|
||||
|
||||
## 6. Если после обновления снова IncompatibleVersion
|
||||
|
||||
1. Убедиться, что действительно запускается **новый** бинарь (версия в Info.plist, один экземпляр в `/Applications`).
|
||||
2. Освободить место на диске и повторить обновление.
|
||||
3. При необходимости поставить более свежую ветку (**alpha**), если она доступна отдельным cask и не конфликтует со стабильным (см. `brew search anytype` и описание cask’ов).
|
||||
|
||||
---
|
||||
|
||||
## 7. Сброс профиля Electron с сохранением vault
|
||||
|
||||
Если обновление **помогло по версии**, но UI всё ещё «ломается», можно пересоздать кэш/Local Storage **всего каталога поддержки**, оставив только `data/` (как и раньше — **сначала полная копия** `data/` в безопасное место).
|
||||
|
||||
Пока Anytype **закрыт**:
|
||||
|
||||
```bash
|
||||
killall Anytype 2>/dev/null || true
|
||||
BASE="$HOME/Library/Application Support/anytype"
|
||||
cp -a "$BASE/data" "$HOME/Desktop/anytype-backups/data-manual-$(date +%Y%m%d_%H%M%S)"
|
||||
mv "$BASE" "${BASE}_profile_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BASE"
|
||||
mv "${BASE}_profile_backup_"*/data "$BASE/"
|
||||
open -a Anytype
|
||||
```
|
||||
|
||||
Не удаляйте каталог `*_profile_backup_*`, пока не убедитесь, что приложение снова стабильно открывается.
|
||||
|
||||
---
|
||||
|
||||
## 8. Ссылки
|
||||
|
||||
- [Anytype — Troubleshooting](https://doc.anytype.io/anytype-docs/advanced/troubleshooting)
|
||||
- [anytype-ts releases (GitHub)](https://github.com/anyproto/anytype-ts/releases)
|
||||
- [Homebrew Cask `anytype`](https://formulae.brew.sh/cask/anytype)
|
||||
@@ -4,7 +4,7 @@
|
||||
> **Временный обход:** Disable Http2 в настройках — помогает, но всё сильно замедляется.
|
||||
> **Рекомендуемое решение:** SOCKS-прокси через SSH-туннель на сервер с нормальным доступом (DO / RU VPS), без отключения HTTP/2.
|
||||
|
||||
**Связанные гайды:** [PROXY_GUIDE.md](../../connectivity/PROXY_GUIDE.md), [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) — тот же паттерн «туннель через прокси-сервер».
|
||||
**Связанные гайды:** [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md), [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) — тот же паттерн «туннель через прокси-сервер». **Выход через DO (как VPN на 209.38.32.21):** [docs/connectivity/vpn/README.md](../connectivity/vpn/README.md). **Windows:** [WINDOWS_CURSOR_TUNNEL.md](../connectivity/WINDOWS_CURSOR_TUNNEL.md) и скрипт `scripts/cursor-socks-tunnel.ps1`.
|
||||
|
||||
---
|
||||
|
||||
@@ -58,35 +58,45 @@
|
||||
### Схема
|
||||
|
||||
```
|
||||
Cursor (macOS) → SOCKS 127.0.0.1:10809 → SSH-туннель → VPS (DO/RU)
|
||||
↓
|
||||
HTTPS/HTTP2 → *.cursor.sh
|
||||
Cursor → SOCKS 127.0.0.1:10809 → SSH → RU hsites-ahau → DO 209.38.32.21
|
||||
↓
|
||||
HTTPS/HTTP2 → *.cursor.sh
|
||||
```
|
||||
|
||||
На VPS не нужны отдельные прокси-сервисы — достаточно SSH (как в [PROXY_GUIDE](../../connectivity/PROXY_GUIDE.md) и [BUILD_AAB_FROM_RUSSIA_GUIDE](../../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md)).
|
||||
При **`CURSOR_SOCKS_VIA_DO=1`** (по умолчанию) — **только** цепочка **Mac → RU → DO** (`telegram-socks-via-do`). Если **`hsites-ahau`** недоступен, скрипт **завершается с ошибкой** (прямой Mac→DO для SOCKS не поднимается). Публичный IP снаружи — **DO** (шаблон `Host telegram-socks-via-do` в [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md)).
|
||||
|
||||
На стороне DO не нужен отдельный прокси-сервис — только SSH и те же `Host`, что для Telegram.
|
||||
|
||||
### Предположения
|
||||
|
||||
- Есть VPS с нормальным доступом к интернету (например, тот же, что для ru.hunab.app: **149.154.64.19**, доступ по `ssh hsites-ahau` или аналог).
|
||||
- Локально свободен порт для SOCKS (ниже — **10809**, чтобы не пересекаться с другими прокси).
|
||||
- В **`~/.ssh/config`** задан **`telegram-socks-via-do`** с **`ProxyJump hsites-ahau`** (шаблон в [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md)).
|
||||
- Локально свободен порт **10809** (не путать с **1081** для Telegram).
|
||||
|
||||
### Шаг 1: Поднять SOCKS-туннель
|
||||
|
||||
Рекомендуется скрипт (выход через DO по умолчанию):
|
||||
|
||||
```bash
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh start
|
||||
```
|
||||
|
||||
Переменные: **`CURSOR_SOCKS_PORT`** (по умолчанию 10809), **`CURSOR_SOCKS_VIA_DO=0`** — только RU (`CURSOR_TUNNEL_HOST`, по умолчанию `hsites-ahau`).
|
||||
|
||||
Вручную (выход с DO, как у скрипта):
|
||||
|
||||
```bash
|
||||
# Убить старый туннель на порту 10809 (если был)
|
||||
pkill -f "ssh.*-D 10809" 2>/dev/null
|
||||
|
||||
# Запуск SOCKS5 на 127.0.0.1:10809 через ваш VPS
|
||||
ssh -D 10809 -f -N hsites-ahau
|
||||
ssh -D 10809 -f -N -o ServerAliveInterval=60 -o ServerAliveCountMax=4 telegram-socks-via-do
|
||||
```
|
||||
|
||||
Если используете другой хост (например, hunab-prod):
|
||||
Только RU (без DO в цепочке выхода):
|
||||
|
||||
```bash
|
||||
ssh -D 10809 -f -N hunab-prod
|
||||
CURSOR_SOCKS_VIA_DO=0 ./docs/cursor/scripts/cursor-socks-tunnel.sh start
|
||||
# или: ssh -D 10809 -f -N hsites-ahau
|
||||
```
|
||||
|
||||
Проверка: туннель держится, пока сессия не разорвана; можно проверить доступ через SOCKS (см. [Проверка и диагностика](#-проверка-и-диагностика)).
|
||||
Проверка: туннель держится, пока сессия не разорвана; см. [Проверка и диагностика](#-проверка-и-диагностика).
|
||||
|
||||
### Шаг 2: Настроить Cursor на использование SOCKS
|
||||
|
||||
@@ -106,13 +116,13 @@ ssh -D 10809 -f -N hunab-prod
|
||||
|
||||
### Шаг 3: Не отключать HTTP/2
|
||||
|
||||
**Cursor > General: Disable Http2** — выключить (оставить HTTP/2 включённым). Весь трафик к Cursor API пойдёт с VPS, где HTTP/2 работает нормально.
|
||||
**Cursor > General: Disable Http2** — выключить (оставить HTTP/2 включённым). Трафик к Cursor API идёт с DO (или с RU, если включён только RU-туннель).
|
||||
|
||||
### Шаг 4: Запуск Cursor при уже поднятом туннеле
|
||||
|
||||
Порядок каждый раз:
|
||||
|
||||
1. В терминале: `ssh -D 10809 -f -N hsites-ahau` (или ваш хост).
|
||||
1. В терминале: `./docs/cursor/scripts/cursor-socks-tunnel.sh start` (или ручная команда из шага 1).
|
||||
2. Запуск Cursor как обычно.
|
||||
|
||||
Чтобы не забывать туннель, можно завести скрипт запуска (см. [Скрипт запуска с туннелем](#скрипт-запуска-с-туннелем)).
|
||||
@@ -194,7 +204,7 @@ ssh -D 10809 -f -N -o ServerAliveInterval=30 -o ServerAliveCountMax=6 hsites-aha
|
||||
./docs/cursor/scripts/cursor-socks-tunnel.sh stop # остановить
|
||||
```
|
||||
|
||||
Переменные окружения: `CURSOR_SOCKS_PORT` (по умолчанию 10809), `CURSOR_TUNNEL_HOST` (по умолчанию `hsites-ahau`).
|
||||
Переменные окружения: `CURSOR_SOCKS_PORT` (10809), **`CURSOR_SOCKS_VIA_DO`** (по умолчанию **1** — выход через DO), при **`CURSOR_SOCKS_VIA_DO=0`** — `CURSOR_TUNNEL_HOST` (по умолчанию `hsites-ahau`).
|
||||
|
||||
**Запуск Cursor после туннеля (macOS):** после `./docs/cursor/scripts/cursor-socks-tunnel.sh start` откройте Cursor как обычно; при настроенных `http.proxy`/`https.proxy` трафик пойдёт через SOCKS.
|
||||
|
||||
@@ -204,11 +214,12 @@ ssh -D 10809 -f -N -o ServerAliveInterval=30 -o ServerAliveCountMax=6 hsites-aha
|
||||
|
||||
| Документ | Назначение |
|
||||
|----------|------------|
|
||||
| [PROXY_GUIDE.md](../../connectivity/PROXY_GUIDE.md) | Прокси ru.hunab.app, SSH ProxyCommand для scp/ssh на прод |
|
||||
| [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) | Сборка AAB из РФ через Maven proxy + SSH-туннель |
|
||||
| [PROXY_GUIDE.md](../connectivity/PROXY_GUIDE.md) | Прокси ru.hunab.app, SSH ProxyCommand для scp/ssh на прод |
|
||||
| [WINDOWS_CURSOR_TUNNEL.md](../connectivity/WINDOWS_CURSOR_TUNNEL.md) | Cursor из РФ на **Windows**: туннель, скрипт .ps1, автозапуск |
|
||||
| [BUILD_AAB_FROM_RUSSIA_GUIDE.md](../connectivity/BUILD_AAB_FROM_RUSSIA_GUIDE.md) | Сборка AAB из РФ через Maven proxy + SSH-туннель |
|
||||
| [Cursor Forum — HTTP/2 network error](https://forum.cursor.com/t/http2-network-error-with-cursor-ide-and-cursor-cli/147318) | Обсуждение ошибок HTTP/2 и региональной маршрутизации |
|
||||
| [Cursor Forum — Agent timeouts when HTTP2 disabled](https://forum.cursor.com/t/agent-timeouts-much-more-when-http2-disabled/76517) | Таймауты агента при отключённом HTTP/2 |
|
||||
|
||||
---
|
||||
|
||||
*Последнее обновление: 2026-03-08*
|
||||
*Последнее обновление: 2026-04-08*
|
||||
107
docs/apps/cursor/scripts/cursor-socks-tunnel.ps1
Normal file
107
docs/apps/cursor/scripts/cursor-socks-tunnel.ps1
Normal file
@@ -0,0 +1,107 @@
|
||||
# SOCKS5-туннель для Cursor (обход PING timeout из РФ).
|
||||
# Использование: .\cursor-socks-tunnel.ps1 [start|stop|status]
|
||||
# См. docs/cursor/README.md и docs/connectivity/WINDOWS_CURSOR_TUNNEL.md
|
||||
|
||||
param(
|
||||
[Parameter(Position = 0)]
|
||||
[ValidateSet("start", "stop", "status")]
|
||||
[string]$Command = "start"
|
||||
)
|
||||
|
||||
$CURSOR_SOCKS_PORT = if ($env:CURSOR_SOCKS_PORT) { $env:CURSOR_SOCKS_PORT } else { "10809" }
|
||||
$SSH_HOST = if ($env:CURSOR_TUNNEL_HOST) { $env:CURSOR_TUNNEL_HOST } else { "ahau@149.154.64.19" }
|
||||
$CursorViaDo = ($env:CURSOR_SOCKS_VIA_DO -ne "0")
|
||||
|
||||
function Test-PortListening {
|
||||
$conn = Get-NetTCPConnection -LocalPort $CURSOR_SOCKS_PORT -State Listen -ErrorAction SilentlyContinue
|
||||
return $null -ne $conn
|
||||
}
|
||||
|
||||
function Get-SshTunnelPids {
|
||||
# Найти процессы ssh.exe, у которых в командной строке есть -D и наш порт
|
||||
Get-CimInstance Win32_Process -Filter "Name = 'ssh.exe'" -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.CommandLine -match "-D\s+$CURSOR_SOCKS_PORT" } |
|
||||
ForEach-Object { $_.ProcessId }
|
||||
}
|
||||
|
||||
function Start-Tunnel {
|
||||
if (Test-PortListening) {
|
||||
Write-Host "SOCKS already listening on 127.0.0.1:$CURSOR_SOCKS_PORT"
|
||||
return
|
||||
}
|
||||
$targetHost = $SSH_HOST
|
||||
$stableOpts = @(
|
||||
"-o", "TCPKeepAlive=yes",
|
||||
"-o", "IPQoS=throughput",
|
||||
"-o", "ServerAliveInterval=60",
|
||||
"-o", "ServerAliveCountMax=4"
|
||||
)
|
||||
$ruOpts = @(
|
||||
"-o", "ServerAliveInterval=30",
|
||||
"-o", "ServerAliveCountMax=6"
|
||||
)
|
||||
if ($CursorViaDo) {
|
||||
& ssh -o BatchMode=yes -o ConnectTimeout=6 -o ConnectionAttempts=1 hsites-ahau "exit 0" 2>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "hsites-ahau недоступен: SOCKS только по цепочке Mac→RU→DO (без прямого Mac→DO). Восстановите SSH до RU или задайте CURSOR_SOCKS_VIA_DO=0."
|
||||
exit 1
|
||||
}
|
||||
$targetHost = "telegram-socks-via-do"
|
||||
}
|
||||
$sshArgs = @(
|
||||
"-D", $CURSOR_SOCKS_PORT,
|
||||
"-f", "-N"
|
||||
)
|
||||
if ($CursorViaDo) {
|
||||
$sshArgs += $stableOpts
|
||||
} else {
|
||||
$sshArgs += $ruOpts
|
||||
}
|
||||
$sshArgs += $targetHost
|
||||
& ssh @sshArgs
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
if ($CursorViaDo) {
|
||||
Write-Host "SOCKS tunnel started: 127.0.0.1:$CURSOR_SOCKS_PORT -> $targetHost (DO exit)"
|
||||
} else {
|
||||
Write-Host "SOCKS tunnel started: 127.0.0.1:$CURSOR_SOCKS_PORT -> $targetHost (RU exit, CURSOR_SOCKS_VIA_DO=0)"
|
||||
}
|
||||
} else {
|
||||
Write-Error "Failed to start tunnel (exit code $LASTEXITCODE). Check SSH config (telegram-socks-via-do) or host: $targetHost"
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
}
|
||||
|
||||
function Stop-Tunnel {
|
||||
$pids = Get-SshTunnelPids
|
||||
if ($pids) {
|
||||
foreach ($pid in $pids) {
|
||||
Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Write-Host "Tunnel stopped (killed SSH process(es): $($pids -join ', '))."
|
||||
} else {
|
||||
# Fallback: убить процесс, занимающий порт 10809
|
||||
$conn = Get-NetTCPConnection -LocalPort $CURSOR_SOCKS_PORT -State Listen -ErrorAction SilentlyContinue
|
||||
if ($conn) {
|
||||
$conn | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }
|
||||
Write-Host "Tunnel stopped (killed process on port $CURSOR_SOCKS_PORT)."
|
||||
} else {
|
||||
Write-Host "No SOCKS tunnel found on port $CURSOR_SOCKS_PORT."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-Status {
|
||||
if (Test-PortListening) {
|
||||
Write-Host "SOCKS listening on 127.0.0.1:$CURSOR_SOCKS_PORT"
|
||||
Get-NetTCPConnection -LocalPort $CURSOR_SOCKS_PORT -State Listen -ErrorAction SilentlyContinue |
|
||||
Format-Table LocalAddress, LocalPort, State, OwningProcess -AutoSize
|
||||
} else {
|
||||
Write-Host "No SOCKS listener on port $CURSOR_SOCKS_PORT"
|
||||
}
|
||||
}
|
||||
|
||||
switch ($Command) {
|
||||
"start" { Start-Tunnel }
|
||||
"stop" { Stop-Tunnel }
|
||||
"status" { Get-Status }
|
||||
}
|
||||
122
docs/connectivity/HOTSPOT_NETWORK_EMERGENCY.md
Normal file
122
docs/connectivity/HOTSPOT_NETWORK_EMERGENCY.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Emergency: сеть после смены хотспота (macOS)
|
||||
|
||||
Симптом: **отключили Mac от Wi‑Fi / переподключились к другому хотспоту — «интернета нет», помогает только перезагрузка.**
|
||||
|
||||
Документ описывает **проверенную на машине причину** в вашей конфигурации, **ручное восстановление без перезагрузки** и **скрипт** из репозитория.
|
||||
|
||||
---
|
||||
|
||||
## Причина (точная по конфигурации)
|
||||
|
||||
На macOS в **Системных настройках → Сеть → Wi‑Fi → Подробнее → Прокси** часто включён **автоматический прокси (PAC)** с URL вида **`http://localhost:1089/proxy.pac`** (см. [PROXY_GUIDE.md](./PROXY_GUIDE.md) → раздел про системный PAC).
|
||||
|
||||
Проверка на рабочей машине (2026): **PAC для сервиса Wi‑Fi включён (Enabled: Yes)**, при этом **на порту 1089 нет процесса**, который отдаёт `proxy.pac` — соединение к `127.0.0.1:1089` отклоняется.
|
||||
|
||||
В таком состоянии:
|
||||
|
||||
1. Система и часть приложений **сначала** должны получить PAC с localhost; при недоступном URL поведение **нестабильно** (часть трафика «в никуда», таймауты).
|
||||
2. При **смене хотспота** меняются шлюз, DNS, часто появляется **captive portal**; страница входа может **не открыться**, пока PAC мешает или DNS закеширован со старой сети.
|
||||
3. **Перезагрузка** «лечит», потому что заново поднимаются **configd**, **mDNSResponder**, порядок инициализации Wi‑Fi и иногда снова стартует локальный PAC‑сервер (Clash / Surge / свой сервис на 1089).
|
||||
|
||||
Дополнительные усилители сбоя (проверять при `status`):
|
||||
|
||||
- **Системный SOCKS** на `127.0.0.1:1080` (Shadowsocks и т.д.) — если включён, а клиент не слушает порт, трафик через системный прокси тоже падает.
|
||||
- **VPN / туннели** с политикой «весь трафик» — после смены сети маршруты могут остаться битые до отключения VPN.
|
||||
|
||||
**SOCKS Telegram (1081) и SSH‑туннели сами по себе не отключают Wi‑Fi** — они приложенческие. Проблема класса «всё мёртво до ребута» у вас с высокой вероятностью — **системный PAC/SOCKS на localhost без живого сервиса + кэш DNS / состояние Wi‑Fi**.
|
||||
|
||||
---
|
||||
|
||||
## Быстро: один скрипт (рекомендуется)
|
||||
|
||||
Из корня репозитория `local_machine`:
|
||||
|
||||
```bash
|
||||
chmod +x ./docs/connectivity/scripts/macos-hotspot-network-recover.sh
|
||||
./docs/connectivity/scripts/macos-hotspot-network-recover.sh status
|
||||
./docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
|
||||
```
|
||||
|
||||
`recover` делает: **выключает PAC** (те же сервисы, что и `macos-system-pac.sh`), **выключает системный SOCKS на Wi‑Fi**, по возможности **сбрасывает DNS** и **кратко перезапускает Wi‑Fi** и DHCP.
|
||||
|
||||
Если скрипт пишет, что нет `sudo` без пароля — выполните вручную команды из раздела «Вручную» ниже.
|
||||
|
||||
После появления интернета, если вам снова нужен PAC:
|
||||
|
||||
```bash
|
||||
./docs/connectivity/scripts/macos-pac-on.sh
|
||||
```
|
||||
|
||||
Только **после** того как процесс на **1089** реально запущен и отдаёт `proxy.pac`.
|
||||
|
||||
---
|
||||
|
||||
## Вручную (без скрипта)
|
||||
|
||||
### 1. Отключить автоматический PAC
|
||||
|
||||
**GUI:** Системные настройки → Сеть → Wi‑Fi → Подробнее → Прокси → снять **«Автоматическая настройка прокси (PAC)»**.
|
||||
|
||||
**CLI** (из репо):
|
||||
|
||||
```bash
|
||||
./docs/connectivity/scripts/macos-pac-off.sh
|
||||
./docs/connectivity/scripts/macos-system-pac.sh status
|
||||
```
|
||||
|
||||
### 2. Отключить системный SOCKS (если включали для Shadowsocks)
|
||||
|
||||
```bash
|
||||
networksetup -setsocksfirewallproxystate "Wi-Fi" off
|
||||
networksetup -getsocksfirewallproxy "Wi-Fi"
|
||||
```
|
||||
|
||||
### 3. Сброс DNS
|
||||
|
||||
```bash
|
||||
sudo dscacheutil -flushcache
|
||||
sudo killall -HUP mDNSResponder
|
||||
```
|
||||
|
||||
### 4. Обновить DHCP и «пнуть» Wi‑Fi
|
||||
|
||||
Узнать интерфейс Wi‑Fi (часто `en0`):
|
||||
|
||||
```bash
|
||||
networksetup -listallhardwareports | sed -n '/Hardware Port: Wi-Fi/,/Ethernet Address/p'
|
||||
```
|
||||
|
||||
Дальше (подставьте свой `Device`, например `en0`):
|
||||
|
||||
```bash
|
||||
sudo ipconfig set en0 DHCP
|
||||
networksetup -setairportpower en0 off
|
||||
sleep 2
|
||||
networksetup -setairportpower en0 on
|
||||
```
|
||||
|
||||
### 5. Captive portal
|
||||
|
||||
Откройте в браузере **`http://captive.apple.com`** или **`http://neverssl.com`** — должна открыться страница входа в сеть отели/кафе.
|
||||
|
||||
---
|
||||
|
||||
## Профилактика (архитектурно)
|
||||
|
||||
1. **Не держать PAC включённым**, если сервис на **1089** не запущен: сначала поднять Clash/Surge/свой HTTP‑сервер с PAC, **затем** `macos-pac-on.sh`.
|
||||
2. Перед подключением к **чужому хотспоту** с обязательной страницей входа — **временно PAC off** (`macos-pac-off.sh`), залогиниться в сеть, затем при необходимости снова **on**.
|
||||
3. Разнести роли: **глобальный PAC** — только когда стабильно есть локальный конфиг‑сервер; для «чистого» интернета в дороге — **PAC выключен**, прокси только в приложениях (Telegram SOCKS **1081**, Cursor **10809** и т.д.) — см. [TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md), [PROXY_EMERGENCY.md](./PROXY_EMERGENCY.md).
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
| Документ | Зачем |
|
||||
|----------|--------|
|
||||
| [PROXY_GUIDE.md](./PROXY_GUIDE.md) | Системный PAC, порты, скрипты `macos-pac-*.sh` |
|
||||
| [PROXY_EMERGENCY.md](./PROXY_EMERGENCY.md) | SSH, SOCKS 1081, прод |
|
||||
| [README.md](./README.md) | Оглавление `connectivity/` |
|
||||
|
||||
---
|
||||
|
||||
*Если после `recover` сеть всё ещё мёртвая — проверьте аппаратный тумблер Wi‑Fi, другое SSID, и логи: Консоль → `Wi-Fi` / `configd`.*
|
||||
172
docs/connectivity/PROXY_EMERGENCY.md
Normal file
172
docs/connectivity/PROXY_EMERGENCY.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# Экстренное восстановление: прокси ru.hunab.app и SSH
|
||||
|
||||
Одна страница на случай, когда «всё слетело». Полная схема, безопасность и нюансы `~/.ssh/config`: [PROXY_GUIDE.md](./PROXY_GUIDE.md).
|
||||
|
||||
**Сеть / хотспот (интернет есть только после перезагрузки):** чаще всего системный **PAC на localhost:1089** без живого сервера или системный SOCKS на **1080**. Пошагово: **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)**.
|
||||
|
||||
---
|
||||
|
||||
## Константы (запомнить / вставить в заметки)
|
||||
|
||||
| Что | Значение |
|
||||
|-----|----------|
|
||||
| RU-прокси (SSH) | **149.154.64.19**, пользователь **ahau**, порт **22** |
|
||||
| Домен прокси | **ru.hunab.app** |
|
||||
| Прод (DO) | **209.38.32.21**, SSH **2222**, пользователь **hunab** (или **root**) |
|
||||
| Прод (веб) | **hunab.app** |
|
||||
|
||||
**Ключи (типично):**
|
||||
|
||||
- RU (ahau): `~/.ssh/id_ed25519`
|
||||
- RU root: `~/.ssh/hsites_new_deploy_key` → хост **`hsites-new`**
|
||||
- Прод: `~/.ssh/hunab_deploy_key`
|
||||
|
||||
---
|
||||
|
||||
## Порядок проверки (2 минуты)
|
||||
|
||||
1. **Интернет и DNS:** открыть в браузере `https://ru.hunab.app/proxy-health` и `https://ru.hunab.app/api/health`.
|
||||
2. **RU SSH:** `ssh -o ConnectTimeout=10 hsites-ahau "echo RU_OK"`
|
||||
Без алиаса: `ssh -o ConnectTimeout=10 -i ~/.ssh/id_ed25519 ahau@149.154.64.19 "echo RU_OK"`
|
||||
3. **Прод через RU:** `ssh -o ConnectTimeout=15 hunab-prod "echo PROD_OK"`
|
||||
4. **Прод напрямую** (если RU мёртв): `ssh -o ConnectTimeout=15 hunab-prod-direct "echo PROD_DIRECT_OK"`
|
||||
|
||||
Если шаг 2 падает — цепочка через прокси недоступна; используйте **прямой** доступ к DO (шаг 4) и SOCKS «через DO» (ниже).
|
||||
|
||||
---
|
||||
|
||||
## Минимальные блоки `~/.ssh/config` (восстановить вручную)
|
||||
|
||||
**Важно:** `hunab-prod` и `hunab-prod-root` — **разные** блоки `Host` (разный `User`). Не объединять в одну строку `Host`.
|
||||
|
||||
```sshconfig
|
||||
Host hsites-ahau
|
||||
HostName 149.154.64.19
|
||||
User ahau
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
StrictHostKeyChecking accept-new
|
||||
ServerAliveInterval 30
|
||||
ServerAliveCountMax 8
|
||||
|
||||
Host hunab-prod hunab-prod-via-ahau
|
||||
HostName 209.38.32.21
|
||||
User hunab
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
ProxyJump hsites-ahau
|
||||
StrictHostKeyChecking accept-new
|
||||
IdentitiesOnly yes
|
||||
ServerAliveInterval 30
|
||||
ServerAliveCountMax 8
|
||||
|
||||
Host hunab-prod-direct
|
||||
HostName 209.38.32.21
|
||||
User hunab
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
StrictHostKeyChecking accept-new
|
||||
IdentitiesOnly yes
|
||||
ServerAliveInterval 30
|
||||
ServerAliveCountMax 8
|
||||
```
|
||||
|
||||
При необходимости root на проде — отдельный блок `Host hunab-prod-root` / `hunab-prod-root-direct` с `User root` (см. [PROXY_GUIDE.md](./PROXY_GUIDE.md)).
|
||||
|
||||
**Зависший master-сокет:**
|
||||
`ssh -O exit hunab-prod 2>/dev/null; ssh -O exit hunab-prod-direct 2>/dev/null; rm -f ~/.ssh/cm-hunab-*`
|
||||
|
||||
---
|
||||
|
||||
## SOCKS для Telegram (порт **1081**)
|
||||
|
||||
**Только RU (если достаточно выхода с 149.154.64.19):**
|
||||
|
||||
```bash
|
||||
ssh -D 1081 -N ahau@149.154.64.19
|
||||
```
|
||||
|
||||
**RU → прод → интернет** (когда с RU до Telegram не проходит):
|
||||
|
||||
```bash
|
||||
ssh -D 1081 -N -J hsites-ahau -i ~/.ssh/hunab_deploy_key -p 2222 hunab@209.38.32.21
|
||||
```
|
||||
|
||||
**Без RU, только DO:**
|
||||
|
||||
```bash
|
||||
ssh -D 1081 -N -i ~/.ssh/hunab_deploy_key -p 2222 hunab@209.38.32.21
|
||||
```
|
||||
|
||||
Проверка (второй терминал):
|
||||
|
||||
```bash
|
||||
curl -x socks5h://127.0.0.1:1081 -sI https://api.telegram.org | head -3
|
||||
```
|
||||
|
||||
В Telegram: **SOCKS5**, **127.0.0.1**, **1081**.
|
||||
Скрипты и launchd: [launchd/README.md](./launchd/README.md), [scripts/telegram-socks-tunnel.sh](./scripts/telegram-socks-tunnel.sh).
|
||||
|
||||
---
|
||||
|
||||
## scp на прод через RU (если нужен файл «прямо сейчас»)
|
||||
|
||||
```bash
|
||||
PROXY_CMD="ssh -i $HOME/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new -W %h:%p hsites-ahau"
|
||||
scp -i ~/.ssh/hunab_deploy_key -o ProxyCommand="$PROXY_CMD" -o ServerAliveInterval=15 \
|
||||
/path/to/file hunab@209.38.32.21:/home/hunab/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cursor / PING timeout из РФ
|
||||
|
||||
SOCKS для IDE: [docs/cursor/README.md](../cursor/README.md), порт **10809**. Скрипт **`docs/cursor/scripts/cursor-socks-tunnel.sh`** при **`CURSOR_SOCKS_VIA_DO=1`** — только **Mac→RU→DO** (`telegram-socks-via-do`); **`CURSOR_SOCKS_VIA_DO=0`** — выход с RU. Не путать с **1081** (Telegram).
|
||||
|
||||
---
|
||||
|
||||
## Скрипт выбора маршрута к проду
|
||||
|
||||
Из репозитория: [scripts/ssh-hunab-prod.sh](./scripts/ssh-hunab-prod.sh)
|
||||
- `HUNAB_PROD_FORCE_VIA=1` — сразу через RU
|
||||
- `HUNAB_PROD_FORCE_DIRECT=1` — только прямой DO
|
||||
|
||||
---
|
||||
|
||||
*Краткая версия от PROXY_GUIDE; при расхождении правьте сначала основной гайд.*
|
||||
|
||||
---
|
||||
|
||||
## Как сейчас на этой машине (проверено: **2026-04-07**)
|
||||
|
||||
**Среда:** macOS, пользователь **`eternal`**, имя хоста **`Eternals.local`**.
|
||||
|
||||
### `~/.ssh/config`
|
||||
|
||||
- В начале файла: **`Include ~/.colima/ssh_config`** (Colima).
|
||||
- **RU → прод (основной путь):** `hunab-prod` и `hunab-prod-via-ahau` → `209.38.32.21:2222`, пользователь **`hunab`**, ключ **`~/.ssh/hunab_deploy_key`**, **`ProxyJump hsites-ahau`**, плюс **ControlMaster** / **`ControlPath ~/.ssh/cm-hunab-prod-%C`**, **ControlPersist 30m**, keepalive, **UseKeychain**.
|
||||
- **Прод без RU:** `hunab-prod-direct`, root-вариант **`hunab-prod-root-direct`** — те же порт/ключ, без `ProxyJump`, свой **`ControlPath`** (`cm-hunab-direct-*` / `cm-hunab-direct-root-*`).
|
||||
- **Root через RU:** `hunab-prod-root`, `hunab-prod-root-via-ahau` — как `hunab-prod`, но **`User root`** и **`cm-hunab-prod-root-%C`**.
|
||||
- **RU SSH (ahau):** **`hsites-ahau`** → **`149.154.64.19`**, **`~/.ssh/id_ed25519`**, keepalive, **UseKeychain** / **AddKeysToAgent**.
|
||||
- **RU SSH (root):** **`hsites-new`** → тот же IP, **`~/.ssh/hsites_new_deploy_key`**.
|
||||
- **Альтернатива через root на RU:** **`prod-ru`**, **`hunab-prod-via-ru`** — **`ProxyJump hsites-new`** (не `hsites-ahau`). В конфиге блок **`Host prod-ru` продублирован дважды** — лишнее можно схлопнуть при правке.
|
||||
- **Туннель через локальный SOCKS 1090:** **`prod-tunnel`** — `ProxyCommand nc -X 5 -x 127.0.0.1:1090 %h %p`.
|
||||
- **SOCKS для Telegram:** только **`telegram-socks-via-do`** (Mac→RU→DO). **`telegram-socks-direct-do`** — опционально для ручного SSH к DO без RU, не для автозамены SOCKS.
|
||||
- Дополнительно в файле: старые хосты **`hsites` / `hsites.hunab`**, **`ahau@ru.hunab`**, боты **`hunab-bot` / `hunab.bots`**, алиас **`tunnel`** на `149.154.64.19`.
|
||||
|
||||
**Ключи на диске (есть):** `~/.ssh/id_ed25519`, `~/.ssh/hunab_deploy_key`, `~/.ssh/hsites_new_deploy_key`.
|
||||
|
||||
### Автозапуск SOCKS **1081** (Telegram)
|
||||
|
||||
- **LaunchAgent:** `~/Library/LaunchAgents/com.hunab.telegram-socks-via-do.plist` — **RunAtLoad**, **KeepAlive**, лог **`/tmp/telegram-socks-tunnel-via-do.log`**.
|
||||
- **Скрипт:** `~/Library/Application Support/hunab/telegram-socks-launch.sh` — всегда **`ssh -D 1081 -N telegram-socks-via-do`** (Mac→RU→DO).
|
||||
- Состояние на момент проверки: задание в **launchctl** в статусе **running**; при обрыве цепочки SSH в логе сыплются ошибки до следующего успешного подключения (смотреть хвост лога).
|
||||
|
||||
### Ручной запуск из репозитория
|
||||
|
||||
- Скрипт: **`docs/connectivity/scripts/telegram-socks-tunnel.sh`** — порт **1081**; без `TELEGRAM_SOCKS_VIA_DO` — только RU; с **`TELEGRAM_SOCKS_VIA_DO=1`** — только Mac→RU→DO (`telegram-socks-via-do`), как LaunchAgent.
|
||||
|
||||
### Прочее на машине
|
||||
|
||||
- В **launchctl** также видны **Shadowsocks** (`com.shadowsocks.local`, `com.qiuyuzhou.shadowsocksX-NG.local`) — к hunab-прокси не относятся, но могут влиять на маршрутизацию, если включены.
|
||||
|
||||
*Порт **1081** и SSH в момент автоматической проверки из среды агента могли быть пустыми или с ошибками сети; ориентируйтесь на `lsof -i :1081` и лог выше.*
|
||||
@@ -6,13 +6,49 @@
|
||||
|
||||
## Оглавление
|
||||
|
||||
1. [Доступ и файлы](#-доступ-и-файлы)
|
||||
2. [Команды управления](#-команды-управления)
|
||||
3. [Безопасность](#-безопасность)
|
||||
4. [Запросы-сканеры (serverless и др.)](#-запросы-сканеры-serverless-и-др)
|
||||
5. [Редирект на прокси](#-автоматический-редирект)
|
||||
6. [Скорость и тесты](#-проверка-скорости)
|
||||
7. [Telegram и SOCKS при старте](#-telegram-и-socks-при-старте)
|
||||
1. [Схема соединений](#схема-соединений)
|
||||
2. [Доступ и файлы](#-доступ-и-файлы)
|
||||
3. [Клиентский SSH config и KexAlgorithms](#клиентский-ssh-config-и-kexalgorithms)
|
||||
4. [Команды управления](#-команды-управления)
|
||||
5. [Безопасность](#-безопасность)
|
||||
6. [Запросы-сканеры (serverless и др.)](#-запросы-сканеры-serverless-и-др)
|
||||
7. [Редирект на прокси](#-автоматический-редирект)
|
||||
8. [Скорость и тесты](#-проверка-скорости)
|
||||
9. [Telegram и SOCKS при старте](#-telegram-и-socks-при-старте)
|
||||
10. [SSH к продакшену (hunab-prod)](#ssh-к-продакшену-hunab-prod)
|
||||
11. [macOS: системный PAC](#-macos-системный-pac)
|
||||
|
||||
---
|
||||
|
||||
## Схема соединений
|
||||
|
||||
Текущая логика с **вашего Mac** (или другой клиентской машины с тем же `~/.ssh/config`):
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph client [Клиент]
|
||||
M["SSH, scp, Docker CLI,\nTelegram с SOCKS"]
|
||||
end
|
||||
RU["RU-прокси 149.154.64.19\nru.hunab.app, SSH 22, user ahau\n(Host hsites-ahau)"]
|
||||
DO["Прод 209.38.32.21 DigitalOcean\nSSH 2222, hunab / root\nDocker, Nginx, приложения"]
|
||||
NET["Интернет (выход с DO)"]
|
||||
|
||||
M -->|"Основной: ssh hunab-prod,\nSOCKS telegram-socks-via-do"| RU
|
||||
RU -->|ProxyJump / туннель| DO
|
||||
M -->|"Прямой SSH: hunab-prod-direct\n(SOCKS через DO — только via-do)"| DO
|
||||
DO --> NET
|
||||
```
|
||||
|
||||
**Кратко:**
|
||||
|
||||
| Направление | Как задаётся |
|
||||
|-------------|----------------|
|
||||
| Mac → RU | `ssh hsites-ahau`, первый прыжок в **ProxyJump** |
|
||||
| RU → прод DO | Второй hop SSH на **209.38.32.21:2222** |
|
||||
| Mac → прод без RU | `hunab-prod-direct`; **`telegram-socks-direct-do`** — тот же hop, для ручного SSH/scp, не для авто-SOCKS |
|
||||
| Telegram / Cursor SOCKS → интернет с IP DO | Только **Mac→RU→DO** (`telegram-socks-via-do`); прямой Mac→DO в скриптах не используется |
|
||||
|
||||
Прод и RU — **разные** машины; ключи разные: к RU обычно **`id_ed25519`** (ahau), к проду на DO — **`hunab_deploy_key`**.
|
||||
|
||||
---
|
||||
|
||||
@@ -36,6 +72,79 @@
|
||||
- Health: https://ru.hunab.app/proxy-health
|
||||
- API: https://ru.hunab.app/api/health
|
||||
|
||||
### Клиентский SSH config и KexAlgorithms
|
||||
|
||||
Файл на той машине, **с которой** вы подключаетесь (обычно Mac):
|
||||
|
||||
- путь **`~/.ssh/config`**;
|
||||
- на Mac это тот же путь в домашнем каталоге, например **`/Users/<ваш-логин>/.ssh/config`**.
|
||||
|
||||
Сюда добавляются `Host`, `ProxyJump`, ключи и при необходимости опции вроде **`KexAlgorithms`**.
|
||||
|
||||
#### Нельзя смешивать `hunab-prod` и `hunab-prod-root` в одном блоке `Host`
|
||||
|
||||
В одной строке **`Host hunab-prod hunab-prod-root`** может быть только **один** `User`. Если указать `User hunab`, то при вызове `ssh hunab-prod-root` клиент всё равно пойдёт как **hunab** — это **ошибка**.
|
||||
|
||||
**Правильно:** два отдельных блока — один с **`User hunab`**, второй с **`User root`** (и при необходимости разные `ControlPath` / алиасы).
|
||||
|
||||
**Можно** в одном блоке перечислить алиасы с **одним и тем же** пользователем и маршрутом, например: **`Host hunab-prod hunab-prod-via-ahau`** — оба имени ведут на прод как **hunab** через **`ProxyJump hsites-ahau`**.
|
||||
|
||||
#### Имеет ли смысл `KexAlgorithms`?
|
||||
|
||||
На актуальных **macOS** и **OpenSSH** на Ubuntu 24 набор алгоритмов обмена ключей по умолчанию уже **нормальный** (в т.ч. curve25519). Явно задавать **`KexAlgorithms`** стоит **не «на всякий случай»**, а когда есть **конкретная** проблема: сообщение вроде *Unable to negotiate … no matching key exchange method*, старый сервер или нестандартный посредник в сети.
|
||||
|
||||
**Минусы** жёсткого списка: если на сервере **sshd** не поддерживает ни один из перечисленных KEX, подключение **сломается**. Поэтому список должен **пересекаться** с тем, что включено на сервере (`ssh -Q kex` на клиенте, настройки `sshd_config` на сервере).
|
||||
|
||||
Если всё же добавляете — вставляйте строку **в каждый нужный блок отдельно** (`hunab-prod`, `hunab-prod-root`, при цепочке через RU при желании и **`Host hsites-ahau`**), **не объединяя** hunab и root в один блок из‑за разного `User`.
|
||||
|
||||
Пример **опционально** (без `ProxyJump` здесь не показан — в реальном конфиге сохраняйте свои `ProxyJump`, `Port`, `ControlPath` и т.д.):
|
||||
|
||||
```
|
||||
Host hunab-prod
|
||||
HostName 209.38.32.21
|
||||
User hunab
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
StrictHostKeyChecking accept-new
|
||||
IdentitiesOnly yes
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
|
||||
Host hunab-prod-root
|
||||
HostName 209.38.32.21
|
||||
User root
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
StrictHostKeyChecking accept-new
|
||||
IdentitiesOnly yes
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
```
|
||||
|
||||
У **текущего** репозиторийного сценария **`hunab-prod` по умолчанию через `hsites-ahau`** — в блок **`Host hunab-prod`** нужно добавлять и **`ProxyJump hsites-ahau`** (как в [TELEGRAM_GUIDE](./TELEGRAM_GUIDE.md) и полных шаблонах ниже), а не только поля из примера выше.
|
||||
|
||||
### SSH к продакшену (hunab-prod)
|
||||
|
||||
Прод: **209.38.32.21**, SSH **порт 2222**, пользователь **hunab** (или **root**), ключ **`hunab_deploy_key`**.
|
||||
|
||||
| Host в `~/.ssh/config` | Назначение |
|
||||
|------------------------|------------|
|
||||
| **hunab-prod** (и алиас **hunab-prod-via-ahau**) | **По умолчанию:** **Mac → hsites-ahau → прод**. Обычная команда `ssh -T hunab-prod 'docker …'` сразу идёт через RU — **не нужен** `export HUNAB_PROD_FORCE_VIA` (эта переменная только для скрипта `ssh-hunab-prod.sh`). |
|
||||
| **hunab-prod-direct** | Прямое TCP к **209.38.32.21:2222** — когда RU недоступен, а DO доступен. |
|
||||
| **hunab-prod-root** / **hunab-prod-root-via-ahau** | Root через **hsites-ahau**; **hunab-prod-root-direct** — прямой root. |
|
||||
| **hunab-prod-via-ru**, **prod-ru** | ProxyJump через **hsites-new** (root); ключ **`hsites_new_deploy_key`**. |
|
||||
|
||||
**Два бэкенда в логах (пример):**
|
||||
|
||||
```bash
|
||||
ssh -T hunab-prod 'docker logs -f --tail=80 hunab-prod-backend-green 2>&1 | sed -u "s/^/[GREEN] /" & docker logs -f --tail=80 hunab-prod-backend 2>&1 | sed -u "s/^/[BLUE ] /" & wait'
|
||||
```
|
||||
|
||||
**Скрипт** `ssh-hunab-prod.sh` — при необходимости сначала пробует **hunab-prod-direct** (8 с), иначе **hunab-prod**:
|
||||
`HUNAB_PROD_FORCE_VIA=1` — сразу через RU; `HUNAB_PROD_FORCE_DIRECT=1` — только прямой.
|
||||
|
||||
Проверка: `ssh hunab-prod "echo OK"` (через RU), `ssh hunab-prod-direct "echo OK"` (прямой).
|
||||
|
||||
**Стабильность:** **ServerAliveInterval 30**, **ServerAliveCountMax 8**, **ControlMaster** / **ControlPersist 30m** на прод-хостах и **hsites-ahau**. Сброс: `ssh -O exit hunab-prod`, `ssh -O exit hunab-prod-direct`, при необходимости `rm -f ~/.ssh/cm-hunab-*`.
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Команды управления
|
||||
@@ -105,9 +214,36 @@ ssh -i ~/.ssh/hunab_deploy_key -o ProxyCommand="$PROXY_CMD" hunab@209.38.32.21 "
|
||||
|
||||
## Cursor IDE (PING timeout из РФ)
|
||||
|
||||
Если Cursor выдаёт **PING timed out** при работе из России, трафик можно пустить через тот же сервер по SOCKS (SSH -D). Подробно:
|
||||
Если Cursor выдаёт **PING timed out** при работе из России, трафик можно пустить через SOCKS (SSH `-D`) на **127.0.0.1:10809**. Скрипт **`docs/cursor/scripts/cursor-socks-tunnel.sh`** при **`CURSOR_SOCKS_VIA_DO=1`** поднимает **только Mac→RU→DO** (`telegram-socks-via-do`); без доступа к RU — ошибка (без прямого Mac→DO). **`CURSOR_SOCKS_VIA_DO=0`** — выход только с RU (`hsites-ahau`).
|
||||
|
||||
📖 **[docs/cursor/README.md](../docs/cursor/README.md)** — обход PING timeout через SOCKS-туннель (без отключения HTTP/2).
|
||||
📖 **[docs/cursor/README.md](../cursor/README.md)** — пошагово, проверки и Windows (`cursor-socks-tunnel.ps1`).
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ macOS: системный PAC
|
||||
|
||||
В **Системных настройках → Сеть** на Mac может быть включён **автоматический прокси** с файлом конфигурации **PAC** (часто `http://localhost:1089/proxy.pac`). Пока PAC включён, браузер и часть приложений ходят в интернет по правилам из PAC; для **локальной админки роутера** (`192.168.x.x`), **чистого Wi‑Fi без прокси** или диагностики удобно **временно выключить** PAC и потом снова включить.
|
||||
|
||||
**Важно:** если PAC **включён**, а по выбранному URL **ничего не слушает** (например, порт **1089** пуст), macOS и приложения ведут себя непредсказуемо; при **смене хотспота** часто кажется, что «сеть мёртвая до перезагрузки». Это не баг Wi‑Fi как такового, а **нерабочий PAC + кэш DNS**. Скрипт восстановления и пошаговый emergency: **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)** (`macos-hotspot-network-recover.sh`).
|
||||
|
||||
**Быстрые скрипты** (из корня репозитория):
|
||||
|
||||
```bash
|
||||
./docs/connectivity/scripts/macos-pac-on.sh # включить PAC (URL + состояние On)
|
||||
./docs/connectivity/scripts/macos-pac-off.sh # выключить PAC (URL не стирается, только Off)
|
||||
./docs/connectivity/scripts/macos-system-pac.sh status # что сейчас в networksetup и scutil
|
||||
```
|
||||
|
||||
Полный вариант одной командой: `./docs/connectivity/scripts/macos-system-pac.sh on|off|status`.
|
||||
|
||||
**Переопределения через окружение:**
|
||||
|
||||
- **`MACOS_PAC_URL`** — по умолчанию `http://localhost:1089/proxy.pac`.
|
||||
- **`MACOS_PAC_SERVICES`** — список имён сетевых сервисов **через символ `|`** (как в `networksetup -listallnetworkservices`). По умолчанию: `Wi-Fi|Thunderbolt Bridge 2`.
|
||||
|
||||
Пример только для Wi‑Fi: `MACOS_PAC_SERVICES=Wi-Fi ./docs/connectivity/scripts/macos-pac-off.sh`
|
||||
|
||||
Убедитесь, что процесс, который отдаёт PAC по выбранному URL (порт **1089**), запущен, иначе после `on` приложения могут не открывать сайты.
|
||||
|
||||
---
|
||||
|
||||
@@ -144,6 +280,68 @@ launchctl load ~/Library/LaunchAgents/com.hunab.telegram-socks.plist
|
||||
|
||||
В Telegram (или другом клиенте) укажите прокси: **SOCKS5**, **127.0.0.1**, порт **1081**.
|
||||
|
||||
### Выход через DigitalOcean (прокси → DO → интернет)
|
||||
|
||||
Если с **149.154.64.19** до инфраструктуры Telegram TLS не проходит (типично DPI), поднимите SOCKS так, чтобы **трафик выходил в интернет с прод-сервера DO** (209.38.32.21), а до RU-прокси шёл только SSH. Цепочка: **Mac → hsites-ahau (149.154.64.19) → hunab@209.38.32.21:2222**.
|
||||
|
||||
**Вручную:**
|
||||
|
||||
```bash
|
||||
ssh -D 1081 -N -J hsites-ahau -i ~/.ssh/hunab_deploy_key -p 2222 hunab@209.38.32.21
|
||||
```
|
||||
|
||||
**Скрипт из репо** (те же параметры по умолчанию):
|
||||
|
||||
```bash
|
||||
export TELEGRAM_SOCKS_VIA_DO=1
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh start
|
||||
```
|
||||
|
||||
Переменные при необходимости: `TELEGRAM_SOCKS_JUMP_HOST`, `TELEGRAM_SOCKS_FINAL_HOST`, `TELEGRAM_SOCKS_FINAL_PORT` (по умолчанию 2222), `TELEGRAM_SOCKS_FINAL_IDENTITY`.
|
||||
|
||||
**`~/.ssh/config`** — удобно для LaunchAgent без длинной строки `ssh`:
|
||||
|
||||
```
|
||||
Host telegram-socks-via-do
|
||||
HostName 209.38.32.21
|
||||
User hunab
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
ProxyJump hsites-ahau
|
||||
StrictHostKeyChecking accept-new
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 4
|
||||
ConnectTimeout 60
|
||||
TCPKeepAlive yes
|
||||
IPQoS throughput
|
||||
UseKeychain yes
|
||||
AddKeysToAgent yes
|
||||
```
|
||||
|
||||
**Прямой SSH к DO** (без RU в цепочке) — для админки вроде `hunab-prod-direct`, **не** для автоматического SOCKS «в обход RU»:
|
||||
|
||||
```
|
||||
Host telegram-socks-direct-do
|
||||
HostName 209.38.32.21
|
||||
User hunab
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/hunab_deploy_key
|
||||
StrictHostKeyChecking accept-new
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 4
|
||||
ConnectTimeout 60
|
||||
TCPKeepAlive yes
|
||||
IPQoS throughput
|
||||
UseKeychain yes
|
||||
AddKeysToAgent yes
|
||||
```
|
||||
|
||||
Скрипты **`TELEGRAM_SOCKS_VIA_DO=1`**, **`cursor-socks-tunnel.sh`** при выходе через DO и LaunchAgent **не** переключаются на этот host — только **Mac→RU→DO** через `telegram-socks-via-do`.
|
||||
|
||||
Проверка SOCKS: `ssh -D 1081 -N telegram-socks-via-do` (в другом терминале: `curl -x socks5h://127.0.0.1:1081 -sI https://api.telegram.org | head -3`).
|
||||
|
||||
**Автозапуск (macOS):** `./docs/connectivity/launchd/install-via-do.sh` копирует `telegram-socks-launch.sh`, который всегда запускает **`telegram-socks-via-do`** (Mac→RU→DO). Лог: `/tmp/telegram-socks-tunnel-via-do.log`. Старый RU-only агент: `./docs/connectivity/launchd/uninstall.sh`. Вернуть только RU: `./docs/connectivity/launchd/uninstall-via-do.sh` и `./docs/connectivity/launchd/install.sh`.
|
||||
|
||||
---
|
||||
|
||||
## SSL
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
| Файл / папка | Назначение |
|
||||
|--------------|------------|
|
||||
| **[PROXY_GUIDE.md](./PROXY_GUIDE.md)** | Основное руководство: SSH, Nginx, безопасность, scp через туннель, Cursor, скорость |
|
||||
| **[TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md)** | Гайд по Telegram: быстрые команды и порядок настройки с нуля на новой машине |
|
||||
| **[TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md)** | Telegram SOCKS: быстрые команды → регламент → развёртывание с нуля по сценариям |
|
||||
| **[PROXY_CHANGELOG.md](./PROXY_CHANGELOG.md)** | История изменений (безопасность, скрипты, документация) |
|
||||
| **[BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md)** | Сборка AAB/APK из России: Maven proxy на проде + SSH-туннель |
|
||||
| **[WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md)** | Cursor из РФ на **Windows**: туннель через прокси, скрипт .ps1, автозапуск |
|
||||
| **[WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md)** | Установка SSH-ключа на Windows (один файл `setup-ssh-key-for-proxy.ps1`, только для туннеля) |
|
||||
| **[HOTSPOT_NETWORK_EMERGENCY.md](./HOTSPOT_NETWORK_EMERGENCY.md)** | Сеть после смены хотспота: причина (PAC localhost), ручной сброс без ребута, `macos-hotspot-network-recover.sh` |
|
||||
| **[scripts/](./scripts/)** | Скрипты для туннелей и локальной работы |
|
||||
| **[launchd/](./launchd/)** | LaunchAgent для macOS: автозапуск SOCKS-туннеля при входе |
|
||||
|
||||
@@ -20,8 +23,8 @@
|
||||
## Быстрые ссылки по задачам
|
||||
|
||||
- **Доступ по SSH к прокси** — [PROXY_GUIDE.md → Доступ и файлы](./PROXY_GUIDE.md#-доступ-и-файлы)
|
||||
- **Telegram через SOCKS** — [TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md) (быстрые команды + гайд для новой машины), [launchd/README.md](./launchd/README.md)
|
||||
- **Cursor (PING timeout из РФ)** — [PROXY_GUIDE.md → Cursor IDE](./PROXY_GUIDE.md#cursor-ide-ping-timeout-из-рф), в репо: `docs/cursor/README.md`, скрипт `docs/cursor/scripts/cursor-socks-tunnel.sh`
|
||||
- **Telegram через SOCKS** — [TELEGRAM_GUIDE.md](./TELEGRAM_GUIDE.md) (команды, регламент, сценарии), [launchd/README.md](./launchd/README.md)
|
||||
- **Cursor (PING timeout из РФ)** — [PROXY_GUIDE.md → Cursor IDE](./PROXY_GUIDE.md#cursor-ide-ping-timeout-из-рф), в репо: `docs/cursor/README.md`, скрипт `docs/cursor/scripts/cursor-socks-tunnel.sh` (macOS/Linux), **[Windows: WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md)** и `docs/cursor/scripts/cursor-socks-tunnel.ps1`
|
||||
- **Загрузка файлов на прод через прокси** — [PROXY_GUIDE.md → Загрузка файлов на прод (scp)](./PROXY_GUIDE.md#-загрузка-файлов-на-прод-через-туннель-scp)
|
||||
- **Безопасность прокси (nginx, fail2ban)** — [PROXY_GUIDE.md → Безопасность](./PROXY_GUIDE.md#-безопасность), SECURITY.md (если есть в папке)
|
||||
- **Сборка AAB из РФ** — [BUILD_AAB_FROM_RUSSIA_GUIDE.md](./BUILD_AAB_FROM_RUSSIA_GUIDE.md)
|
||||
@@ -32,7 +35,9 @@
|
||||
|
||||
| Скрипт | Описание |
|
||||
|--------|----------|
|
||||
| **telegram-socks-tunnel.sh** | Поднять/остановить/проверить SOCKS5 на 127.0.0.1:1081 для Telegram (и др.). Хост: ahau@149.154.64.19. Переменные: `TELEGRAM_SOCKS_PORT`, `TELEGRAM_TUNNEL_HOST`. |
|
||||
| **telegram-socks-tunnel.sh** | SOCKS5 на 1081 для Telegram. `TELEGRAM_SOCKS_VIA_DO=1` — только Mac→RU→DO (`telegram-socks-via-do`); без RU — ошибка. См. PROXY_GUIDE. |
|
||||
| **macos-hotspot-network-recover.sh** | После смены хотспота: `status` / `recover` (PAC off, SOCKS off на Wi‑Fi, DNS, DHCP, перезапуск радио). См. HOTSPOT_NETWORK_EMERGENCY.md. |
|
||||
| **ssh-hunab-prod.sh** | Опционально: сначала `hunab-prod-direct`, иначе `hunab-prod` (в конфиге по умолчанию уже через RU). См. PROXY_GUIDE. |
|
||||
|
||||
---
|
||||
|
||||
@@ -40,9 +45,10 @@
|
||||
|
||||
Автозапуск SOCKS-туннеля для Telegram при входе в macOS:
|
||||
|
||||
- **com.hunab.telegram-socks.plist** — LaunchAgent (RunAtLoad, KeepAlive, порт 1081).
|
||||
- **install.sh** — один раз скопировать plist и загрузить; дальше туннель поднимается при каждом входе.
|
||||
- **uninstall.sh** — снять автозапуск.
|
||||
- **com.hunab.telegram-socks.plist** — LaunchAgent (RunAtLoad, KeepAlive, порт 1081, выход на RU-прокси).
|
||||
- **com.hunab.telegram-socks-via-do.plist** — только Mac→RU→DO (`telegram-socks-launch.sh` + `telegram-socks-via-do`).
|
||||
- **install.sh** / **uninstall.sh** — RU-only агент.
|
||||
- **install-via-do.sh** / **uninstall-via-do.sh** — агент с выходом через DigitalOcean.
|
||||
- **[launchd/README.md](./launchd/README.md)** — установка, удаление, требования (ключ в keychain), возможные конфликты портов.
|
||||
|
||||
---
|
||||
@@ -52,4 +58,4 @@
|
||||
| Порт | Назначение |
|
||||
|------|------------|
|
||||
| **1081** | Telegram (и др. приложения) — SOCKS через `scripts/telegram-socks-tunnel.sh` или LaunchAgent |
|
||||
| **10809** | Cursor IDE — SOCKS через `docs/cursor/scripts/cursor-socks-tunnel.sh` |
|
||||
| **10809** | Cursor IDE — SOCKS через `docs/cursor/scripts/cursor-socks-tunnel.sh` / `cursor-socks-tunnel.ps1`; по умолчанию только **Mac→RU→DO** (`CURSOR_SOCKS_VIA_DO=1`, `telegram-socks-via-do`) |
|
||||
|
||||
@@ -1,146 +1,213 @@
|
||||
# Telegram через SOCKS-туннель (ru.hunab.app)
|
||||
# Telegram через SOCKS (RU-прокси → DigitalOcean)
|
||||
|
||||
Трафик Telegram идёт через прокси 149.154.64.19 по SOCKS5 на **127.0.0.1:1081**. Гайд для быстрых команд и для настройки с нуля на новой машине.
|
||||
На Mac поднимается **SOCKS5** на **127.0.0.1:1081**. Для выхода в интернет с **IP DO** используется **только** цепочка **Mac → 149.154.64.19 → DO (209.38.32.21)** (`telegram-socks-via-do`). Без доступа к RU автоматический SOCKS «напрямую в DO» **не** поднимается. Подробности: [PROXY_GUIDE.md](./PROXY_GUIDE.md).
|
||||
|
||||
Все пути ниже — от корня репозитория **local_machine**.
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Быстрые команды
|
||||
## Быстрые команды
|
||||
|
||||
### Туннель вручную (основной: только Mac→RU→DO)
|
||||
|
||||
```bash
|
||||
# Поднять туннель вручную
|
||||
export TELEGRAM_SOCKS_VIA_DO=1
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh start
|
||||
|
||||
# Проверить, слушается ли порт
|
||||
lsof -i :1081
|
||||
|
||||
# Статус туннеля
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh status
|
||||
|
||||
# Остановить туннель
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh stop
|
||||
|
||||
# Включить автозапуск при входе (один раз)
|
||||
./docs/connectivity/launchd/install.sh
|
||||
|
||||
# Отключить автозапуск
|
||||
./docs/connectivity/launchd/uninstall.sh
|
||||
|
||||
# Проверить доступ к Telegram API через прокси
|
||||
curl -x socks5h://127.0.0.1:1081 -sI https://api.telegram.org | head -3
|
||||
```
|
||||
|
||||
**В Telegram:** Настройки → Данные и память → Прокси → добавить **SOCKS5**, **127.0.0.1**, порт **1081**.
|
||||
|
||||
---
|
||||
|
||||
## Полный гайд: новая машина (порядок действий)
|
||||
|
||||
### 1. Требования
|
||||
|
||||
- macOS (для LaunchAgent; на Linux можно использовать systemd или скрипт в автозагрузке).
|
||||
- Доступ по SSH к **149.154.64.19** под пользователем **ahau** (ключ в `~/.ssh/` или алиас в `~/.ssh/config`, например `hsites-ahau`).
|
||||
- Репозиторий **local_machine** склонирован (нужны скрипты из `docs/connectivity/`).
|
||||
|
||||
### 2. Проверка SSH
|
||||
|
||||
Убедись, что вход без пароля работает:
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 ahau@149.154.64.19 "echo OK"
|
||||
```
|
||||
|
||||
Если просит пароль или ключ не подхватывается — настрой `~/.ssh/config` или укажи ключ явно: `ssh -i ~/.ssh/id_ed25519 ahau@149.154.64.19`.
|
||||
|
||||
### 3. Поднять туннель вручную (первый раз)
|
||||
|
||||
Из корня репо:
|
||||
|
||||
```bash
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh start
|
||||
```
|
||||
|
||||
Ожидаемый вывод: `SOCKS tunnel started: 127.0.0.1:1081 -> ahau@149.154.64.19`.
|
||||
|
||||
Проверка порта:
|
||||
|
||||
```bash
|
||||
lsof -i :1081
|
||||
```
|
||||
|
||||
Должен быть процесс `ssh` в состоянии LISTEN.
|
||||
|
||||
### 4. Настройка прокси в Telegram
|
||||
|
||||
1. Открой **Telegram** (Desktop или мобильное приложение).
|
||||
2. **Настройки** (Settings) → **Данные и память** (Data and Storage) → **Прокси** (Proxy).
|
||||
3. **Добавить прокси** (Add Proxy):
|
||||
- Тип: **SOCKS5**
|
||||
- Сервер: **127.0.0.1**
|
||||
- Порт: **1081**
|
||||
- Логин/пароль — не нужны.
|
||||
4. Сохрани и **включи** прокси.
|
||||
|
||||
После этого Telegram должен подключаться через туннель. Если не подключается — см. раздел [Проверка и диагностика](#проверка-и-диагностика) ниже.
|
||||
|
||||
### 5. Автозапуск туннеля при входе в систему (macOS)
|
||||
|
||||
Чтобы не запускать туннель вручную после каждой перезагрузки:
|
||||
|
||||
```bash
|
||||
./docs/connectivity/launchd/install.sh
|
||||
```
|
||||
|
||||
Скрипт копирует plist в `~/Library/LaunchAgents/` и загружает его. Дальше при каждом входе в систему туннель будет подниматься сам (и перезапускаться при обрыве).
|
||||
|
||||
**Важно:** при старте системы SSH должен иметь доступ к ключу. Добавь ключ в связку с keychain (один раз):
|
||||
|
||||
```bash
|
||||
ssh-add --apple-use-keychain
|
||||
# или, если ключ не дефолтный:
|
||||
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
Проверка после перезагрузки:
|
||||
|
||||
```bash
|
||||
lsof -i :1081
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh status
|
||||
```
|
||||
|
||||
### 6. Проверка и диагностика
|
||||
|
||||
**Туннель слушает порт?**
|
||||
### Проверка порта и API Telegram
|
||||
|
||||
```bash
|
||||
lsof -i :1081 -sTCP:LISTEN
|
||||
curl -x socks5h://127.0.0.1:1081 -sI --connect-timeout 10 https://api.telegram.org | head -5
|
||||
```
|
||||
|
||||
**Доступ к API Telegram через SOCKS?**
|
||||
### Автозапуск после перезагрузки (via-DO + авто-fallback)
|
||||
|
||||
Скрипт **`~/Library/Application Support/hunab/telegram-socks-launch.sh`** ставится из репо командой ниже.
|
||||
|
||||
```bash
|
||||
curl -x socks5h://127.0.0.1:1081 -sI https://api.telegram.org | head -5
|
||||
./docs/connectivity/launchd/install-via-do.sh
|
||||
./docs/connectivity/launchd/uninstall-via-do.sh
|
||||
launchctl list | grep telegram-socks
|
||||
launchctl kickstart -k "gui/$(id -u)/com.hunab.telegram-socks-via-do"
|
||||
```
|
||||
|
||||
Успех: в ответе есть HTTP-заголовки (например 302 или 200). Таймаут или отказ — туннель не работает или порт занят.
|
||||
Лог агента: `tail -50 /tmp/telegram-socks-tunnel-via-do.log`
|
||||
|
||||
**Лог LaunchAgent (если туннель при старте не поднимается):**
|
||||
### Ключи в связку (один раз после смены ключей / сбоя SSH)
|
||||
|
||||
```bash
|
||||
tail -50 /tmp/telegram-socks-tunnel.log
|
||||
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
|
||||
ssh-add --apple-use-keychain ~/.ssh/hunab_deploy_key
|
||||
```
|
||||
|
||||
Типичные причины: ключ не в keychain, ошибка «Permission denied», сеть ещё недоступна при RunAtLoad.
|
||||
### Прокси в Telegram (обязательно)
|
||||
|
||||
### 7. Конфликты портов
|
||||
| Поле | Значение |
|
||||
|------|----------|
|
||||
| Тип | **SOCKS5** |
|
||||
| Сервер | **127.0.0.1** |
|
||||
| Порт | **1081** |
|
||||
| Логин / пароль | *пусто* |
|
||||
|
||||
| Порт | Назначение |
|
||||
|-------|------------|
|
||||
| 1081 | Telegram SOCKS (этот гайд) |
|
||||
| 10809 | Cursor IDE SOCKS (другой туннель) |
|
||||
**Настройки → Данные и память → Прокси** — добавить, сохранить, **включить**.
|
||||
|
||||
Если на 1081 уже висит другой прокси (например Shadowsocks), смени порт в plist и в настройках Telegram или отключи автозапуск: `./docs/connectivity/launchd/uninstall.sh`.
|
||||
---
|
||||
|
||||
### 8. Ссылки
|
||||
## Регламент
|
||||
|
||||
- [PROXY_GUIDE.md](./PROXY_GUIDE.md) — общее руководство по прокси ru.hunab.app (SSH, Nginx, Cursor, scp).
|
||||
- [launchd/README.md](./launchd/README.md) — детали LaunchAgent (удаление, ThrottleInterval, логи).
|
||||
Документ задаёт **обязательные** правила и **порядок действий**. Отклонения — только осознанно (обрывы, DPI, конфликт портов).
|
||||
|
||||
### 1. Назначение
|
||||
|
||||
- Выход в интернет через **DO**; предпочтительно через **`hsites-ahau`**, при его недоступности — **прямой** SSH к DO (автоматически).
|
||||
- Исключить работу Telegram **мимо** туннеля и конкуренцию на порту **1081**.
|
||||
|
||||
### 2. Обязательные нормы (всегда)
|
||||
|
||||
| № | Требование |
|
||||
|---|------------|
|
||||
| N1 | **Основной** сценарий: **`TELEGRAM_SOCKS_VIA_DO=1`** и/или LaunchAgent **`com.hunab.telegram-socks-via-do`**, не «только RU без DO», если с RU TLS до Telegram нестабилен. |
|
||||
| N2 | В Telegram — **SOCKS5**, **127.0.0.1**, **1081**, без логина/пароля (см. таблицу в «Быстрые команды»). |
|
||||
| N3 | На **1081** только наш `ssh -D`. Другие прокси (Shadowsocks, v2ray и т.п.) на этот порт **не ставить**. |
|
||||
| N4 | В `~/.ssh/config` есть **`Host telegram-socks-via-do`** (с jump **`hsites-ahau`**) и **`hsites-ahau`** — [PROXY_GUIDE.md](./PROXY_GUIDE.md). |
|
||||
| N5 | Ключи **id_ed25519** и **hunab_deploy_key** доступны SSH после входа: **связка ключей** (`ssh-add --apple-use-keychain`). |
|
||||
| N6 | Для работы после перезагрузки: plist **`~/Library/LaunchAgents/com.hunab.telegram-socks-via-do.plist`** загружен. Ручной `start` без агента перезагрузку **не переживает**. |
|
||||
| N7 | Не держать **два** LaunchAgent на **1081**: `com.hunab.telegram-socks` (только RU) и `com.hunab.telegram-socks-via-do` — **ровно один** вариант. |
|
||||
|
||||
### 3. Первоначальная настройка (один раз)
|
||||
|
||||
Порядок фиксирован (детальнее и варианты — раздел «Развёртывание с нуля»).
|
||||
|
||||
1. Проверка SSH (см. быстрые проверки в сценарии 1).
|
||||
2. `~/.ssh/config`: **`Host telegram-socks-via-do`** по [PROXY_GUIDE.md](./PROXY_GUIDE.md).
|
||||
3. `ssh-add --apple-use-keychain` для обоих ключей.
|
||||
4. Снять старый агент only-RU при необходимости: `./docs/connectivity/launchd/uninstall.sh`.
|
||||
5. `./docs/connectivity/launchd/install-via-do.sh`.
|
||||
6. Настроить прокси в Telegram (таблица выше).
|
||||
7. Контрольные проверки (п. 6).
|
||||
|
||||
### 4. Повседневная эксплуатация
|
||||
|
||||
| Ситуация | Действие |
|
||||
|----------|----------|
|
||||
| Обычный день | Ничего не запускать: туннель поднимает LaunchAgent после входа. |
|
||||
| Ручной перезапуск туннеля | `stop` → `export TELEGRAM_SOCKS_VIA_DO=1` → `start` (см. быстрые команды). |
|
||||
| После смены plist / обновления репо | `install-via-do.sh` или `kickstart` (см. быстрые команды). |
|
||||
|
||||
### 5. Норматив: клиент Telegram
|
||||
|
||||
Соответствует таблице в разделе **«Быстрые команды»**. Иначе нарушение N2.
|
||||
|
||||
### 6. Контрольные проверки
|
||||
|
||||
```bash
|
||||
test -f "$HOME/Library/LaunchAgents/com.hunab.telegram-socks-via-do.plist" && echo "plist OK" || echo "FAIL: нет plist"
|
||||
launchctl list | grep telegram-socks
|
||||
lsof -i :1081 -sTCP:LISTEN
|
||||
curl -x socks5h://127.0.0.1:1081 -sI --connect-timeout 10 https://api.telegram.org | head -5
|
||||
```
|
||||
|
||||
Лог: `/tmp/telegram-socks-tunnel-via-do.log`.
|
||||
|
||||
### 7. Запреты и конфликты
|
||||
|
||||
- Не занимать **1081** сторонним ПО (N3).
|
||||
- Не два LaunchAgent на 1081 (N7).
|
||||
- Не отключать прокси в Telegram и ожидать тот же доступ «через туннель» (N2).
|
||||
|
||||
| Порт | Назначение |
|
||||
|------|------------|
|
||||
| **1081** | Telegram SOCKS |
|
||||
| **10809** | Cursor IDE SOCKS |
|
||||
|
||||
### 8. Действия при сбое
|
||||
|
||||
1. П. 6 (контрольные проверки).
|
||||
2. `tail -80 /tmp/telegram-socks-tunnel-via-do.log`.
|
||||
3. **`Too many open files`**: обновить plist из репозитория, снова `install-via-do.sh`.
|
||||
4. **`channel … open failed`**: часто сеть или следствие п.3; перезапуск туннеля после правок.
|
||||
5. После правок `~/.ssh/config`: `stop` → `TELEGRAM_SOCKS_VIA_DO=1` → `start` или `kickstart`.
|
||||
6. Прокси в Telegram по-прежнему включён (п. 5).
|
||||
7. **`Connection timed out during banner exchange`** к RU: без доступа к **149.154.64.19** цепочка Mac→RU→DO **невозможна** — восстановите доступ к RU или временно используйте только-RU агент (`install.sh`) / другую сеть; автоматического SOCKS «напрямую в DO» нет.
|
||||
|
||||
### 9. Снятие автозапуска и откат
|
||||
|
||||
- Via-DO: `./docs/connectivity/launchd/uninstall-via-do.sh`.
|
||||
- Переход на только-RU: `./docs/connectivity/launchd/install.sh` — см. **сценарий 2** ниже.
|
||||
|
||||
---
|
||||
|
||||
## Развёртывание с нуля (по сценариям)
|
||||
|
||||
Общие требования: **macOS**; репозиторий **local_machine**; SSH без пароля к **209.38.32.21:2222** (hunab, `hunab_deploy_key`) и к **149.154.64.19** (ahau) — для SOCKS через DO нужен **оба** hop (Mac→RU→DO).
|
||||
|
||||
### Сценарий 1: основной — macOS, автозапуск (только Mac→RU→DO)
|
||||
|
||||
Рекомендуемый вариант (регламент N1, N6).
|
||||
|
||||
1. Проверка SSH (для via-DO нужны **оба**):
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 ahau@149.154.64.19 "echo OK"
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=15 -i ~/.ssh/hunab_deploy_key -p 2222 hunab@209.38.32.21 "echo OK"
|
||||
```
|
||||
|
||||
2. Добавить в `~/.ssh/config` блок **`Host telegram-socks-via-do`** — [PROXY_GUIDE.md](./PROXY_GUIDE.md).
|
||||
|
||||
3. `ssh-add --apple-use-keychain` для `id_ed25519` и `hunab_deploy_key`.
|
||||
|
||||
4. Если ранее стоял агент только-RU: `./docs/connectivity/launchd/uninstall.sh`.
|
||||
|
||||
5. `./docs/connectivity/launchd/install-via-do.sh`.
|
||||
|
||||
6. В Telegram — прокси по таблице в **«Быстрые команды»**.
|
||||
|
||||
7. Проверки из п. 6 регламента.
|
||||
|
||||
### Сценарий 2: только RU-прокси (без DO)
|
||||
|
||||
Если прямой выход с **149.154.64.19** до Telegram устойчив или для диагностики. **Не** ставить одновременно с via-DO на 1081 (N7).
|
||||
|
||||
**Ручной туннель:**
|
||||
|
||||
```bash
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh start
|
||||
```
|
||||
|
||||
**Автозапуск:** `./docs/connectivity/launchd/install.sh`
|
||||
**Снятие:** `./docs/connectivity/launchd/uninstall.sh`
|
||||
|
||||
Перед переходом на сценарий 1 — `uninstall.sh`, затем сценарий 1 с шага 5.
|
||||
|
||||
### Сценарий 3: без LaunchAgent, только ручной туннель (via-DO)
|
||||
|
||||
Для отладки; после перезагрузки нужно снова поднимать вручную.
|
||||
|
||||
```bash
|
||||
export TELEGRAM_SOCKS_VIA_DO=1
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh start
|
||||
```
|
||||
|
||||
Убедиться, что `~/.ssh/config` и ключи настроены как в сценарии 1 (шаги 1–3). Прокси в Telegram — как в таблице.
|
||||
|
||||
### Сценарий 4: смена с via-DO на only-RU или обратно
|
||||
|
||||
- **На only-RU:** `./docs/connectivity/launchd/uninstall-via-do.sh`, затем `./docs/connectivity/launchd/install.sh`.
|
||||
- **Обратно на via-DO:** `./docs/connectivity/launchd/uninstall.sh`, затем `./docs/connectivity/launchd/install-via-do.sh`.
|
||||
|
||||
Каждый раз проверять: один plist на 1081, `launchctl list | grep telegram-socks`.
|
||||
|
||||
### Сценарий 5: Linux (без LaunchAgent)
|
||||
|
||||
Репозиторий и скрипт `docs/connectivity/scripts/telegram-socks-tunnel.sh`; автозапуск — **systemd**, **cron @reboot**, или ручной `start` как в сценарии 3. Plist macOS на Linux не используется.
|
||||
|
||||
---
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [PROXY_GUIDE.md](./PROXY_GUIDE.md) — прокси ru.hunab.app, `Host telegram-socks-via-do`, scp.
|
||||
- [launchd/README.md](./launchd/README.md) — LaunchAgent, логи, порты.
|
||||
|
||||
269
docs/connectivity/WINDOWS_CURSOR_TUNNEL.md
Normal file
269
docs/connectivity/WINDOWS_CURSOR_TUNNEL.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Cursor из РФ на Windows — туннель через прокси ru.hunab.app
|
||||
|
||||
Пошаговая инструкция для тех, кто впервые настраивает Cursor за пределами «обычного» интернета. Все действия выполняются на компьютере Windows.
|
||||
|
||||
---
|
||||
|
||||
## Зачем это нужно
|
||||
|
||||
В России Cursor иногда выдаёт ошибку **PING timed out**: связь с серверами Cursor обрывается или не устанавливается. Чтобы обойти это, трафик Cursor пускают через **прокси-сервер** ru.hunab.app (149.154.64.19). С вашего Windows до этого сервера создаётся зашифрованный **туннель** по SSH. Cursor тогда подключается к интернету не напрямую, а через этот туннель — и ошибка пропадает.
|
||||
|
||||
**Что вы сделаете по шагам:**
|
||||
1) проверите/установите программу для туннеля (OpenSSH) и ключ доступа к прокси;
|
||||
2) запустите туннель (одна команда или скрипт);
|
||||
3) настроите Cursor так, чтобы он использовал этот туннель;
|
||||
4) при желании настроите автозапуск туннеля при входе в Windows.
|
||||
|
||||
---
|
||||
|
||||
## Небольшой словарик
|
||||
|
||||
| Термин | По-простому |
|
||||
|--------|-------------|
|
||||
| **Туннель (SOCKS)** | Канал «ваш компьютер → прокси-сервер». Весь трафик Cursor к интернету идёт через этот канал. |
|
||||
| **Порт 10809** | Номер «входа» на вашем же компьютере. Cursor подключается к `127.0.0.1:10809` — то есть к себе же, но к программе туннеля, которая пересылает данные на прокси. |
|
||||
| **PowerShell** | Программа в Windows для ввода текстовых команд. Открыть: поиск в Пуске «PowerShell» или Win+X → «Windows PowerShell». |
|
||||
| **%USERPROFILE%** | Папка вашего пользователя Windows, например `C:\Users\Иван`. В PowerShell то же: `$env:USERPROFILE`. |
|
||||
| **Корень репо** | Папка, в которой лежит проект local_machine (внутри есть папки `docs`, `scripts` и т.д.). |
|
||||
|
||||
---
|
||||
|
||||
## Что нужно до начала
|
||||
|
||||
- **SSH-ключ** уже установлен на Windows и проверен (подключение к ahau@149.154.64.19 без пароля). Если ещё нет — сначала сделайте по инструкции [WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md).
|
||||
- **Cursor** установлен на Windows.
|
||||
- Настроено **подключение к интернету** (туннель будет идти через него до прокси).
|
||||
|
||||
---
|
||||
|
||||
## Содержание
|
||||
|
||||
1. [Шаг 1: Проверить OpenSSH и ключ](#-шаг-1-проверить-openssh-и-ключ)
|
||||
2. [Шаг 2: Запустить туннель](#-шаг-2-запустить-туннель)
|
||||
3. [Шаг 3: Настроить Cursor](#-шаг-3-настроить-cursor)
|
||||
4. [Порядок работы каждый раз](#-порядок-работы-каждый-раз)
|
||||
5. [Автозапуск туннеля при входе в Windows](#-автозапуск-туннеля-при-входе-в-windows)
|
||||
6. [Если что-то не работает](#-если-что-то-не-работает)
|
||||
|
||||
---
|
||||
|
||||
## Шаг 1: Проверить OpenSSH и ключ
|
||||
|
||||
### 1.1 Открыть PowerShell
|
||||
|
||||
- Нажмите клавишу **Win** (или кнопку «Пуск») и начните вводить **PowerShell**.
|
||||
- Выберите **Windows PowerShell** (лучше не «от имени администратора», если не требуется).
|
||||
|
||||
Откроется синее окно с текстом вроде `PS C:\Users\ВашеИмя>`. В нём вы будете вводить команды.
|
||||
|
||||
### 1.2 Проверить, установлен ли OpenSSH
|
||||
|
||||
Введите команду и нажмите Enter:
|
||||
|
||||
```powershell
|
||||
ssh -V
|
||||
```
|
||||
|
||||
**Хороший результат:** появляется строка с версией, например `OpenSSH_for_Windows_8.1` или похоже.
|
||||
|
||||
**Если пишет «не распознано» или «command not found»:**
|
||||
Нужно установить клиент OpenSSH:
|
||||
|
||||
1. Откройте **Параметры Windows** (Win+I).
|
||||
2. Зайдите в **Приложения** → **Дополнительные компоненты** (или «Опциональные компоненты»).
|
||||
3. Нажмите **Добавить компонент**.
|
||||
4. Найдите в списке **OpenSSH-клиент** и нажмите **Установить**.
|
||||
|
||||
После установки закройте и снова откройте PowerShell и снова выполните `ssh -V`.
|
||||
|
||||
### 1.3 Убедиться, что ключ на месте и работает
|
||||
|
||||
Ключ должен лежать в папке вашего пользователя, в подпапке `.ssh`, с именем `id_ed25519`. То есть полный путь:
|
||||
|
||||
`C:\Users\**ВашеИмя**\.ssh\id_ed25519`
|
||||
|
||||
Проверка одной командой (скопируйте целиком и вставьте в PowerShell):
|
||||
|
||||
```powershell
|
||||
ssh -i $env:USERPROFILE\.ssh\id_ed25519 ahau@149.154.64.19 "echo OK"
|
||||
```
|
||||
|
||||
При первом запуске может спросить про доверие хосту — введите **yes**.
|
||||
**Ожидаемый результат:** в следующей строке выводится **OK**, пароль не запрашивается.
|
||||
|
||||
Если получили **OK** — переходите к [Шагу 2](#-шаг-2-запустить-туннель).
|
||||
Если ошибка (например, «Permission denied», «No such file») — вернитесь к [WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md) и доведите установку ключа до конца.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 2: Запустить туннель
|
||||
|
||||
Туннель — это программа, которая держит соединение с прокси и «слушает» на вашем компьютере порт 10809. Cursor потом будет подключаться к этому порту. Окно, в котором запущен туннель, **лучше не закрывать** (можно свернуть).
|
||||
|
||||
### Вариант A: Запуск скриптом (если у вас есть папка с репо local_machine)
|
||||
|
||||
1. Откройте PowerShell.
|
||||
2. Перейдите в **корень репо** (папка, где лежат `docs`, `scripts`). Пример:
|
||||
```powershell
|
||||
cd C:\Users\ВашеИмя\code\local_machine
|
||||
```
|
||||
Подставьте свой путь к папке проекта.
|
||||
3. Разрешить выполнение скриптов (достаточно один раз):
|
||||
```powershell
|
||||
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||
```
|
||||
На вопрос ответьте **Y**.
|
||||
4. Запустите туннель:
|
||||
```powershell
|
||||
.\docs\cursor\scripts\cursor-socks-tunnel.ps1 start
|
||||
```
|
||||
5. **Ожидаемый вывод:** `... -> telegram-socks-via-do (DO exit)` — только цепочка **Mac→RU→DO**; если **hsites-ahau** недоступен, скрипт завершится с ошибкой (прямой Mac→DO не поднимается). Нужен `Host telegram-socks-via-do` в `~/.ssh/config`, см. [PROXY_GUIDE.md](./PROXY_GUIDE.md). Только выход с RU: `$env:CURSOR_SOCKS_VIA_DO="0"; .\docs\cursor\scripts\cursor-socks-tunnel.ps1 start`. Окно можно свернуть.
|
||||
|
||||
Проверить, что туннель работает: в **другом** окне PowerShell выполните:
|
||||
|
||||
```powershell
|
||||
.\docs\cursor\scripts\cursor-socks-tunnel.ps1 status
|
||||
```
|
||||
|
||||
Должно написать, что SOCKS слушает на порту 10809.
|
||||
|
||||
Остановить туннель (когда не нужен):
|
||||
|
||||
```powershell
|
||||
.\docs\cursor\scripts\cursor-socks-tunnel.ps1 stop
|
||||
```
|
||||
|
||||
### Вариант B: Запуск одной командой (без скрипта)
|
||||
|
||||
1. Откройте PowerShell.
|
||||
2. Выход через **DO** (как скрипт по умолчанию):
|
||||
|
||||
```powershell
|
||||
ssh -D 10809 -o ServerAliveInterval=60 -o ServerAliveCountMax=4 telegram-socks-via-do
|
||||
```
|
||||
|
||||
Только **RU** (без DO в цепочке выхода):
|
||||
|
||||
```powershell
|
||||
ssh -D 10809 -o ServerAliveInterval=30 -o ServerAliveCountMax=6 ahau@149.154.64.19
|
||||
```
|
||||
|
||||
3. При первом запуске может спросить про доверие хосту — введите **yes**. Пароль спрашивать не должно (работает ключ).
|
||||
4. **Окно не закрывайте** — пока оно открыто, туннель работает. В заголовке окна может ничего не меняться — это нормально. Можно свернуть окно.
|
||||
|
||||
Проверка: откройте **второе** окно PowerShell и выполните:
|
||||
|
||||
```powershell
|
||||
netstat -an | findstr "10809"
|
||||
```
|
||||
|
||||
Должна появиться строка, где есть `127.0.0.1` и `10809` и состояние **LISTENING**.
|
||||
|
||||
Чтобы остановить туннель — закройте то окно PowerShell, в котором была запущена команда `ssh -D 10809 ...`.
|
||||
|
||||
---
|
||||
|
||||
## Шаг 3: Настроить Cursor
|
||||
|
||||
Нужно один раз сказать Cursor использовать туннель как прокси.
|
||||
|
||||
1. Откройте **Cursor**.
|
||||
2. Откройте настройки:
|
||||
- либо меню **File** → **Preferences** → **Settings** (или **Файл** → **Параметры** → **Настройки**);
|
||||
- либо нажмите **Ctrl+,** (запятая).
|
||||
3. Вверху в поиске настроек введите: **proxy**.
|
||||
4. Найдите пункт **Http: Proxy** (или **Http: Proxy Support** и т.п.) и в поле значения введите **ровно**:
|
||||
```text
|
||||
socks5://127.0.0.1:10809
|
||||
```
|
||||
5. Если есть отдельное поле **Https: Proxy** — введите туда то же самое: `socks5://127.0.0.1:10809`.
|
||||
6. Убедитесь, что **Cursor > General: Disable Http2** выключен (галочка снята). HTTP/2 должен быть включён — так Cursor будет стабильнее работать через туннель.
|
||||
|
||||
**Альтернатива через JSON-настройки:**
|
||||
|
||||
- В Cursor откройте палитру команд: **Ctrl+Shift+P**.
|
||||
- Введите: **Open User Settings (JSON)** и выберите этот пункт.
|
||||
- В открывшийся файл добавьте (или допишите к существующему объекту) строки:
|
||||
|
||||
```json
|
||||
"http.proxy": "socks5://127.0.0.1:10809",
|
||||
"https.proxy": "socks5://127.0.0.1:10809"
|
||||
```
|
||||
|
||||
Сохраните файл. Настройки применятся автоматически.
|
||||
|
||||
---
|
||||
|
||||
## Порядок работы каждый раз
|
||||
|
||||
1. **Сначала** запустите туннель (скриптом или командой `ssh -D 10809 ...`). Дождитесь, что он поднялся (проверка через `status` или `netstat`).
|
||||
2. **Потом** откройте Cursor и работайте как обычно. Cursor будет отправлять трафик через туннель на порт 10809.
|
||||
|
||||
Если туннель не запущен, Cursor может снова показывать PING timeout или не подключаться к серверам.
|
||||
|
||||
---
|
||||
|
||||
## Автозапуск туннеля при входе в Windows
|
||||
|
||||
Чтобы не запускать туннель вручную каждый раз, можно настроить его старт при входе в систему.
|
||||
|
||||
### Способ 1: Планировщик заданий
|
||||
|
||||
1. Нажмите **Win+R**, введите **taskschd.msc** и нажмите Enter. Откроется **Планировщик заданий**.
|
||||
2. Справа нажмите **Создать задачу…** (не «Создать простую задачу»).
|
||||
3. Вкладка **Общие**:
|
||||
- Имя: например **Cursor SOCKS tunnel**.
|
||||
- Отметьте «Выполнять для всех пользователей» при необходимости; «Выполнять с наивысшими правами» можно не ставить.
|
||||
4. Вкладка **Триггеры** → **Создать**:
|
||||
- Начать задачу: **При входе в систему**.
|
||||
- Пользователь: ваш пользователь Windows. ОК.
|
||||
5. Вкладка **Действия** → **Создать**:
|
||||
- Действие: **Запуск программы**.
|
||||
- Программа или сценарий: **powershell.exe**.
|
||||
- Добавить аргументы:
|
||||
- Если используете скрипт из репо (подставьте свой путь к папке local_machine):
|
||||
```text
|
||||
-NoProfile -WindowStyle Hidden -File "C:\Users\ВашеИмя\путь\к\local_machine\docs\cursor\scripts\cursor-socks-tunnel.ps1" start
|
||||
```
|
||||
- Или без скрипта (фон; только Mac→RU→DO, нужен доступ к RU):
|
||||
```text
|
||||
-NoProfile -WindowStyle Minimized -Command "ssh -D 10809 -o ServerAliveInterval=60 -o ServerAliveCountMax=4 telegram-socks-via-do"
|
||||
```
|
||||
Нажмите ОК.
|
||||
6. ОК по задаче. При следующем входе в Windows туннель должен запускаться сам.
|
||||
|
||||
### Способ 2: Ярлык в папке «Автозагрузка»
|
||||
|
||||
1. Нажмите **Win+R**, введите **shell:startup** и нажмите Enter. Откроется папка автозагрузки вашего пользователя.
|
||||
2. В этой папке создайте ярлык (правый клик → Создать → Ярлык).
|
||||
3. В качестве расположения объекта укажите, например (подставьте свой путь к репо):
|
||||
```text
|
||||
powershell.exe -NoProfile -WindowStyle Normal -File "C:\Users\ВашеИмя\code\local_machine\docs\cursor\scripts\cursor-socks-tunnel.ps1" start
|
||||
```
|
||||
4. Имя ярлыка: например **Cursor tunnel**. Готово. При каждом входе в Windows будет запускаться PowerShell и подниматься туннель.
|
||||
|
||||
---
|
||||
|
||||
## Если что-то не работает
|
||||
|
||||
| Ситуация | Что проверить и сделать |
|
||||
|----------|-------------------------|
|
||||
| **Cursor не подключается, ошибки в чате** | 1) Туннель действительно запущен: в PowerShell `.\docs\cursor\scripts\cursor-socks-tunnel.ps1 status` или `netstat -an \| findstr 10809`. 2) В настройках Cursor указано ровно `socks5://127.0.0.1:10809`. 3) Перезапустите Cursor (полностью закрыть и открыть снова). |
|
||||
| **Всё ещё PING timed out** | Убедитесь, что в настройках Cursor **Disable Http2** выключен (HTTP/2 включён). Перезапустите туннель и Cursor. |
|
||||
| **Порт 10809 занят** | Другой программой уже занят. Можно сменить порт: например 10810. Тогда в команде туннеля замените 10809 на 10810 и в Cursor укажите `socks5://127.0.0.1:10810`. |
|
||||
| **Туннель обрывается через время** | В команде SSH должны быть параметры `ServerAliveInterval=30` и `ServerAliveCountMax=6` — в скрипте они уже есть; при ручном запуске используйте команду из Варианта B. |
|
||||
| **Ошибка при запуске скрипта .ps1** | Выполните один раз: `Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned` и ответьте Y. |
|
||||
| **SSH просит пароль** | Ключ не подхватывается. Проверьте, что файл лежит по пути `C:\Users\ВашеИмя\.ssh\id_ed25519` и в команде указан `-i $env:USERPROFILE\.ssh\id_ed25519`. При необходимости заново установите ключ по [WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md). |
|
||||
|
||||
Логи Cursor на Windows лежат в папке:
|
||||
`%APPDATA%\Cursor\logs\` (файл `main.log`). В проблемных местах ищите слова ERROR, PING, timeout, ECONNREFUSED, ETIMEDOUT.
|
||||
|
||||
---
|
||||
|
||||
## Ссылки
|
||||
|
||||
| Документ | Для чего |
|
||||
|----------|----------|
|
||||
| [WINDOWS_SSH_KEY_INSTALL.md](./WINDOWS_SSH_KEY_INSTALL.md) | Установка SSH-ключа на Windows (если ещё не сделано). |
|
||||
| [PROXY_GUIDE.md](./PROXY_GUIDE.md) | Общее описание прокси ru.hunab.app. |
|
||||
| [README.md](./README.md) | Содержимое папки connectivity. |
|
||||
166
docs/connectivity/WINDOWS_SSH_KEY_INSTALL.md
Normal file
166
docs/connectivity/WINDOWS_SSH_KEY_INSTALL.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Установка SSH-ключа на Windows (только для туннеля через прокси)
|
||||
|
||||
Инструкция для тех, кто впервые настраивает такой доступ. Все шаги расписаны по порядку.
|
||||
|
||||
---
|
||||
|
||||
## Зачем это нужно
|
||||
|
||||
Чтобы Cursor (или Telegram) на Windows могли работать через прокси-сервер **ru.hunab.app**, нужен **SSH-ключ**. Это пара файлов: «приватный» (секретный, хранится только у вас) и «публичный» (его один раз добавляют на сервер). После этого Windows сможет подключаться к серверу **без ввода пароля**, только по ключу. Ключ используется **только для туннеля** — никаких прав на сервере пользователю не нужно.
|
||||
|
||||
**Если с Windows нет доступа к интернет-серверу** (например, вы за корпоративным файрволом), ключ создают на другой машине (Mac), публичный ключ ставят на сервер, а **приватный ключ** переносят на Windows. Дальше в инструкции два варианта: A (ключ создан на Mac) и B (ключ создают на самой Windows).
|
||||
|
||||
---
|
||||
|
||||
## Небольшой словарик
|
||||
|
||||
| Слово | По-простому |
|
||||
|-------|-------------|
|
||||
| **SSH** | Способ безопасно подключаться к удалённому компьютеру по сети. |
|
||||
| **SSH-ключ** | Пара файлов (приватный + публичный). Вместо пароля сервер проверяет, что у вас есть «ваша половина» ключа. |
|
||||
| **Приватный ключ** | Файл `id_ed25519`. Храните только у себя, никому не отдавайте. |
|
||||
| **Публичный ключ** | Файл `id_ed25519.pub`. Его один раз добавляют на сервер в список разрешённых ключей. |
|
||||
| **%USERPROFILE%** | Папка вашего пользователя Windows, например `C:\Users\Иван`. В PowerShell то же самое записывается как `$env:USERPROFILE`. |
|
||||
| **PowerShell** | Программа в Windows для ввода текстовых команд. Открывается через поиск по названию «PowerShell» или через меню Win+X → «Windows PowerShell». |
|
||||
|
||||
---
|
||||
|
||||
## Вариант A: Ключ создан на Mac (с Windows до сервера доступа нет)
|
||||
|
||||
Этот вариант для ситуации, когда с Windows-компьютера вы **не** можете зайти на сервер 149.154.64.19 (нет доступа в интернет к нему или он закрыт). Ключ создаётся на Mac (или другой машине, с которой сервер доступен), затем **один файл** переносится на Windows.
|
||||
|
||||
### Часть 1. На Mac (или другой машине с доступом к серверу)
|
||||
|
||||
1. Откройте **Терминал** на Mac.
|
||||
2. Перейдите в папку с репозиторием **local_machine** (корень репо — та папка, где лежат папки `docs`, `scripts` и т.д.):
|
||||
```bash
|
||||
cd /путь/к/local_machine
|
||||
```
|
||||
Пример: `cd /Users/eternal/code/local_machine`
|
||||
3. Запустите скрипт:
|
||||
```bash
|
||||
./docs/connectivity/scripts/create-windows-proxy-key.sh
|
||||
```
|
||||
4. Скрипт создаст ключ и сам добавит публичный ключ на сервер. В конце он выведет текст вроде:
|
||||
- «Скопировать на Windows один файл: …/windows-proxy-key/id_ed25519»
|
||||
- «На Windows положить в: %USERPROFILE%\.ssh\id_ed25519»
|
||||
|
||||
Запомните или сохраните этот путь к файлу на Mac. Публичный ключ на сервер уже добавлен, больше на Mac ничего делать не нужно.
|
||||
|
||||
### Часть 2. На Windows — куда положить ключ
|
||||
|
||||
**Куда копировать:** в папку вашего пользователя, в подпапку `.ssh`. Полный путь выглядит так:
|
||||
|
||||
- `C:\Users\**ИмяПользователя**\.ssh\id_ed25519`
|
||||
|
||||
**ИмяПользователя** — это то имя, под которым вы входите в Windows (та же папка, что открывается, когда вы в проводнике заходите в «Этот компьютер» → «Локальный диск (C:)» → «Users»).
|
||||
|
||||
**Как сделать по шагам:**
|
||||
|
||||
1. **Создать папку `.ssh`**, если её ещё нет:
|
||||
- Нажмите **Win+R** (клавиша с логотипом Windows и буква R).
|
||||
- Введите: `%USERPROFILE%` и нажмите Enter. Откроется ваша папка пользователя (например, `C:\Users\Иван`).
|
||||
- В этой папке создайте новую папку с именем **`.ssh`** (точка в начале обязательна). Если Windows спросит про объединение имени — оставьте именно `.ssh`.
|
||||
|
||||
2. **Скопировать файл с Mac на Windows** (флешка, облако, мессенджер — как удобно). Файл должен называться **`id_ed25519`** (без расширения .txt и без .pub).
|
||||
|
||||
3. **Положить файл в папку `.ssh`:**
|
||||
- Перетащите `id_ed25519` в папку `C:\Users\**ВашеИмя**\.ssh\`
|
||||
- Итог: файл должен лежать по пути `C:\Users\**ВашеИмя**\.ssh\id_ed25519`.
|
||||
|
||||
### Часть 3. Проверка на Windows
|
||||
|
||||
1. Откройте **PowerShell** (поиск в меню Пуск: «PowerShell» → «Windows PowerShell»).
|
||||
2. Введите команду (можно скопировать целиком; путь подставится сам):
|
||||
```powershell
|
||||
ssh -i $env:USERPROFILE\.ssh\id_ed25519 ahau@149.154.64.19 "echo OK"
|
||||
```
|
||||
3. При первом запуске может появиться вопрос «Trust this host?» — введите **yes** и нажмите Enter.
|
||||
4. **Ожидаемый результат:** в следующей строке выводится только слово **OK**. Пароль спрашивать не должно.
|
||||
|
||||
Если вывело **OK** — ключ установлен правильно. Дальше: настройка туннеля и Cursor по инструкции [WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md).
|
||||
|
||||
Если появилась ошибка (например, «Permission denied» или «Could not resolve hostname»), проверьте:
|
||||
- файл лежит именно в `C:\Users\**ВашеИмя**\.ssh\id_ed25519`;
|
||||
- файл скопирован полностью, не переименован в `id_ed25519.txt`;
|
||||
- с Mac скрипт действительно выполнился до конца и написал, что публичный ключ добавлен на сервер.
|
||||
|
||||
---
|
||||
|
||||
## Вариант B: Ключ создаётся на Windows (с Windows есть доступ к серверу)
|
||||
|
||||
Если с вашего Windows-компьютера **есть** доступ в интернет до 149.154.64.19, ключ можно создать прямо на Windows и один раз добавить публичный ключ на сервер (с этой же Windows или с Mac).
|
||||
|
||||
### Что понадобится
|
||||
|
||||
- Файл **`setup-ssh-key-for-proxy.ps1`** из репо: `docs/connectivity/scripts/setup-ssh-key-for-proxy.ps1`. Скопируйте его на Windows в любую папку (например, «Загрузки» или папку с репо).
|
||||
- В Windows должен быть установлен **OpenSSH-клиент**. Проверка: откройте PowerShell и введите `ssh -V`. Должна появиться строка с версией (например, OpenSSH_for_Windows_8.x). Если команда не найдена — установите: **Параметры Windows** → **Приложения** → **Дополнительные компоненты** → найдите **OpenSSH-клиент** → Установить.
|
||||
|
||||
### Шаг 1. Разрешить выполнение скриптов (сделать один раз)
|
||||
|
||||
1. Откройте **PowerShell** (не cmd). Лучше «Запуск от имени текущего пользователя».
|
||||
2. Введите команду:
|
||||
```powershell
|
||||
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
||||
```
|
||||
3. На вопрос о смене политики ответьте **Y** (или **A**) и нажмите Enter.
|
||||
|
||||
### Шаг 2. Запустить скрипт установки ключа
|
||||
|
||||
1. В PowerShell перейдите в папку, куда положили `setup-ssh-key-for-proxy.ps1`. Пример для папки «Загрузки»:
|
||||
```powershell
|
||||
cd $env:USERPROFILE\Downloads
|
||||
```
|
||||
Если файл в другой папке, подставьте свой путь вместо `Downloads`.
|
||||
2. Запустите скрипт:
|
||||
```powershell
|
||||
.\setup-ssh-key-for-proxy.ps1
|
||||
```
|
||||
3. **Если ключа ещё нет:** скрипт спросит, создать ли новый ключ. Введите **Y** и Enter. Он создаст файлы в папке `%USERPROFILE%\.ssh\` и выведет **одну длинную строку** (публичный ключ). Эту строку нужно один раз добавить на сервер (см. ниже).
|
||||
4. **Если ключ уже лежит в `%USERPROFILE%\.ssh\id_ed25519`:** скрипт просто проверит подключение и напишет что-то вроде `[OK] SSH works`.
|
||||
|
||||
### Шаг 3. Добавить публичный ключ на сервер (только если ключ создали на Windows)
|
||||
|
||||
Если вы только что сгенерировали ключ на Windows, скрипт вывел строку вида:
|
||||
|
||||
`ssh-ed25519 AAAAC3... много символов ... windows-proxy-tunnel`
|
||||
|
||||
Эту строку **целиком** нужно один раз добавить на сервер 149.154.64.19 в файл `~/.ssh/authorized_keys` пользователя **ahau**. Сделать это может кто-то с доступом к серверу (например, с Mac):
|
||||
|
||||
```bash
|
||||
# Выполнить на сервере 149.154.64.19 под пользователем ahau (или с Mac: ssh ahau@149.154.64.19)
|
||||
echo "ВСТАВЬТЕ_СЮДА_ЦЕЛИКОМ_СТРОКУ_ИЗ_СКРИПТА" >> ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
После этого на Windows снова запустите `.\setup-ssh-key-for-proxy.ps1` — должно появиться `[OK] SSH works`.
|
||||
|
||||
### Шаг 4. Проверить подключение вручную
|
||||
|
||||
В PowerShell:
|
||||
|
||||
```powershell
|
||||
ssh -i $env:USERPROFILE\.ssh\id_ed25519 ahau@149.154.64.19 "echo OK"
|
||||
```
|
||||
|
||||
Должно вывести **OK** без запроса пароля.
|
||||
|
||||
---
|
||||
|
||||
## Дальше
|
||||
|
||||
- Настройка туннеля и Cursor: [WINDOWS_CURSOR_TUNNEL.md](./WINDOWS_CURSOR_TUNNEL.md).
|
||||
- Приватный ключ (`id_ed25519`) никому не передавайте и не выкладывайте никуда.
|
||||
|
||||
---
|
||||
|
||||
## Кратко: какие файлы для чего
|
||||
|
||||
| Файл | Где запускать | Что делает |
|
||||
|------|----------------|------------|
|
||||
| **create-windows-proxy-key.sh** | На **Mac** (в папке репо) | Создаёт ключ, добавляет публичный ключ на сервер. Приватный ключ остаётся в папке `windows-proxy-key/` — его нужно скопировать на Windows. |
|
||||
| **setup-ssh-key-for-proxy.ps1** | На **Windows** | Проверяет или создаёт ключ в `%USERPROFILE%\.ssh\`, проверяет подключение к серверу, при необходимости дописывает в конфиг SSH хост `hsites-ahau`. |
|
||||
|
||||
Пути в репозитории:
|
||||
`docs/connectivity/scripts/create-windows-proxy-key.sh`
|
||||
`docs/connectivity/scripts/setup-ssh-key-for-proxy.ps1`
|
||||
@@ -1,17 +1,30 @@
|
||||
# LaunchAgent: SOCKS-туннель для Telegram при входе в систему
|
||||
|
||||
Поднимает `ssh -D 1081 ahau@149.154.64.19` при логине и перезапускает при обрыве.
|
||||
**Основной сценарий:** выход в интернет с **IP DO** только по цепочке **Mac→RU→DO** — `install-via-do.sh` ставит **`telegram-socks-launch.sh`**, который всегда запускает **`telegram-socks-via-do`**. Прямой Mac→DO для SOCKS не используется. Нужны **`Host telegram-socks-via-do`** (с `ProxyJump hsites-ahau`) и доступ к RU. См. [TELEGRAM_GUIDE.md](../TELEGRAM_GUIDE.md).
|
||||
|
||||
**Альтернатива:** только RU прокси — `install.sh` (`ssh -D 1081 ahau@149.154.64.19`).
|
||||
|
||||
- **Порт:** 1081 (отдельно от Cursor 10809, конфликтов нет)
|
||||
- **Сервер:** 149.154.64.19 (ru.hunab.app), пользователь ahau
|
||||
|
||||
## Установка (один раз — дальше работает при каждом входе)
|
||||
## Установка (один раз — дальше работает при каждом входе и после перезагрузки)
|
||||
|
||||
**Через DO (основной):** ключи **ahau** + **`hunab_deploy_key`**, Host **`telegram-socks-via-do`** — [PROXY_GUIDE.md](../PROXY_GUIDE.md).
|
||||
|
||||
```bash
|
||||
# из корня репо local_machine
|
||||
./connectivity/launchd/install.sh
|
||||
./docs/connectivity/launchd/install-via-do.sh
|
||||
```
|
||||
|
||||
Лог: `/tmp/telegram-socks-tunnel-via-do.log`. Снять: `./docs/connectivity/launchd/uninstall-via-do.sh`.
|
||||
|
||||
### Только RU (без DO)
|
||||
|
||||
```bash
|
||||
./docs/connectivity/launchd/install.sh
|
||||
```
|
||||
|
||||
Не держите оба plist на **1081** одновременно — перед `install-via-do` скрипт выгружает старый `com.hunab.telegram-socks` при наличии.
|
||||
|
||||
Скрипт копирует plist в `~/Library/LaunchAgents/` и выполняет `launchctl load`. После этого туннель будет автоматически подниматься при каждом старте/входе в систему.
|
||||
|
||||
Проверка: туннель уже должен быть поднят (RunAtLoad), порт слушает:
|
||||
@@ -19,26 +32,29 @@
|
||||
```bash
|
||||
lsof -i :1081
|
||||
# или
|
||||
./connectivity/scripts/telegram-socks-tunnel.sh status
|
||||
./docs/connectivity/scripts/telegram-socks-tunnel.sh status
|
||||
```
|
||||
|
||||
## Удаление
|
||||
|
||||
```bash
|
||||
./connectivity/launchd/uninstall.sh
|
||||
./docs/connectivity/launchd/uninstall-via-do.sh # сценарий через DO
|
||||
./docs/connectivity/launchd/uninstall.sh # сценарий только RU
|
||||
```
|
||||
|
||||
Файлы в репозитории: **telegram-socks-launch.sh** (только `telegram-socks-via-do`), **com.hunab.telegram-socks-via-do.plist**. Копия скрипта: **`~/Library/Application Support/hunab/telegram-socks-launch.sh`**.
|
||||
|
||||
## Поведение
|
||||
|
||||
- **RunAtLoad** — запуск при входе в систему
|
||||
- **KeepAlive** — при обрыве SSH launchd перезапускает туннель
|
||||
- **ThrottleInterval 30** — не чаще одного перезапуска в 30 секунд при постоянных падениях
|
||||
- Лог: `/tmp/telegram-socks-tunnel.log` (при желании замените путь в plist после копирования)
|
||||
- Логи: `/tmp/telegram-socks-tunnel-via-do.log` (via-DO), `/tmp/telegram-socks-tunnel.log` (только RU)
|
||||
|
||||
## Требования
|
||||
|
||||
- SSH-ключ для `ahau@149.154.64.19` должен быть доступен при логине (в клюchain или без пароля), иначе туннель не поднимется. Проверка вручную: `ssh -D 1081 -N ahau@149.154.64.19`.
|
||||
- Если используете алиас из `~/.ssh/config` (например `hsites-ahau`), в plist можно заменить `ahau@149.154.64.19` на `hsites-ahau`.
|
||||
- **via-DO:** Host `telegram-socks-via-do` (+ jump `hsites-ahau`); ключи **ahau** и **hunab** в keychain. Проверка: `ssh -N -D 1082 telegram-socks-via-do`.
|
||||
- **Только RU:** SSH-ключ для `ahau@149.154.64.19` при логине; проверка: `ssh -D 1081 -N hsites-ahau`.
|
||||
|
||||
## Конфликты портов
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!-- Скрипт: ~/Library/Application Support/hunab/telegram-socks-launch.sh. Только Mac→RU→DO (telegram-socks-via-do). -->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.hunab.telegram-socks-via-do</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>-c</string>
|
||||
<string>exec /bin/bash "$HOME/Library/Application Support/hunab/telegram-socks-launch.sh"</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>ThrottleInterval</key>
|
||||
<integer>15</integer>
|
||||
<key>SoftResourceLimits</key>
|
||||
<dict>
|
||||
<key>NumberOfFiles</key>
|
||||
<integer>65536</integer>
|
||||
</dict>
|
||||
<key>HardResourceLimits</key>
|
||||
<dict>
|
||||
<key>NumberOfFiles</key>
|
||||
<integer>65536</integer>
|
||||
</dict>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/telegram-socks-tunnel-via-do.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/telegram-socks-tunnel-via-do.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
29
docs/connectivity/launchd/install-via-do.sh
Executable file
29
docs/connectivity/launchd/install-via-do.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# LaunchAgent: SOCKS на 1081 с выходом через DigitalOcean (прокси → DO), см. PROXY_GUIDE.md.
|
||||
# Требуется блок Host telegram-socks-via-do в ~/.ssh/config.
|
||||
set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PLIST_NAME="com.hunab.telegram-socks-via-do.plist"
|
||||
DST="$HOME/Library/LaunchAgents/$PLIST_NAME"
|
||||
|
||||
mkdir -p "$HOME/Library/LaunchAgents"
|
||||
HUNAB_SUPPORT="$HOME/Library/Application Support/hunab"
|
||||
mkdir -p "$HUNAB_SUPPORT"
|
||||
cp "$SCRIPT_DIR/telegram-socks-launch.sh" "$HUNAB_SUPPORT/telegram-socks-launch.sh"
|
||||
chmod +x "$HUNAB_SUPPORT/telegram-socks-launch.sh"
|
||||
# Не использовать имя UID — в bash это readonly (присваивание ломает скрипт).
|
||||
GUI_UID=$(id -u)
|
||||
launchctl unload "$HOME/Library/LaunchAgents/com.hunab.telegram-socks.plist" 2>/dev/null || true
|
||||
launchctl bootout "gui/$GUI_UID/com.hunab.telegram-socks-via-do" 2>/dev/null || true
|
||||
launchctl unload "$DST" 2>/dev/null || true
|
||||
cp "$SCRIPT_DIR/$PLIST_NAME" "$DST"
|
||||
if launchctl bootstrap "gui/$GUI_UID" "$DST" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
launchctl load "$DST" || true
|
||||
fi
|
||||
echo "Installed: $DST (loaded). Survives reboot while this plist exists."
|
||||
echo "Launch helper: $HUNAB_SUPPORT/telegram-socks-launch.sh (only Mac→RU→DO via telegram-socks-via-do)."
|
||||
echo "Ensure ~/.ssh/config defines Host telegram-socks-via-do (+ ProxyJump hsites-ahau) — PROXY_GUIDE.md / TELEGRAM_GUIDE.md."
|
||||
echo "Verify autostart: launchctl list | grep telegram-socks"
|
||||
lsof -i :1081 -sTCP:LISTEN 2>/dev/null && echo "Port 1081 is listening." || echo "Check in a few seconds: lsof -i :1081"
|
||||
13
docs/connectivity/launchd/telegram-socks-launch.sh
Executable file
13
docs/connectivity/launchd/telegram-socks-launch.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# LaunchAgent: SOCKS на 1081 только по цепочке Mac → RU → DO (Host telegram-socks-via-do).
|
||||
# Прямой Mac → DO для SOCKS не используется.
|
||||
ulimit -n 65536 2>/dev/null || ulimit -n 10240 2>/dev/null || true
|
||||
|
||||
SSH_CMD=(/usr/bin/ssh -D 1081 -N \
|
||||
-o TCPKeepAlive=yes \
|
||||
-o IPQoS=throughput \
|
||||
-o ServerAliveInterval=60 \
|
||||
-o ServerAliveCountMax=4 \
|
||||
-o ConnectTimeout=30)
|
||||
|
||||
exec "${SSH_CMD[@]}" telegram-socks-via-do
|
||||
12
docs/connectivity/launchd/uninstall-via-do.sh
Executable file
12
docs/connectivity/launchd/uninstall-via-do.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Снимает LaunchAgent com.hunab.telegram-socks-via-do.
|
||||
set -e
|
||||
PLIST_NAME="com.hunab.telegram-socks-via-do.plist"
|
||||
DST="$HOME/Library/LaunchAgents/$PLIST_NAME"
|
||||
|
||||
GUI_UID=$(id -u)
|
||||
launchctl bootout "gui/$GUI_UID/com.hunab.telegram-socks-via-do" 2>/dev/null || true
|
||||
launchctl unload "$DST" 2>/dev/null || true
|
||||
rm -f "$DST"
|
||||
rm -f "$HOME/Library/Application Support/hunab/telegram-socks-launch.sh"
|
||||
echo "Uninstalled: $DST (and launch helper if present)"
|
||||
27
docs/connectivity/matrix/Caddyfile.example
Normal file
27
docs/connectivity/matrix/Caddyfile.example
Normal file
@@ -0,0 +1,27 @@
|
||||
# Пример для Caddy 2: TLS автоматически (Let's Encrypt).
|
||||
# Замените matrix.example.com и example.com на свои домены.
|
||||
#
|
||||
# Запуск Caddy на том же хосте, что и docker-compose (Synapse на 127.0.0.1:8008/8448).
|
||||
|
||||
matrix.example.com {
|
||||
reverse_proxy /_matrix/* 127.0.0.1:8008
|
||||
reverse_proxy /_synapse/* 127.0.0.1:8008
|
||||
}
|
||||
|
||||
# Федерация Matrix: S2S часто идёт на :8448. Проброс на тот же процесс Synapse.
|
||||
matrix.example.com:8448 {
|
||||
reverse_proxy 127.0.0.1:8448
|
||||
}
|
||||
|
||||
# Клиенты ищут .well-known на apex-домене (если server_name = example.com).
|
||||
example.com {
|
||||
encode gzip
|
||||
handle /.well-known/matrix/server {
|
||||
header Content-Type application/json
|
||||
respond `{"m.server": "matrix.example.com:443"}`
|
||||
}
|
||||
handle /.well-known/matrix/client {
|
||||
header Content-Type application/json
|
||||
respond `{"m.homeserver": {"base_url": "https://matrix.example.com"}}`
|
||||
}
|
||||
}
|
||||
45
docs/connectivity/matrix/docker-compose.yml
Normal file
45
docs/connectivity/matrix/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# Synapse + PostgreSQL на одном хосте (DigitalOcean и т.п.).
|
||||
# Перед первым запуском: см. MATRIX_SYNAPSE_DO.md — generate и homeserver.yaml.
|
||||
#
|
||||
# Использование на сервере:
|
||||
# export MATRIX_DOMAIN=matrix.example.com # хост, где слушает Synapse за прокси
|
||||
# export SYNAPSE_SERVER_NAME=example.com # публичное имя сервера (часто apex)
|
||||
# docker compose up -d
|
||||
#
|
||||
# Порты 8008/8448 не публикуем наружу, если перед Synapse стоит Caddy/Nginx на 443.
|
||||
|
||||
services:
|
||||
db:
|
||||
image: docker.io/postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: synapse
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
|
||||
POSTGRES_DB: synapse
|
||||
POSTGRES_INITDB_ARGS: --encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
||||
volumes:
|
||||
- synapse-db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U synapse"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
synapse:
|
||||
image: docker.io/matrixdotorg/synapse:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- synapse-data:/data
|
||||
environment:
|
||||
SYNAPSE_NO_TLS: "1"
|
||||
ports:
|
||||
- "127.0.0.1:8008:8008"
|
||||
- "127.0.0.1:8448:8448"
|
||||
command: run
|
||||
|
||||
volumes:
|
||||
synapse-data:
|
||||
synapse-db:
|
||||
2
docs/connectivity/matrix/env.example
Normal file
2
docs/connectivity/matrix/env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
# Скопируйте в .env рядом с docker-compose.yml и заполните.
|
||||
POSTGRES_PASSWORD=сгенерируйте_длинный_случайный_пароль
|
||||
51
docs/connectivity/scripts/create-windows-proxy-key.sh
Executable file
51
docs/connectivity/scripts/create-windows-proxy-key.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# Создать SSH-ключ для туннеля на Windows и добавить публичный ключ на прокси.
|
||||
# Запускать на машине, с которой ЕСТЬ доступ к 149.154.64.19 (например Mac).
|
||||
# Приватный ключ остаётся в windows-proxy-key/ — скопировать на Windows в %USERPROFILE%\.ssh\id_ed25519
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KEY_DIR="${SCRIPT_DIR}/windows-proxy-key"
|
||||
KEY_FILE="${KEY_DIR}/id_ed25519"
|
||||
PROXY_HOST="${CURSOR_TUNNEL_HOST:-hsites-ahau}"
|
||||
|
||||
echo "=== Ключ для туннеля на Windows (только туннелирование, прав на сервере не нужно) ==="
|
||||
echo " Прокси: $PROXY_HOST (149.154.64.19)"
|
||||
echo ""
|
||||
|
||||
mkdir -p "$KEY_DIR"
|
||||
chmod 700 "$KEY_DIR"
|
||||
|
||||
if [[ -f "$KEY_FILE" ]]; then
|
||||
echo "Ключ уже есть: $KEY_FILE"
|
||||
echo "Публичный ключ на сервере уже добавлен (если скрипт выполнялся до конца)."
|
||||
echo ""
|
||||
echo "Скопируйте на Windows один файл (приватный ключ):"
|
||||
echo " Источник: $KEY_FILE"
|
||||
echo " На Windows: %USERPROFILE%\\.ssh\\id_ed25519"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Создаю ключ в $KEY_DIR ..."
|
||||
ssh-keygen -t ed25519 -f "$KEY_FILE" -N "" -C "windows-proxy-tunnel"
|
||||
|
||||
echo ""
|
||||
echo "Добавляю публичный ключ на сервер $PROXY_HOST ..."
|
||||
cat "${KEY_FILE}.pub" | ssh "$PROXY_HOST" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
echo "[OK] Публичный ключ добавлен в ~/.ssh/authorized_keys на сервере."
|
||||
|
||||
echo ""
|
||||
echo "Проверка подключения с этим ключом..."
|
||||
ssh -i "$KEY_FILE" -o BatchMode=yes -o ConnectTimeout=10 "$PROXY_HOST" "echo OK" || { echo "Проверка не удалась."; exit 1; }
|
||||
echo "[OK] Подключение работает."
|
||||
echo ""
|
||||
echo "--- Что сделать дальше ---"
|
||||
echo "1. Скопировать на Windows ОДИН файл (приватный ключ, никому не передавать):"
|
||||
echo " $KEY_FILE"
|
||||
echo " На Windows положить в: %USERPROFILE%\\.ssh\\id_ed25519"
|
||||
echo " (папку .ssh создать, если нет.)"
|
||||
echo ""
|
||||
echo "2. На Windows запустить туннель: см. docs/connectivity/WINDOWS_CURSOR_TUNNEL.md"
|
||||
echo " или: ssh -D 10809 -o ServerAliveInterval=30 -o ServerAliveCountMax=6 ahau@149.154.64.19"
|
||||
echo ""
|
||||
105
docs/connectivity/scripts/macos-hotspot-network-recover.sh
Executable file
105
docs/connectivity/scripts/macos-hotspot-network-recover.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
# Сброс сетевого стека macOS после смены хотспота / «интернет не поднимается без перезагрузки».
|
||||
# Типичная причина в связке с docs/connectivity: системный PAC на localhost:1089 при мёртвом/ещё не
|
||||
# поднятом PAC-сервере + кэш DNS / Wi‑Fi. См. HOTSPOT_NETWORK_EMERGENCY.md
|
||||
#
|
||||
# Использование (из корня репозитория):
|
||||
# ./docs/connectivity/scripts/macos-hotspot-network-recover.sh status
|
||||
# ./docs/connectivity/scripts/macos-hotspot-network-recover.sh recover
|
||||
#
|
||||
# Переменные:
|
||||
# MACOS_PAC_SERVICES — как в macos-system-pac.sh (по умолчанию Wi-Fi|Thunderbolt Bridge 2)
|
||||
# WIFI_DEVICE — например en0; иначе авто: первый Device у Hardware Port: Wi-Fi
|
||||
|
||||
set -e
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
PAC_SCRIPT="$ROOT/macos-system-pac.sh"
|
||||
|
||||
wifi_device() {
|
||||
if [ -n "${WIFI_DEVICE:-}" ]; then
|
||||
printf '%s\n' "$WIFI_DEVICE"
|
||||
return
|
||||
fi
|
||||
# "Device: en0" под блоком "Hardware Port: Wi-Fi"
|
||||
networksetup -listallhardwareports 2>/dev/null \
|
||||
| awk '/Hardware Port: Wi-Fi$/ {p=1; next} p && /^Device: / {print $2; exit}'
|
||||
}
|
||||
|
||||
usage() {
|
||||
printf '%s\n' "Usage: $(basename "$0") status|recover" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$#" -eq 1 ] || usage
|
||||
cmd=$1
|
||||
|
||||
case "$cmd" in
|
||||
status)
|
||||
echo "=== Wi-Fi device (hardware) ==="
|
||||
wifi_device || true
|
||||
echo "=== networksetup -listallnetworkservices (excerpt) ==="
|
||||
networksetup -listallnetworkservices 2>/dev/null | head -15 || true
|
||||
echo "=== PAC / autoproxy (Wi-Fi + Thunderbolt Bridge 2 if present) ==="
|
||||
if [ -x "$PAC_SCRIPT" ]; then
|
||||
MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" status || true
|
||||
fi
|
||||
echo "=== SOCKS firewall (Wi-Fi) ==="
|
||||
networksetup -getsocksfirewallproxy "Wi-Fi" 2>/dev/null || true
|
||||
echo "=== Web proxy (Wi-Fi) ==="
|
||||
networksetup -getwebproxy "Wi-Fi" 2>/dev/null || true
|
||||
echo "=== Listener on 1089 (PAC URL default) ==="
|
||||
if lsof -i :1089 -sTCP:LISTEN 2>/dev/null | head -5; then
|
||||
:
|
||||
else
|
||||
echo "(none — при Enabled PAC в Safari/системе возможны сбои)"
|
||||
fi
|
||||
echo "=== Default route ==="
|
||||
route -n get default 2>/dev/null | head -12 || true
|
||||
echo "=== Quick reachability (ICMP may be blocked on some hotspots) ==="
|
||||
ping -c 1 -W 2000 1.1.1.1 >/dev/null 2>&1 && echo "ping 1.1.1.1: ok" || echo "ping 1.1.1.1: fail"
|
||||
;;
|
||||
recover)
|
||||
echo "1) Отключаем автоматический PAC на выбранных сервисах..."
|
||||
if [ -x "$PAC_SCRIPT" ]; then
|
||||
MACOS_PAC_SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}" "$PAC_SCRIPT" off
|
||||
else
|
||||
echo "WARN: нет $PAC_SCRIPT — вручную: Системные настройки → Сеть → Wi‑Fi → Подробнее → Прокси → снять «Автоматическая настройка прокси»" >&2
|
||||
fi
|
||||
echo "2) Отключаем системный SOCKS-прокси на Wi-Fi (если был включён Shadowsocks и т.п.)..."
|
||||
if networksetup -listallnetworkservices 2>/dev/null | grep -Fqx "Wi-Fi"; then
|
||||
networksetup -setsocksfirewallproxystate "Wi-Fi" off 2>/dev/null || true
|
||||
fi
|
||||
echo "3) Сброс DNS-кэша (нужен sudo)..."
|
||||
if sudo -n true 2>/dev/null; then
|
||||
sudo dscacheutil -flushcache 2>/dev/null || true
|
||||
sudo killall -HUP mDNSResponder 2>/dev/null || true
|
||||
echo " mDNSResponder перезагружен."
|
||||
else
|
||||
echo " Пропуск (нет sudo без пароля). Выполните вручную:"
|
||||
echo " sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder"
|
||||
fi
|
||||
dev="$(wifi_device)"
|
||||
if [ -n "$dev" ]; then
|
||||
echo "4) Обновление DHCP на $dev (нужен sudo)..."
|
||||
if sudo -n true 2>/dev/null; then
|
||||
sudo ipconfig set "$dev" DHCP 2>/dev/null || true
|
||||
else
|
||||
echo " Пропуск. Вручную: sudo ipconfig set $dev DHCP"
|
||||
fi
|
||||
echo "5) Краткое выключение/включение Wi‑Fi радиомодуля ($dev)..."
|
||||
networksetup -setairportpower "$dev" off 2>/dev/null || true
|
||||
sleep 2
|
||||
networksetup -setairportpower "$dev" on 2>/dev/null || true
|
||||
else
|
||||
echo "4–5) Не удалось определить Wi‑Fi device — пропуск DHCP/airport power. Вручную выключите/включите Wi‑Fi в меню."
|
||||
fi
|
||||
echo "=== Готово. Проверка: ==="
|
||||
"$0" status
|
||||
echo ""
|
||||
echo "Если интернет всё ещё нет: откройте хотспот в браузере (captive portal), затем при необходимости снова включите PAC:"
|
||||
echo " ./docs/connectivity/scripts/macos-pac-on.sh"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
2
docs/connectivity/scripts/macos-pac-off.sh
Executable file
2
docs/connectivity/scripts/macos-pac-off.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/macos-system-pac.sh" off
|
||||
2
docs/connectivity/scripts/macos-pac-on.sh
Executable file
2
docs/connectivity/scripts/macos-pac-on.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/macos-system-pac.sh" on
|
||||
76
docs/connectivity/scripts/macos-system-pac.sh
Executable file
76
docs/connectivity/scripts/macos-system-pac.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
# Включает/выключает системный «автоматический прокси» (PAC) в macOS через networksetup.
|
||||
# По умолчанию: PAC http://localhost:1089/proxy.pac на сервисах Wi-Fi и Thunderbolt Bridge 2.
|
||||
#
|
||||
# Переменные окружения:
|
||||
# MACOS_PAC_URL — URL proxy.pac (по умолчанию http://localhost:1089/proxy.pac)
|
||||
# MACOS_PAC_SERVICES — список имён сетевых сервисов через «|» (см. networksetup -listallnetworkservices)
|
||||
|
||||
usage() {
|
||||
printf '%s\n' "Usage: $(basename "$0") on|off|status" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$#" -eq 1 ] || usage
|
||||
cmd=$1
|
||||
|
||||
PAC_URL="${MACOS_PAC_URL:-http://localhost:1089/proxy.pac}"
|
||||
SERVICES="${MACOS_PAC_SERVICES:-Wi-Fi|Thunderbolt Bridge 2}"
|
||||
|
||||
svc_exists() {
|
||||
networksetup -listallnetworkservices 2>/dev/null | grep -Fqx "$1"
|
||||
}
|
||||
|
||||
case "$cmd" in
|
||||
on)
|
||||
OLDIFS=$IFS
|
||||
IFS='|'
|
||||
for svc in $SERVICES; do
|
||||
IFS=$OLDIFS
|
||||
[ -z "$svc" ] && continue
|
||||
if ! svc_exists "$svc"; then
|
||||
echo "skip (нет сервиса): $svc" >&2
|
||||
continue
|
||||
fi
|
||||
networksetup -setautoproxyurl "$svc" "$PAC_URL"
|
||||
networksetup -setautoproxystate "$svc" on
|
||||
echo "PAC on: $svc"
|
||||
done
|
||||
;;
|
||||
off)
|
||||
OLDIFS=$IFS
|
||||
IFS='|'
|
||||
for svc in $SERVICES; do
|
||||
IFS=$OLDIFS
|
||||
[ -z "$svc" ] && continue
|
||||
if ! svc_exists "$svc"; then
|
||||
echo "skip (нет сервиса): $svc" >&2
|
||||
continue
|
||||
fi
|
||||
networksetup -setautoproxystate "$svc" off
|
||||
echo "PAC off: $svc"
|
||||
done
|
||||
;;
|
||||
status)
|
||||
printf '%s\n' "PAC_URL=$PAC_URL"
|
||||
printf '%s\n' "SERVICES=$SERVICES"
|
||||
printf '%s\n' "---"
|
||||
OLDIFS=$IFS
|
||||
IFS='|'
|
||||
for svc in $SERVICES; do
|
||||
IFS=$OLDIFS
|
||||
[ -z "$svc" ] && continue
|
||||
if svc_exists "$svc"; then
|
||||
echo "[$svc]"
|
||||
networksetup -getautoproxyurl "$svc"
|
||||
else
|
||||
echo "[$svc] (нет такого сервиса)"
|
||||
fi
|
||||
done
|
||||
printf '%s\n' "--- scutil --proxy ---"
|
||||
scutil --proxy 2>/dev/null || true
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
97
docs/connectivity/scripts/setup-ssh-key-for-proxy.ps1
Normal file
97
docs/connectivity/scripts/setup-ssh-key-for-proxy.ps1
Normal file
@@ -0,0 +1,97 @@
|
||||
# Setup SSH key for proxy tunneling only (ru.hunab.app / 149.154.64.19).
|
||||
# Usage: run in PowerShell from repo root or pass path; no admin rights needed.
|
||||
# The key is ONLY for SSH tunnels (-D SOCKS, -L). User ahau on server needs no extra rights.
|
||||
|
||||
param(
|
||||
[string]$SshDir = "$env:USERPROFILE\.ssh",
|
||||
[string]$KeyName = "id_ed25519",
|
||||
[string]$ProxyUser = "ahau",
|
||||
[string]$ProxyHost = "149.154.64.19"
|
||||
)
|
||||
|
||||
$KeyPath = Join-Path $SshDir $KeyName
|
||||
$KeyPubPath = "$KeyPath.pub"
|
||||
|
||||
Write-Host "=== SSH key for proxy tunnel (Cursor/Telegram) ===" -ForegroundColor Cyan
|
||||
Write-Host " Server: ${ProxyUser}@${ProxyHost} (ru.hunab.app)"
|
||||
Write-Host " Purpose: tunneling only. No shell or other rights on server."
|
||||
Write-Host ""
|
||||
|
||||
# Ensure .ssh exists
|
||||
if (-not (Test-Path $SshDir)) {
|
||||
New-Item -ItemType Directory -Path $SshDir -Force | Out-Null
|
||||
Write-Host "[OK] Created $SshDir"
|
||||
} else {
|
||||
Write-Host "[OK] Directory exists: $SshDir"
|
||||
}
|
||||
|
||||
# Check OpenSSH
|
||||
try {
|
||||
$null = Get-Command ssh -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Host "[ERROR] OpenSSH client not found. Install: Settings -> Apps -> Optional features -> OpenSSH Client" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path $KeyPath)) {
|
||||
Write-Host ""
|
||||
Write-Host "No key found at $KeyPath" -ForegroundColor Yellow
|
||||
Write-Host " Option A: Copy id_ed25519 (and id_ed25519.pub) from your Mac to $SshDir"
|
||||
Write-Host " Option B: Generate a new key now (only for tunneling; add .pub to server once)"
|
||||
$choice = Read-Host "Generate new key? (y/N)"
|
||||
if ($choice -match '^[yY]') {
|
||||
ssh-keygen -t ed25519 -f $KeyPath -N '""'
|
||||
if (-not (Test-Path $KeyPath)) { Write-Host "Key creation failed." -ForegroundColor Red; exit 1 }
|
||||
Write-Host "[OK] Key created: $KeyPath"
|
||||
} else {
|
||||
Write-Host "Copy id_ed25519 and id_ed25519.pub to $SshDir then run this script again."
|
||||
exit 0
|
||||
}
|
||||
} else {
|
||||
Write-Host "[OK] Key exists: $KeyPath"
|
||||
}
|
||||
|
||||
# Show public key (user must add to server if new)
|
||||
if (Test-Path $KeyPubPath) {
|
||||
Write-Host ""
|
||||
Write-Host "Public key (add one line to ~/.ssh/authorized_keys of ${ProxyUser}@${ProxyHost}):" -ForegroundColor Cyan
|
||||
Write-Host (Get-Content $KeyPubPath -Raw).Trim()
|
||||
Write-Host ""
|
||||
Write-Host "On server (once): append the line above to /home/${ProxyUser}/.ssh/authorized_keys"
|
||||
Write-Host "This key is only used for tunneling; no sudo or other rights needed for ahau."
|
||||
} else {
|
||||
Write-Host "[WARN] No $KeyPubPath found. Generate or copy from Mac." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Test connection
|
||||
Write-Host ""
|
||||
Write-Host "Testing connection to ${ProxyUser}@${ProxyHost} ..."
|
||||
$testResult = & ssh -i $KeyPath -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "${ProxyUser}@${ProxyHost}" "echo OK" 2>&1
|
||||
if ($LASTEXITCODE -eq 0 -and $testResult -eq "OK") {
|
||||
Write-Host "[OK] SSH works. You can start the tunnel (cursor-socks-tunnel.ps1 start)." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[FAIL] SSH test failed. Ensure the public key is in authorized_keys on the server." -ForegroundColor Red
|
||||
Write-Host $testResult
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Optional: ensure config has hsites-ahau
|
||||
$ConfigPath = Join-Path $SshDir "config"
|
||||
$configEntry = @"
|
||||
Host hsites-ahau
|
||||
HostName $ProxyHost
|
||||
User $ProxyUser
|
||||
IdentityFile ~/.ssh/$KeyName
|
||||
ServerAliveInterval 30
|
||||
ServerAliveCountMax 6
|
||||
"@
|
||||
if (-not (Test-Path $ConfigPath)) {
|
||||
Set-Content -Path $ConfigPath -Value $configEntry.Trim()
|
||||
Write-Host "[OK] Created $ConfigPath with Host hsites-ahau"
|
||||
} elseif ((Get-Content $ConfigPath -Raw) -notmatch "hsites-ahau") {
|
||||
Add-Content -Path $ConfigPath -Value "`n$configEntry"
|
||||
Write-Host "[OK] Appended hsites-ahau to $ConfigPath"
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done. Run: .\docs\cursor\scripts\cursor-socks-tunnel.ps1 start" -ForegroundColor Green
|
||||
19
docs/connectivity/scripts/ssh-hunab-prod.sh
Executable file
19
docs/connectivity/scripts/ssh-hunab-prod.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# SSH к проду: по умолчанию в конфиге hunab-prod уже через hsites-ahau.
|
||||
# Если прямой DO доступен — быстрее hunab-prod-direct (пробуем за 8 с).
|
||||
# HUNAB_PROD_FORCE_VIA=1 — не пробовать прямой, сразу hunab-prod (через RU).
|
||||
# HUNAB_PROD_FORCE_DIRECT=1 — только hunab-prod-direct.
|
||||
# Использование: ./ssh-hunab-prod.sh [аргументы ssh]
|
||||
set -e
|
||||
KA=( -o ServerAliveInterval=30 -o ServerAliveCountMax=8 -o TCPKeepAlive=yes )
|
||||
|
||||
if [ "${HUNAB_PROD_FORCE_DIRECT:-0}" = "1" ]; then
|
||||
exec ssh "${KA[@]}" hunab-prod-direct "$@"
|
||||
fi
|
||||
|
||||
if [ "${HUNAB_PROD_FORCE_VIA:-0}" != "1" ]; then
|
||||
if ssh "${KA[@]}" -o BatchMode=yes -o ConnectTimeout=8 -o ConnectionAttempts=1 hunab-prod-direct "exit 0" 2>/dev/null; then
|
||||
exec ssh "${KA[@]}" hunab-prod-direct "$@"
|
||||
fi
|
||||
fi
|
||||
exec ssh "${KA[@]}" hunab-prod "$@"
|
||||
@@ -1,26 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
# SOCKS5-туннель для Telegram (и др. приложений) через прокси 149.154.64.19.
|
||||
# Порт 1081 — отдельно от Cursor (10809). Использование: ./telegram-socks-tunnel.sh [start|stop|status]
|
||||
# См. connectivity/PROXY_GUIDE.md
|
||||
# См. connectivity/PROXY_GUIDE.md (в т.ч. цепочка через DigitalOcean: TELEGRAM_SOCKS_VIA_DO=1)
|
||||
|
||||
set -e
|
||||
TELEGRAM_SOCKS_PORT="${TELEGRAM_SOCKS_PORT:-1081}"
|
||||
SSH_HOST="${TELEGRAM_TUNNEL_HOST:-ahau@149.154.64.19}"
|
||||
TELEGRAM_SOCKS_VIA_DO="${TELEGRAM_SOCKS_VIA_DO:-0}"
|
||||
TELEGRAM_SOCKS_JUMP_HOST="${TELEGRAM_SOCKS_JUMP_HOST:-hsites-ahau}"
|
||||
TELEGRAM_SOCKS_FINAL_HOST="${TELEGRAM_SOCKS_FINAL_HOST:-hunab@209.38.32.21}"
|
||||
TELEGRAM_SOCKS_FINAL_PORT="${TELEGRAM_SOCKS_FINAL_PORT:-2222}"
|
||||
TELEGRAM_SOCKS_FINAL_IDENTITY="${TELEGRAM_SOCKS_FINAL_IDENTITY:-$HOME/.ssh/hunab_deploy_key}"
|
||||
# Пусто = только loopback (по умолчанию в OpenSSH). 0.0.0.0 — слушать на всех интерфейсах (телефон → SOCKS на Mac по хотспоту).
|
||||
TELEGRAM_SOCKS_BIND="${TELEGRAM_SOCKS_BIND:-}"
|
||||
# SOCKS держит много соединений (Telegram); без повышения ulimit — «Too many open files».
|
||||
TELEGRAM_SSH_STABLE_OPTS="-o TCPKeepAlive=yes -o IPQoS=throughput -o ServerAliveInterval=60 -o ServerAliveCountMax=4"
|
||||
|
||||
raise_fd_limit() {
|
||||
ulimit -n 65536 2>/dev/null || ulimit -n 10240 2>/dev/null || true
|
||||
}
|
||||
|
||||
socks_listening() {
|
||||
lsof -i :"$TELEGRAM_SOCKS_PORT" -sTCP:LISTEN -t >/dev/null 2>&1
|
||||
}
|
||||
|
||||
kill_listener_on_port() {
|
||||
# PIDs listening on TELEGRAM_SOCKS_PORT (usually ssh -D; LaunchAgent may respawn — loop a few times)
|
||||
local i=0
|
||||
while socks_listening && [ "$i" -lt 20 ]; do
|
||||
lsof -ti :"$TELEGRAM_SOCKS_PORT" -sTCP:LISTEN 2>/dev/null | xargs kill 2>/dev/null || true
|
||||
sleep 0.35
|
||||
i=$((i + 1))
|
||||
done
|
||||
}
|
||||
|
||||
start() {
|
||||
if lsof -i :"$TELEGRAM_SOCKS_PORT" -sTCP:LISTEN -t >/dev/null 2>&1; then
|
||||
echo "SOCKS already listening on 127.0.0.1:$TELEGRAM_SOCKS_PORT"
|
||||
return 0
|
||||
if socks_listening; then
|
||||
if [ "$TELEGRAM_SOCKS_VIA_DO" = "1" ]; then
|
||||
echo "Port $TELEGRAM_SOCKS_PORT busy — replacing with via-DO tunnel..."
|
||||
kill_listener_on_port
|
||||
if socks_listening; then
|
||||
echo "Port $TELEGRAM_SOCKS_PORT still in use. If LaunchAgent keeps respawning the old tunnel, run:"
|
||||
echo " launchctl unload \"\$HOME/Library/LaunchAgents/com.hunab.telegram-socks.plist\" 2>/dev/null"
|
||||
echo " (or ./docs/connectivity/launchd/uninstall.sh) then: TELEGRAM_SOCKS_VIA_DO=1 $0 start"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "SOCKS already listening on port $TELEGRAM_SOCKS_PORT"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
raise_fd_limit
|
||||
local d_spec="$TELEGRAM_SOCKS_PORT"
|
||||
if [ -n "$TELEGRAM_SOCKS_BIND" ]; then
|
||||
d_spec="${TELEGRAM_SOCKS_BIND}:${TELEGRAM_SOCKS_PORT}"
|
||||
fi
|
||||
if [ "$TELEGRAM_SOCKS_VIA_DO" = "1" ]; then
|
||||
if ! ssh -o BatchMode=yes -o ConnectTimeout=6 -o ConnectionAttempts=1 hsites-ahau "exit 0" 2>/dev/null; then
|
||||
echo "Ошибка: hsites-ahau недоступен. TELEGRAM_SOCKS_VIA_DO=1 — только Mac→RU→DO; прямой Mac→DO для SOCKS отключён." >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC2086
|
||||
ssh -D "$TELEGRAM_SOCKS_PORT" -f -N \
|
||||
$TELEGRAM_SSH_STABLE_OPTS \
|
||||
telegram-socks-via-do
|
||||
echo "SOCKS started: 127.0.0.1:$TELEGRAM_SOCKS_PORT -> hsites-ahau -> DO (telegram-socks-via-do)"
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
ssh -D "$TELEGRAM_SOCKS_PORT" -f -N \
|
||||
$TELEGRAM_SSH_STABLE_OPTS \
|
||||
"$SSH_HOST"
|
||||
echo "SOCKS tunnel started: 127.0.0.1:$TELEGRAM_SOCKS_PORT -> $SSH_HOST"
|
||||
fi
|
||||
ssh -D "$TELEGRAM_SOCKS_PORT" -f -N \
|
||||
-o ServerAliveInterval=30 \
|
||||
-o ServerAliveCountMax=6 \
|
||||
"$SSH_HOST"
|
||||
echo "SOCKS tunnel started: 127.0.0.1:$TELEGRAM_SOCKS_PORT -> $SSH_HOST"
|
||||
}
|
||||
|
||||
stop() {
|
||||
pkill -f "ssh.*-D $TELEGRAM_SOCKS_PORT.*$SSH_HOST" 2>/dev/null || true
|
||||
pkill -f "ssh.*-D $TELEGRAM_SOCKS_PORT" 2>/dev/null || true
|
||||
echo "Tunnel stopped (if it was running)."
|
||||
}
|
||||
|
||||
@@ -39,7 +94,8 @@ case "${1:-start}" in
|
||||
status) status ;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status}"
|
||||
echo "Env: TELEGRAM_SOCKS_PORT=$TELEGRAM_SOCKS_PORT, TELEGRAM_TUNNEL_HOST=$SSH_HOST"
|
||||
echo "Env: TELEGRAM_SOCKS_PORT, TELEGRAM_TUNNEL_HOST (default exit: ru proxy)"
|
||||
echo " TELEGRAM_SOCKS_VIA_DO=1 — только Mac→RU→DO (telegram-socks-via-do); без прямого DO"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
|
||||
---
|
||||
|
||||
## SSH SOCKS: выход в интернет через DO без WireGuard / Shadowsocks
|
||||
|
||||
Тот же сервер **209.38.32.21**. Для **Cursor** (10809) и **Telegram** (1081) можно обойтись без Docker-VPN: локальный SOCKS через SSH, снаружи — IP DO только по цепочке **Mac→RU→DO** (`telegram-socks-via-do`). Прямой Mac→DO для этих скриптов не используется. Быстрое переключение режимов: [SSH_EXIT_MODES.md](./SSH_EXIT_MODES.md), скрипты в **`scripts/vpn/`** (из корня репозитория: `./scripts/vpn/ssh-exit-mode.sh`). Подробности по IDE: [docs/cursor/README.md](../../cursor/README.md).
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### Shadowsocks (Рекомендуется для обхода блокировок)
|
||||
@@ -41,6 +47,8 @@ Method: chacha20-ietf-poly1305
|
||||
|
||||
### Основные руководства
|
||||
|
||||
- **[SSH_EXIT_MODES.md](./SSH_EXIT_MODES.md)** — быстрое переключение: локальный IP / RU / Mac→RU→DO; скрипты: **`scripts/vpn/`** в корне репозитория (`./scripts/vpn/ssh-exit-mode.sh`)
|
||||
|
||||
- **[SHADOWSOCKS.md](./SHADOWSOCKS.md)** - Полное руководство по Shadowsocks
|
||||
- Развертывание сервера
|
||||
- Настройка клиентов (Android, macOS, CLI)
|
||||
@@ -69,6 +77,9 @@ Method: chacha20-ietf-poly1305
|
||||
|
||||
Все скрипты находятся в папке `scripts/`:
|
||||
|
||||
- **`scripts/vpn/ssh-exit-mode.sh`** — режимы SOCKS: `local` | `ru` | `ru-do` | `status` | `verify` (см. [SSH_EXIT_MODES.md](./SSH_EXIT_MODES.md))
|
||||
- **`scripts/vpn/exit-via-local.sh`**, **`exit-via-ru.sh`**, **`exit-via-ru-do.sh`** — короткие вызовы тех же режимов
|
||||
|
||||
- `scripts/toggle-shadowsocks-proxy.sh` - Переключение системного прокси
|
||||
- `scripts/check-proxy-status.sh` - Проверка статуса прокси
|
||||
- `scripts/fix-shadowsocks.sh` - Автоматическое исправление
|
||||
|
||||
95
docs/connectivity/vpn/SSH_EXIT_MODES.md
Normal file
95
docs/connectivity/vpn/SSH_EXIT_MODES.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# SSH SOCKS: быстрое переключение выхода в интернет
|
||||
|
||||
Три режима без WireGuard/Shadowsocks: **локальный IP**, **RU-прокси**, **RU → DO** (снаружи виден **IP прод-сервера DO**).
|
||||
|
||||
Политика: для выхода с **IP DO** используется **только** цепочка **Mac → RU (`hsites-ahau`) → DO** (`telegram-socks-via-do`). Прямой Mac→DO скрипты не поднимают.
|
||||
|
||||
---
|
||||
|
||||
## Ожидаемые IP (проверка «снаружи»)
|
||||
|
||||
| Режим | Как проверить | Ожидаемый публичный IP (на момент настройки репо) |
|
||||
|--------|----------------|-----------------------------------------------------|
|
||||
| **local** | `curl -sS https://ipinfo.io/ip` | Ваш домашний/мобильный IP |
|
||||
| **ru** | `curl -sS -x socks5h://127.0.0.1:10809 https://ipinfo.io/ip` | **149.154.64.19** (RU VPS, `ru.hunab.app`) |
|
||||
| **ru-do** | то же `-x socks5h://127.0.0.1:10809` | **209.38.32.21** (DO, `hunab.app`) |
|
||||
|
||||
Если IP у VPS сменился — сверяйте с [PROXY_GUIDE.md](../PROXY_GUIDE.md) / [PROXY_EMERGENCY.md](../PROXY_EMERGENCY.md).
|
||||
|
||||
Быстрая проверка одной командой (порт SOCKS по умолчанию **10809**):
|
||||
|
||||
```bash
|
||||
./scripts/vpn/ssh-exit-mode.sh verify
|
||||
```
|
||||
|
||||
После **`ru-do`** в блоке «Через SOCKS» должно быть **209.38.32.21** (или актуальный IP DO). После **`ru`** — **149.154.64.19**.
|
||||
|
||||
---
|
||||
|
||||
## Скрипты (из корня репозитория `local_machine`)
|
||||
|
||||
**Один вход:**
|
||||
|
||||
```bash
|
||||
./scripts/vpn/ssh-exit-mode.sh COMMAND
|
||||
```
|
||||
|
||||
**Короткие алиасы** (то же самое):
|
||||
|
||||
```bash
|
||||
./scripts/vpn/exit-via-local.sh # стоп туннелей
|
||||
./scripts/vpn/exit-via-ru.sh # только RU
|
||||
./scripts/vpn/exit-via-ru-do.sh # RU → DO (IP DO снаружи)
|
||||
```
|
||||
|
||||
| Команда | Действие |
|
||||
|---------|----------|
|
||||
| **`local`** \| **`off`** \| **`stop`** | Гасит SSH SOCKS на портах **10809**, **1081**, **1082**. Дальше: в **Cursor** уберите `http.proxy` / `https.proxy` (или отключите системный SOCKS) — трафик пойдёт с **локального публичного IP**. |
|
||||
| **`ru`** | Поднимает SOCKS **`127.0.0.1:10809`** → выход с **RU** (`hsites-ahau`). В Cursor: `socks5://127.0.0.1:10809`. |
|
||||
| **`ru-do`** | Проверка RU (6 с), затем SOCKS на **10809** через **`telegram-socks-via-do`**. Снаружи — **IP DO**. В Cursor: тот же `socks5://127.0.0.1:10809`. |
|
||||
| **`status`** | Кто слушает порты из списка остановки. |
|
||||
| **`verify`** | IP без прокси и через SOCKS на `SSH_EXIT_MAIN_PORT`. |
|
||||
|
||||
**Переменные:** `SSH_EXIT_MAIN_PORT` (по умолчанию **10809**), `SSH_EXIT_STOP_PORTS`, `SSH_EXIT_RU_HOST` (по умолчанию `hsites-ahau`).
|
||||
|
||||
**`~/.ssh/config`:** `hsites-ahau`, **`telegram-socks-via-do`** с `ProxyJump hsites-ahau` — шаблоны в [PROXY_GUIDE.md](../PROXY_GUIDE.md).
|
||||
|
||||
---
|
||||
|
||||
## Cursor за 30 секунд
|
||||
|
||||
1. **`./scripts/vpn/exit-via-ru-do.sh`** (или `./scripts/vpn/ssh-exit-mode.sh ru-do`).
|
||||
2. **`./scripts/vpn/ssh-exit-mode.sh verify`** — убедиться, что через SOCKS IP **DO**.
|
||||
3. В Cursor: **Settings → proxy** → `socks5://127.0.0.1:10809` (и для HTTPS то же). **Disable Http2** — выкл.
|
||||
|
||||
На **локальный IP:** `exit-via-local.sh`, затем в Cursor **убрать** proxy.
|
||||
|
||||
---
|
||||
|
||||
## Telegram (порт 1081)
|
||||
|
||||
Скрипт **`local`** останавливает и **1081**. Если нужен Telegram через DO при том же «полном стопе», после `local` поднимите туннель отдельно: [TELEGRAM_GUIDE.md](../TELEGRAM_GUIDE.md), `telegram-socks-tunnel.sh` / LaunchAgent.
|
||||
|
||||
---
|
||||
|
||||
## Схема
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph off [local]
|
||||
A[Приложения без SOCKS] --> NET1[Интернет]
|
||||
end
|
||||
subgraph r [ru]
|
||||
B[SOCKS :10809] --> RU[hsites-ahau]
|
||||
RU --> NET2[Интернет]
|
||||
end
|
||||
subgraph rd [ru-do]
|
||||
C[SOCKS :10809] --> RU2[hsites-ahau]
|
||||
RU2 --> DO[DO 209.38.32.21]
|
||||
DO --> NET3[Интернет]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Обновлено: 2026-04-08*
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Очистка диска Mac — сводный гайд
|
||||
|
||||
Один документ: что не трогать, что можно удалять, быстрые команды, скрипты.
|
||||
**Скрипт:** [cleanup-disk-now.sh](./cleanup-disk-now.sh) · **Спец. фикс:** [ANYTYPE_WORKSPACE_FIX.md](./ANYTYPE_WORKSPACE_FIX.md) · **История:** [CHANGELOG.md](./CHANGELOG.md)
|
||||
**Запрещено удалять:** [DO_NOT_DELETE.md](./DO_NOT_DELETE.md) · **Скрипт:** [cleanup-disk-now.sh](./cleanup-disk-now.sh) · **Спец. фикс:** [ANYTYPE_WORKSPACE_FIX.md](./ANYTYPE_WORKSPACE_FIX.md) · **История:** [CHANGELOG.md](./CHANGELOG.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -20,8 +20,13 @@
|
||||
|
||||
## ❌ Что НЕ удалять
|
||||
|
||||
**Полный белый список:** [DO_NOT_DELETE.md](./DO_NOT_DELETE.md)
|
||||
|
||||
Краткая выжимка:
|
||||
|
||||
| Категория | Пути | Причина |
|
||||
|-----------|------|---------|
|
||||
| **Thunderbird** | `~/Library/Thunderbird/`, `Profiles/*/Mail/*/Inbox` | Почта, не кэш |
|
||||
| Brave | `~/Library/Application Support/BraveSoftware`, `~/Library/Caches/BraveSoftware` | Настройки, закладки, история |
|
||||
| Cursor History | `~/Library/Application Support/Cursor/User/History` | История изменений файлов |
|
||||
| Cursor workspaceStorage | `~/Library/Application Support/Cursor/User/workspaceStorage` | Настройки workspace |
|
||||
@@ -64,7 +69,10 @@
|
||||
|-----|------|----------|
|
||||
| ShipIt (Cursor) | `~/Library/Caches/com.todesktop.230313mzl4w4u92.ShipIt` | 768 MB |
|
||||
| anytype-updater | `~/Library/Caches/anytype-updater` | 453 MB |
|
||||
| com.brave.Browser | `~/Library/Caches/com.brave.Browser` | 423 MB |
|
||||
| ms-playwright | `~/Library/Caches/ms-playwright` | ~800 MB |
|
||||
| responsivelyapp-updater | `~/Library/Caches/responsivelyapp-updater` | ~100 MB |
|
||||
| ResponsivelyApp | `~/Library/Application Support/ResponsivelyApp` | ~90 MB |
|
||||
| GeoServices | `~/Library/Caches/GeoServices` | ~45 MB |
|
||||
| Firefox | `~/Library/Caches/Firefox` | 83 MB |
|
||||
| Google | `~/Library/Caches/Google` | — |
|
||||
| Homebrew | `~/Library/Caches/Homebrew` | — |
|
||||
@@ -136,7 +144,8 @@ rm -f "$HOME/Library/Application Support/Cursor/User/globalStorage/state.vscdb.b
|
||||
# Library/Caches (не Brave)
|
||||
rm -rf "$HOME/Library/Caches/com.todesktop.230313mzl4w4u92.ShipIt"
|
||||
rm -rf "$HOME/Library/Caches/anytype-updater"
|
||||
rm -rf "$HOME/Library/Caches/com.brave.Browser"
|
||||
rm -rf "$HOME/Library/Caches/ms-playwright"
|
||||
rm -rf "$HOME/Library/Caches/responsivelyapp-updater"
|
||||
rm -rf "$HOME/Library/Caches/Firefox"
|
||||
rm -rf "$HOME/Library/Caches/Google"
|
||||
rm -rf "$HOME/Library/Caches/Homebrew"
|
||||
@@ -157,7 +166,7 @@ find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null
|
||||
## 🔄 Регулярность очистки
|
||||
|
||||
- **Еженедельно:** build-артефакты, пустые логи.
|
||||
- **Ежемесячно:** кэши разработки (npm, node-gyp, TypeScript, pip, .yarn), Library/Caches (Google, Firefox, ShipIt, anytype-updater), Cursor WebStorage/Partitions/projects.
|
||||
- **Ежемесячно:** кэши разработки (npm, node-gyp, TypeScript, pip, .yarn), Library/Caches (Google, Firefox, ShipIt, anytype-updater, ms-playwright, responsivelyapp-updater), Cursor WebStorage/Partitions/projects, уровень 1 из cleanup-disk-now.sh.
|
||||
- **При заполнении диска >80%:** всё из ежемесячного + Colima (если не нужен), state.vscdb.backup, приложения (Notion, zoom, LibreOffice, Movavi, Ledger), записи экрана.
|
||||
|
||||
---
|
||||
@@ -172,7 +181,8 @@ find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| [cleanup-disk-now.sh](./cleanup-disk-now.sh) | Интерактивная безопасная очистка (согласовано с DO_NOT_DELETE) |
|
||||
| [DO_NOT_DELETE.md](./DO_NOT_DELETE.md) | **Запрещено удалять** — белый список |
|
||||
| [cleanup-disk-now.sh](./cleanup-disk-now.sh) | Интерактивная безопасная очистка (см. DO_NOT_DELETE.md) |
|
||||
| [scripts/scan-cleanup-items.sh](./scripts/scan-cleanup-items.sh) | Сканирование: что можно удалить и размеры |
|
||||
| [scripts/compress-screenrecordings.sh](./scripts/compress-screenrecordings.sh) | Сжатие записей экрана |
|
||||
| [scripts/quick-cleanup-asahi.sh](./scripts/quick-cleanup-asahi.sh) | Быстрая очистка под Asahi |
|
||||
|
||||
204
docs/disk_cleanup/DO_NOT_DELETE.md
Normal file
204
docs/disk_cleanup/DO_NOT_DELETE.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# ⛔ Запрещено удалять при очистке диска
|
||||
|
||||
**Источник истины** для [cleanup-disk-now.sh](./cleanup-disk-now.sh), [scan-cleanup-items.sh](./scripts/scan-cleanup-items.sh) и любых ручных `rm`.
|
||||
|
||||
Сводный гайд по безопасной очистке: [DISK_CLEANUP.md](./DISK_CLEANUP.md).
|
||||
|
||||
---
|
||||
|
||||
## Правила
|
||||
|
||||
1. **Не удалять целиком** пути из таблиц ниже — только явно разрешённые подпути из [DISK_CLEANUP.md](./DISK_CLEANUP.md).
|
||||
2. Скрипты очистки **обязаны** сверяться с этим файлом перед добавлением новых `rm`.
|
||||
3. При сомнении — **не удалять**; перенести на внешний диск ([scan-move-candidates.sh](./scripts/scan-move-candidates.sh)).
|
||||
4. **Фото и видео** (личные медиа) — отдельная политика: не трогать без явного решения пользователя (см. раздел «Медиа»).
|
||||
|
||||
---
|
||||
|
||||
## Почта — Thunderbird
|
||||
|
||||
| Путь | Ориентир | Причина |
|
||||
|------|----------|---------|
|
||||
| `~/Library/Thunderbird/` | ~2 GB | Профили, настройки, локальная почта |
|
||||
| `~/Library/Thunderbird/Profiles/*/Mail/` | — | Все папки ящиков (Inbox, Sent, …) |
|
||||
| `~/Library/Thunderbird/Profiles/*/Mail/*/Inbox` | **~470 MB+** | **Почта, не кэш** — удаление = потеря писем |
|
||||
| `~/Library/Thunderbird/Profiles/*/ImapMail/` | — | Локальные копии IMAP |
|
||||
| `~/Library/Thunderbird/Profiles/*/global-messages-db.sqlite` | — | Индекс сообщений |
|
||||
|
||||
**Разрешено (осторожно):** только кэши обновлений Thunderbird в `~/Library/Caches/`, если появятся — **не** трогать `Application Support` и `Profiles`.
|
||||
|
||||
---
|
||||
|
||||
## Браузеры
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Application Support/BraveSoftware` | Закладки, история, настройки |
|
||||
| `~/Library/Caches/BraveSoftware` | Связан с Brave (не путать с `com.brave.Browser` без проверки) |
|
||||
|
||||
Firefox / Chrome: не удалять `Application Support` целиком; кэши в `~/Library/Caches/` — по [DISK_CLEANUP.md](./DISK_CLEANUP.md).
|
||||
|
||||
---
|
||||
|
||||
## Cursor / VS Code
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Application Support/Cursor/User/History` | История изменений файлов |
|
||||
| `~/Library/Application Support/Cursor/User/workspaceStorage` | Настройки workspace |
|
||||
| `~/.cursor/extensions` | Расширения (долгая переустановка) |
|
||||
| `~/Library/Application Support/Cursor/logs` | Логи; удаление ухудшает отладку |
|
||||
| `~/Library/Application Support/Cursor/CachedData` | Кэш редактора |
|
||||
| `~/.cursor/ai-tracking` | Трекинг AI |
|
||||
| `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb` | Активная БД состояния (**опционально** чистят вручную при нехватке места — см. DISK_CLEANUP) |
|
||||
|
||||
**Разрешено:** `state.vscdb.backup`, `User/temp/` (устаревшие копии), `WebStorage`, `Partitions`, `~/.cursor/projects`, ShipIt в Caches.
|
||||
|
||||
---
|
||||
|
||||
## Мессенджеры и заметки
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Application Support/Telegram Desktop` | Данные и кэш Telegram |
|
||||
| `~/Library/Application Support/anytype` (целиком) | Workspace'ы Anytype — см. [ANYTYPE_WORKSPACE_FIX.md](./ANYTYPE_WORKSPACE_FIX.md) |
|
||||
| `~/Library/Application Support/direct-commander` | Настройки файлового менеджера |
|
||||
|
||||
---
|
||||
|
||||
## Разработка — Android / Node
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Android/sdk` | Android SDK |
|
||||
| `~/.gradle` | Кэш Gradle |
|
||||
| `~/code/hunabapp-dev/frontend/android/app/build` | Артефакты сборки frontend Android |
|
||||
| `~/code/hunabapp-dev/frontend/node_modules` | Зависимости frontend |
|
||||
| `~/Library/pnpm` | Store pnpm (~2 GB, долгая перекачка) |
|
||||
| `~/Library/Caches/pnpm` | Кэш pnpm |
|
||||
|
||||
**Опционально (не в автоочистке):** `~/.android/avd/` — эмуляторы; удалять только если не нужны.
|
||||
|
||||
---
|
||||
|
||||
## Проекты и бэкапы в `~/code`
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/code/bots/backup_*` | Бэкапы ботов |
|
||||
| `~/code/bots/archives_*` | Архивы ботов |
|
||||
| `~/code/methatron/pics/tablecloth/1-7.zip` | Архив проекта |
|
||||
| `~/code/methatron/.system/db.mysql.persicia` | БД MySQL проекта (~1.7 GB) |
|
||||
| `~/code/methatron/.system/db.mysql.*` | Прочие БД methatron |
|
||||
| Архивы в `~/code/legal/` (zip/rar по делам) | Юридические материалы |
|
||||
| `~/code/legal/legal_education/` | Рабочие материалы (в т.ч. видео курсов) |
|
||||
|
||||
**Не удалять автоматически:** `~/code/archive/` — проверять вручную.
|
||||
|
||||
---
|
||||
|
||||
## Медиа (фото / видео)
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Pictures/` | Фото и альбомы |
|
||||
| `~/Videos/` | Видео |
|
||||
| `~/Movies/` | Фильмы / iMovie |
|
||||
| `~/Music/` | Музыка |
|
||||
| `~/Library/Photos/` | Библиотека Фото (системная) |
|
||||
| `~/Library/ScreenRecordings/` | Записи экрана — только по явному согласию |
|
||||
| Видео в `~/code/legal/`, `~/MEGA/`, `~/gdrive*`, `~/g.drive mikhevel/` | Не считать «кэшем» |
|
||||
|
||||
---
|
||||
|
||||
## Облака и синхронизация
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/MEGA/` | Локальная копия MEGA |
|
||||
| `~/gdrive.hoonabku`, `~/gdrive.sendusletters`, `~/g.drive mikhevel/` | Google Drive / локальные зеркала |
|
||||
| `~/My Drive (*@gmail.com)` | Google Drive File Stream |
|
||||
|
||||
---
|
||||
|
||||
## Игры и Wine
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Application Support/Steam` | Данные Steam |
|
||||
| `~/Library/Containers/com.isaacmarovitz.Whisky/` | Bottles Whisky (игры, Wine) |
|
||||
| `~/Library/Application Support/com.isaacmarovitz.Whisky` | Данные Whisky |
|
||||
|
||||
---
|
||||
|
||||
## Git-бэкапы hunabapp-dev (Google Drive)
|
||||
|
||||
Каталог: `~/gdrive.hoonabku/h.app_bac/` (~900 MB+).
|
||||
|
||||
**Политика:** при очистке диска **всегда сохранять последний git bundle** и его checksum. При появлении нового бэкапа — **обновить таблицу ниже**; более старые bundle в этом каталоге можно удалять вручную только после проверки, что новый бэкап на месте и в Google Drive.
|
||||
|
||||
| Файл | Размер (ориентир) | Дата | Статус |
|
||||
|------|-------------------|------|--------|
|
||||
| `hunabapp-dev_git_20260524_235507.bundle` | 463 MB | 2026-05-24 | **Последний — не удалять** |
|
||||
| `hunabapp-dev_20260524_235507.sha256` | 208 B | 2026-05-24 | **Пара к bundle — не удалять** |
|
||||
|
||||
Также **не удалять без явного решения:**
|
||||
|
||||
| Файл | Причина |
|
||||
|------|---------|
|
||||
| `~/gdrive.hoonabku/h.app_bac/.env.development` | Секреты окружения |
|
||||
| `~/gdrive.hoonabku/h.app_bac/.env.production` | Секреты окружения |
|
||||
|
||||
**Можно удалить после появления более нового bundle (проверить вручную):**
|
||||
|
||||
| Файл | Размер (ориентир) | Дата |
|
||||
|------|-------------------|------|
|
||||
| `~/gdrive.hoonabku/h.app_bac/.tar.gz` | 449 MB | 2026-03-10 |
|
||||
|
||||
---
|
||||
|
||||
## Git (не удалять, только оптимизировать)
|
||||
|
||||
| Путь | Действие |
|
||||
|------|----------|
|
||||
| `~/code/*/.git/` | `git gc --aggressive` — сжатие, **не** `rm -rf` |
|
||||
| Крупные pack-файлы в `.git/objects/pack/` | Часть истории репозитория |
|
||||
|
||||
---
|
||||
|
||||
## Системное
|
||||
|
||||
| Путь | Причина |
|
||||
|------|---------|
|
||||
| `~/Library/Keychains/` | Пароли |
|
||||
| `~/Library/Application Support/com.apple.sharedfilelist/` | Недавние файлы |
|
||||
| `~/Library/Mobile Documents/` | iCloud Drive |
|
||||
| Снимки Time Machine (локальные) | `tmutil listlocalsnapshots` — не удалять вручную без понимания |
|
||||
|
||||
---
|
||||
|
||||
## Шаблон для скриптов (bash)
|
||||
|
||||
```bash
|
||||
# Пример: пропуск защищённого пути
|
||||
case "$path" in
|
||||
*"/Thunderbird"*|*"/Thunderbird/"*) return 0 ;;
|
||||
*"/BraveSoftware"*) return 0 ;;
|
||||
*"/Cursor/User/History"*) return 0 ;;
|
||||
*"/anytype"*) return 0 ;;
|
||||
*"/gdrive.hoonabku/h.app_bac/"*) return 0 ;;
|
||||
*"/h.app_bac/"*) return 0 ;;
|
||||
esac
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Связанные файлы
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| [DISK_CLEANUP.md](./DISK_CLEANUP.md) | Что **можно** удалять |
|
||||
| [cleanup-disk-now.sh](./cleanup-disk-now.sh) | Интерактивная очистка |
|
||||
| [scripts/scan-cleanup-items.sh](./scripts/scan-cleanup-items.sh) | Сканирование кандидатов |
|
||||
|
||||
**Последнее обновление:** 2026-05-27 — git-бэкап hunabapp-dev в `h.app_bac/` (последний bundle 2026-05-24).
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Безопасная очистка диска — по списку из docs/disk_cleanup/DISK_CLEANUP.md.
|
||||
# НЕ трогает: Brave, Cursor History/workspaceStorage/extensions, Telegram, pnpm, anytype.
|
||||
# Запрещено удалять: docs/disk_cleanup/DO_NOT_DELETE.md
|
||||
|
||||
set -e
|
||||
|
||||
echo "🧹 Безопасная очистка диска (согласовано с DO_NOT_DELETE)"
|
||||
echo "🧹 Безопасная очистка диска (см. DO_NOT_DELETE.md)"
|
||||
echo "=========================================================="
|
||||
echo ""
|
||||
|
||||
@@ -34,6 +34,7 @@ cleanup_dir "$HOME/Library/Caches/node-gyp" "node-gyp кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/typescript" "TypeScript кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/pip" "pip кэш"
|
||||
cleanup_dir "$HOME/.yarn" ".yarn кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/ms-playwright" "Playwright кэш (браузеры для тестов)"
|
||||
echo ""
|
||||
|
||||
# 2. Windsurf полностью (кэш + данные; приложение — удалить вручную из Программы)
|
||||
@@ -49,7 +50,34 @@ echo ""
|
||||
echo "=== 3. Кэши браузеров (не Brave) ==="
|
||||
cleanup_dir "$HOME/Library/Caches/Google" "Google кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/Firefox" "Firefox кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/com.brave.Browser" "com.brave.Browser кэш"
|
||||
echo ""
|
||||
|
||||
# 3b. Уровень 1 — лёгкие кэши и временные артефакты (~0.5 GB)
|
||||
echo "=== 3b. Уровень 1 (кэши приложений, Apple, hunabapp temp) ==="
|
||||
cleanup_dir "$HOME/Library/Caches/responsivelyapp-updater" "responsivelyapp-updater кэш"
|
||||
cleanup_dir "$HOME/Library/Application Support/ResponsivelyApp" "ResponsivelyApp кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/GeoServices" "GeoServices кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/com.apple.helpd" "com.apple.helpd кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/com.apple.CloudTelemetry" "com.apple.CloudTelemetry кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/com.apple.ctcategories.service" "com.apple.ctcategories.service кэш"
|
||||
HUNAB_TEMP="$HOME/code/hunabapp-dev/temp"
|
||||
if [ -d "$HUNAB_TEMP" ]; then
|
||||
find "$HUNAB_TEMP" -maxdepth 1 -type f \( -name 'hunab-backend-bundle-*.tar.gz' -o -name 'hunab-backend-logs-*.tar.gz' \) -print0 2>/dev/null | while IFS= read -r -d '' f; do
|
||||
size=$(du -sh "$f" 2>/dev/null | cut -f1)
|
||||
echo "🗑️ Удаление hunabapp temp: $(basename "$f") ($size)..."
|
||||
rm -f "$f" && echo " ✅ удалён" || echo " ⚠️ не удалось"
|
||||
done
|
||||
fi
|
||||
echo " Build/dist в ~/code (кроме frontend/android)..."
|
||||
find "$HOME/code" -type d \( -name "build" -o -name "dist" -o -name ".next" -o -name "out" \) \
|
||||
-not -path "*/node_modules/*" \
|
||||
-not -path "*/venv/*" \
|
||||
-not -path "*/.venv/*" \
|
||||
-not -path "*/site-packages/*" \
|
||||
-not -path "*/env/*" \
|
||||
-not -path "*/hunabapp-dev/frontend/android/*" \
|
||||
-exec rm -rf {} + 2>/dev/null || true
|
||||
echo " ✅ Build-артефакты уровня 1 очищены"
|
||||
echo ""
|
||||
|
||||
# 4. Приложения — Notion, zoom, LibreOffice, Movavi, Ledger (Telegram в DO_NOT_DELETE)
|
||||
@@ -83,6 +111,7 @@ echo ""
|
||||
|
||||
# 6. Homebrew, .cache, пустые логи
|
||||
echo "=== 6. Homebrew, .cache, пустые логи ==="
|
||||
cleanup_dir "$HOME/Library/Caches/anytype-updater" "anytype-updater кэш"
|
||||
cleanup_dir "$HOME/Library/Caches/Homebrew" "Homebrew кэш"
|
||||
cleanup_dir "$HOME/.cache" ".cache"
|
||||
find "$HOME/Library/Logs" -type f -size 0 -delete 2>/dev/null && echo " ✅ Пустые логи удалены" || true
|
||||
@@ -162,6 +191,7 @@ echo "📊 Состояние диска после очистки:"
|
||||
df -h / | tail -1
|
||||
echo ""
|
||||
echo "✅ Очистка завершена. Перезапустите приложения при необходимости."
|
||||
echo " Список: docs/disk_cleanup/DISK_CLEANUP.md"
|
||||
echo " Разрешено: docs/disk_cleanup/DISK_CLEANUP.md"
|
||||
echo " Запрещено: docs/disk_cleanup/DO_NOT_DELETE.md"
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -33,7 +33,14 @@ scan_dir() {
|
||||
if [ "$exclude_protected" = "true" ]; then
|
||||
# Проверяем, не является ли это защищенным элементом
|
||||
case "$path" in
|
||||
*"/Thunderbird"*) return ;;
|
||||
*"/Pictures"*) return ;;
|
||||
*"/Videos"*) return ;;
|
||||
*"/BraveSoftware"*) return ;;
|
||||
*"/Steam"*) return ;;
|
||||
*"/Whisky"*) return ;;
|
||||
*"/gdrive.hoonabku/h.app_bac"*) return ;;
|
||||
*"/h.app_bac/"*) return ;;
|
||||
*"/Cursor/User/History"*) return ;;
|
||||
*"/Cursor/User/workspaceStorage"*) return ;;
|
||||
*"/Cursor/extensions"*) return ;;
|
||||
@@ -120,6 +127,9 @@ scan_dir "$HOME/.yarn" ".yarn кэш" true
|
||||
scan_dir "$HOME/.colima" "Colima кэш" true
|
||||
scan_dir "$HOME/Library/Caches/colima" "Colima кэш (Library)" true
|
||||
|
||||
# Playwright кэш (браузеры для e2e-тестов)
|
||||
scan_dir "$HOME/Library/Caches/ms-playwright" "Playwright кэш" true
|
||||
|
||||
# Codeium/Windsurf кэши
|
||||
scan_dir "$HOME/.codeium/windsurf" "Codeium/Windsurf кэш" true
|
||||
|
||||
@@ -135,7 +145,7 @@ find ~/code -type d -name "node_modules" \
|
||||
-not -path "*/hunabapp-dev/frontend/node_modules" \
|
||||
-exec du -sh {} \; 2>/dev/null | sort -hr | head -5 | while read size path; do
|
||||
printf " %-60s %8s\n" "$path" "$size"
|
||||
local size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
if [ -n "$size_mb" ]; then
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + size_mb))
|
||||
((ITEMS_FOUND++))
|
||||
@@ -156,7 +166,7 @@ find ~/code -type d \( -name "build" -o -name "dist" -o -name ".next" -o -name "
|
||||
-not -path "*/hunabapp-dev/frontend/android/app/build*" \
|
||||
-exec du -sh {} \; 2>/dev/null | sort -hr | head -10 | while read size path; do
|
||||
printf " %-60s %8s\n" "$path" "$size"
|
||||
local size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
if [ -n "$size_mb" ]; then
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + size_mb))
|
||||
((ITEMS_FOUND++))
|
||||
@@ -200,6 +210,7 @@ scan_dir "$HOME/Library/Application Support/Syncthing" "Syncthing кэш" true
|
||||
|
||||
# ResponsivelyApp
|
||||
scan_dir "$HOME/Library/Application Support/ResponsivelyApp" "ResponsivelyApp кэш" true
|
||||
scan_dir "$HOME/Library/Caches/responsivelyapp-updater" "responsivelyapp-updater кэш" true
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}=== CURSOR КЭШИ (кроме защищенных) ===${NC}"
|
||||
@@ -219,7 +230,7 @@ scan_dir "$HOME/Library/Application Support/Cursor/CachedExtensionVSIXs" "Cursor
|
||||
|
||||
# Cursor logs (старые, если есть)
|
||||
if [ -d "$HOME/Library/Application Support/Cursor/logs" ]; then
|
||||
local logs_size=$(du -sm "$HOME/Library/Application Support/Cursor/logs" 2>/dev/null | cut -f1)
|
||||
logs_size=$(du -sm "$HOME/Library/Application Support/Cursor/logs" 2>/dev/null | cut -f1)
|
||||
if [ -n "$logs_size" ] && [ "$logs_size" -gt 10 ]; then
|
||||
printf " %-60s %8sMB\n" "Cursor logs (старые)" "$logs_size"
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + logs_size))
|
||||
@@ -237,6 +248,9 @@ echo ""
|
||||
# Homebrew кэш
|
||||
scan_dir "$HOME/Library/Caches/Homebrew" "Homebrew кэш" true
|
||||
|
||||
# anytype-updater кэш
|
||||
scan_dir "$HOME/Library/Caches/anytype-updater" "anytype-updater кэш" true
|
||||
|
||||
# GeoServices кэш
|
||||
scan_dir "$HOME/Library/Caches/GeoServices" "GeoServices кэш" true
|
||||
|
||||
@@ -266,8 +280,8 @@ echo ""
|
||||
scan_dir "$HOME/Library/Logs/Google" "Логи Google" true
|
||||
|
||||
# Пустые логи
|
||||
local empty_logs=$(find ~/Library/Logs -type f -size 0 2>/dev/null | wc -l | tr -d ' ')
|
||||
if [ "$empty_logs" -gt 0 ]; then
|
||||
empty_logs=$(find ~/Library/Logs -type f -size 0 2>/dev/null | wc -l | tr -d ' ')
|
||||
if [ -n "$empty_logs" ] && [ "$empty_logs" -gt 0 ] 2>/dev/null; then
|
||||
printf " %-60s %8s файлов\n" "Пустые логи" "$empty_logs"
|
||||
fi
|
||||
|
||||
@@ -280,7 +294,7 @@ find ~/code -type d -name "__pycache__" \
|
||||
-path "*/venv/*" -o -path "*/.venv/*" -o -path "*/env/*" \
|
||||
-exec du -sh {} \; 2>/dev/null | sort -hr | head -10 | while read size path; do
|
||||
printf " %-60s %8s\n" "$path" "$size"
|
||||
local size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
if [ -n "$size_mb" ]; then
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + size_mb))
|
||||
((ITEMS_FOUND++))
|
||||
@@ -306,7 +320,7 @@ find ~/code -type f \( -name "*.zip" -o -name "*.rar" -o -name "*.tar.gz" -o -na
|
||||
-size +10M \
|
||||
-exec du -sh {} \; 2>/dev/null | sort -hr | head -10 | while read size path; do
|
||||
printf " %-60s %8s\n" "$path" "$size"
|
||||
local size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
size_mb=$(du -sm "$path" 2>/dev/null | cut -f1)
|
||||
if [ -n "$size_mb" ]; then
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + size_mb))
|
||||
((ITEMS_FOUND++))
|
||||
@@ -335,7 +349,7 @@ echo -e "Найдено элементов для очистки: ${YELLOW}$ITEM
|
||||
echo ""
|
||||
|
||||
if [ "$TOTAL_SIZE" -gt 0 ]; then
|
||||
local total_gb=$(echo "scale=2; $TOTAL_SIZE / 1024" | bc 2>/dev/null || echo "0")
|
||||
total_gb=$(echo "scale=2; $TOTAL_SIZE / 1024" | bc 2>/dev/null || echo "0")
|
||||
echo -e "Общий размер: ${GREEN}${TOTAL_SIZE}MB (~${total_gb}GB)${NC}"
|
||||
else
|
||||
echo -e "Общий размер: ${GREEN}0MB${NC}"
|
||||
@@ -343,7 +357,7 @@ fi
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ ВАЖНО:${NC}"
|
||||
echo " - Защищённые элементы (см. DISK_CLEANUP.md) исключены из сканирования"
|
||||
echo " - Защищённые элементы: docs/disk_cleanup/DO_NOT_DELETE.md"
|
||||
echo " - Проверьте список перед удалением"
|
||||
echo " - Некоторые элементы могут быть пересозданы автоматически"
|
||||
echo ""
|
||||
|
||||
128
docs/disk_cleanup/videos/LOSSLESS_VIDEO_COMPRESSION_GUIDE.md
Normal file
128
docs/disk_cleanup/videos/LOSSLESS_VIDEO_COMPRESSION_GUIDE.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Сжатие видео: lossless и почти lossless
|
||||
|
||||
Гайд для `ffmpeg` с акцентом на две задачи:
|
||||
- **строго без потерь** (математический lossless);
|
||||
- **существенно меньше размер** с визуально очень близким качеством.
|
||||
|
||||
## Ключевое правило
|
||||
|
||||
Нельзя одновременно гарантировать:
|
||||
1) **существенно меньший размер**,
|
||||
2) **строго нулевые потери**.
|
||||
|
||||
Для реально меньших файлов обычно используют режим "почти lossless" (visually lossless).
|
||||
|
||||
## Быстрый выбор режима
|
||||
|
||||
- Нужен архив "бит-в-бит по кадрам" -> `x265 lossless` или `x264 lossless`.
|
||||
- Нужен заметно меньший размер и хорошая совместимость (QuickTime, мессенджеры) -> `x264 CRF 18-20`.
|
||||
|
||||
---
|
||||
|
||||
## 1) Проверка исходника
|
||||
|
||||
```bash
|
||||
ffprobe -v error -show_entries format=filename,format_name,duration,size,bit_rate -of default=nw=1:nk=0 "input.mov"
|
||||
```
|
||||
|
||||
## 2) Строгий lossless (HEVC / x265)
|
||||
|
||||
```bash
|
||||
ffmpeg -i "input.mov" \
|
||||
-map 0 \
|
||||
-c:v libx265 -x265-params lossless=1 \
|
||||
-preset medium \
|
||||
-c:a copy \
|
||||
-movflags +faststart \
|
||||
"output_lossless_x265.mp4"
|
||||
```
|
||||
|
||||
Примечания:
|
||||
- `lossless=1` — математически без потерь;
|
||||
- `-c:a copy` — аудио без перекодирования;
|
||||
- размер может стать больше исходника.
|
||||
|
||||
## 3) Строгий lossless (H.264, совместимее)
|
||||
|
||||
```bash
|
||||
ffmpeg -i "input.mov" \
|
||||
-map 0 \
|
||||
-c:v libx264 -crf 0 -preset medium \
|
||||
-pix_fmt yuv420p \
|
||||
-c:a copy \
|
||||
-movflags +faststart \
|
||||
"output_lossless_x264.mp4"
|
||||
```
|
||||
|
||||
## 4) Практичный режим: существенно меньше, визуально очень близко
|
||||
|
||||
Рекомендуемый профиль для QuickTime:
|
||||
|
||||
```bash
|
||||
ffmpeg -i "input.mov" \
|
||||
-map 0 \
|
||||
-c:v libx264 -preset slow -crf 20 \
|
||||
-profile:v high -pix_fmt yuv420p \
|
||||
-c:a copy \
|
||||
-movflags +faststart \
|
||||
"output_smaller.mp4"
|
||||
```
|
||||
|
||||
Подстройка качества:
|
||||
- `crf 18` — выше качество, больше размер;
|
||||
- `crf 20` — хороший баланс;
|
||||
- `crf 22` — меньше размер, больше риск артефактов.
|
||||
|
||||
## 5) Как проверить, что lossless действительно lossless
|
||||
|
||||
```bash
|
||||
ffmpeg -i "input.mov" -map 0:v:0 -f framemd5 "input.framemd5"
|
||||
ffmpeg -i "output_lossless_x265.mp4" -map 0:v:0 -f framemd5 "output.framemd5"
|
||||
diff "input.framemd5" "output.framemd5"
|
||||
```
|
||||
|
||||
Если `diff` пустой — кадры эквивалентны после декодирования.
|
||||
|
||||
## 6) Batch-команда для папки (`.mov`)
|
||||
|
||||
### 6.1 Строгий lossless
|
||||
|
||||
```bash
|
||||
for f in *.mov; do
|
||||
ffmpeg -i "$f" \
|
||||
-map 0 \
|
||||
-c:v libx265 -x265-params lossless=1 \
|
||||
-preset medium \
|
||||
-c:a copy \
|
||||
-movflags +faststart \
|
||||
"${f%.mov}_lossless.mp4"
|
||||
done
|
||||
```
|
||||
|
||||
### 6.2 Существенно меньше, но визуально близко
|
||||
|
||||
```bash
|
||||
for f in *.mov; do
|
||||
ffmpeg -i "$f" \
|
||||
-map 0 \
|
||||
-c:v libx264 -preset slow -crf 20 \
|
||||
-profile:v high -pix_fmt yuv420p \
|
||||
-c:a copy \
|
||||
-movflags +faststart \
|
||||
"${f%.mov}_smaller.mp4"
|
||||
done
|
||||
```
|
||||
|
||||
## 7) Проблемы с открытием файла
|
||||
|
||||
Если файл не открывается в QuickTime или мессенджере:
|
||||
|
||||
1. Проверь контейнер:
|
||||
```bash
|
||||
ffprobe -v error "video.mp4"
|
||||
```
|
||||
2. Перемести метаданные в начало файла:
|
||||
```bash
|
||||
ffmpeg -i "video.mp4" -c copy -movflags +faststart "video_fixed.mp4"
|
||||
```
|
||||
3. Если кодек не поддерживается, перекодируй в H.264 (`libx264`) с `yuv420p`.
|
||||
117
docs/games/GOG_GALAXY_7H_RCA.md
Normal file
117
docs/games/GOG_GALAXY_7H_RCA.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# GOG Galaxy — RCA за ~7 часов (2026-05-28)
|
||||
|
||||
Bottle: **GOG-Install** (`9ee55e7c-a58e-411e-a93f-30530d4f1073`).
|
||||
|
||||
## Swap / подвисание Mac
|
||||
|
||||
Запуски Galaxy (CEF + несколько `GalaxyClient Helper` + MoltenVK в foreground) **грузят RAM и swap**. При `used swap` > ~400MB скрипты **блокируют** новый запуск.
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/emergency-stop.sh
|
||||
# перезагрузка Mac — если система всё ещё тяжёлая
|
||||
```
|
||||
|
||||
**Не запускать подряд:** `launch-galaxy.sh`, `launch-galaxy-ui.sh`, Whisky pin — без `emergency-stop` между попытками.
|
||||
|
||||
## Статус (честно)
|
||||
|
||||
| Что проверено | Результат |
|
||||
|---------------|-----------|
|
||||
| `GalaxyClient.exe` в процессах после `launch-galaxy.sh` | **Да** (многократно) |
|
||||
| `GalaxyInitialization.log`: `will continue` | **Да** |
|
||||
| `GalaxyClient.log`: `Showing login form` | **Да** |
|
||||
| `GalaxyClient.log`: `Successfully brought window to the foreground` | **Да** |
|
||||
| **Окно видно пользователю на macOS** | **Нет** (не подтверждено) |
|
||||
|
||||
**Вывод:** клиент **запускается**, login UI **в логах есть**; проблема — **отрисовка/привязка окна CEF в Wine на Mac**, не «бинарника нет».
|
||||
|
||||
Собрать свежие логи:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/collect-gog-logs-7h.sh
|
||||
```
|
||||
|
||||
## Хронология (по логам bottle, MSK)
|
||||
|
||||
| Время (≈) | Событие | Значение |
|
||||
|-------------|---------|----------|
|
||||
| 04:56 | `Second client instance` → `will exit` | повторный launch без `reset.sh` |
|
||||
| 05:08 | то же | lock / второй экземпляр |
|
||||
| 05:15–05:18 | `will continue`, login form, foreground | **рабочий запуск процесса** |
|
||||
| 05:19–05:20 | `DeelevateStrategy` fatal, exit **-31** | запуск **без** `/deelevated` (vdesktop) |
|
||||
| 05:21+ | `/deelevated` в Init, login form | фикс в `config.sh` |
|
||||
| 05:29–05:35 | login + foreground (vdesktop) | процесс OK, окно на Mac — нет |
|
||||
| 06:53 | Init: `/deelevated /runWithoutUpdating`, login form | последний автотест |
|
||||
|
||||
## Типичные строки в логах
|
||||
|
||||
**Успех процесса:**
|
||||
|
||||
```
|
||||
Initialization strategy 'InitClientStrategy' ... The client will continue.
|
||||
No refresh token found. Showing login form.
|
||||
Successfully brought window to the foreground.
|
||||
```
|
||||
|
||||
**Падение (старый антипаттерн):**
|
||||
|
||||
```
|
||||
Something went wrong during DeelevateStrategy.
|
||||
... exit code -31 (Privilege de-elevation is needed.). The client will exit.
|
||||
```
|
||||
|
||||
**Окно на Mac (CEF):**
|
||||
|
||||
```
|
||||
Received message from source not matching parent window.
|
||||
eglInitialize: No available renderers.
|
||||
```
|
||||
|
||||
## JA3 без Galaxy UI (рекомендуется на этом Mac)
|
||||
|
||||
Galaxy login в Wine **не показывает окно** → ставить игру через **offline installer** в браузере:
|
||||
|
||||
1. https://www.gog.com/ → Library → Jagged Alliance 3 → **Download offline backup**
|
||||
2. На Mac:
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/emergency-stop.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3-offline.sh ~/Downloads/setup_jagged_alliance_3*.exe
|
||||
```
|
||||
|
||||
## Канон запуска Galaxy (best effort, может не быть окна)
|
||||
|
||||
1. **Whisky GUI (предпочтительно для окна):**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy-whisky.sh
|
||||
# → pin «gog-galaxy» → Run
|
||||
```
|
||||
|
||||
2. **Терминал (процесс + логин в логе):**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/reset.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy.sh
|
||||
```
|
||||
|
||||
3. **Если окна нет — foreground + vdesktop:**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy-ui.sh
|
||||
```
|
||||
|
||||
4. **A/B CEF (как Steam):**
|
||||
|
||||
```bash
|
||||
GOG_CEF_PROFILE=steam bash ~/code/local_machine/scripts/games/gog/launch-galaxy-ui.sh
|
||||
```
|
||||
|
||||
## Что не считать «успехом»
|
||||
|
||||
- Скрипт печатает `OK: GOG Galaxy запущен` — только процесс + лог, **не** видимое окно.
|
||||
- Web-stub / `GalaxySetup.exe` в Wine — **не** доводят установку (gdi32/Inno).
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- [TROUBLESHOOTING.md — GOG Galaxy: окно](TROUBLESHOOTING.md#gog-galaxy-окно-не-открывается)
|
||||
- [gog/README.md](../../scripts/games/gog/README.md)
|
||||
216
docs/games/JA3_MAC_WITHOUT_GALAXY.md
Normal file
216
docs/games/JA3_MAC_WITHOUT_GALAXY.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Jagged Alliance 3 на Mac без GOG Galaxy
|
||||
|
||||
**Полный runbook (установка + графика + RCA + антипаттерны):** [JA3_RUNBOOK.md](JA3_RUNBOOK.md)
|
||||
|
||||
Источники: [GOG — offline installers](https://support.gog.com/hc/en-us/articles/213148105-How-do-I-download-my-purchased-items), [Porting Kit / JA3](https://www.paulthetall.com/jagged-aliance-3-for-mac/), практика на этой машине (**установка 2026-05-28**, **запуск CrossOver 2026-05-29**).
|
||||
|
||||
## Главное
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| **GOG Galaxy** | **не нужен** для одиночной игры (DRM-free) |
|
||||
| **Galaxy** | только для мультиплеера (по карточке GOG) |
|
||||
| **Что нужно** | **Offline backup** (Windows): `setup_*.exe` + `setup_*-1.bin` … `-4.bin` |
|
||||
| **Установка** | **`innoextract`** в bottle — **не** Wine-Inno (падает на botva2/gdi32) |
|
||||
| **Исполняемый файл** | `C:\GOG Games\Jagged Alliance 3\JA3.exe` |
|
||||
| **Bottle** | **GOG-Install** (`scripts/games/gog/bottle.env`) — не HoMM/Steam |
|
||||
| **Galaxy в Wine** | **не использовать** для JA3 — CEF, swap ([GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md)) |
|
||||
| **Запуск игры** | **CrossOver** `run-ja3-crossover.sh` — см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) |
|
||||
| **Steam.app / Steam.exe** | **не в контуре** — отдельный bottle HoMM; в GOG-bottle клиента Steam быть не должно |
|
||||
|
||||
### Не путать с Steam
|
||||
|
||||
В логе JA3 бывает `Steam initialized (AppId …, Beta branch GOG)` — это **GOG Galaxy wrapper** (`steam_api64.dll` + `Galaxy.dll` в папке игры), **не** установленный Steam client и **не** `Steam.app` на Mac.
|
||||
|
||||
| Файл | Это |
|
||||
|------|-----|
|
||||
| `steam_api64.dll` в папке JA3 | GOG API-шим, **не удалять** |
|
||||
| `Program Files (x86)/Steam/steam.exe` | Steam client — **удалить** из GOG-bottle |
|
||||
|
||||
```bash
|
||||
# только GOG-bottle:
|
||||
bash ~/code/local_machine/scripts/games/gog/purge-steam-from-bottle.sh
|
||||
# все bottle + остатки macOS:
|
||||
bash ~/code/local_machine/scripts/games/purge-steam-wine.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Runbook: установка multipart с экономией диска
|
||||
|
||||
Сценарий: все части offline backup лежат в **`~/Games/`** (или `JA3_GAMES_DIR`). На диске мало места (~10–12 GB свободно) — **нельзя** копировать 15 GB архивов в bottle и **нельзя** распаковывать во вторую папку `cache/`.
|
||||
|
||||
### Файлы (5 частей, одна папка)
|
||||
|
||||
| Файл | Размер | Роль |
|
||||
|------|--------|------|
|
||||
| `setup_jagged_alliance_3_1.5.2_(80730).exe` | ~1.6 MB | Лаунчер Inno (обязателен) |
|
||||
| `setup_jagged_alliance_3_1.5.2_(80730)-1.bin` … `-4.bin` | ~15 GB суммарно | Данные установщика |
|
||||
|
||||
Имя `.exe` должно **совпадать с префиксом `.bin`** (без ` (1)` от браузера). Если скачали как `…(80730) (1).exe` — переименовать в `…(80730).exe`.
|
||||
|
||||
### Почему не Wine
|
||||
|
||||
```bash
|
||||
# ❌ install-ja3-multipart.sh / install-ja3-offline.sh через wine64
|
||||
# Падает за ~10 с — Inno Setup + botva2 в Wine (как GalaxySetup).
|
||||
```
|
||||
|
||||
Канон при **наличии архивов**: **`install-ja3-innoextract.sh`** + **`run-ja3.sh`** (флаг `-unpacked`).
|
||||
|
||||
**Важно:** одного innoextract **мало** — Inno не выполняет пост-скрипты (Lua/VFS). Скрипты копируют `CommonLua` и запускают с **`-unpacked`**. Если графика падает (`Failed creating render device`) — доведите установку Wine-инсталлятором: **`install-ja3-wine-finish.sh`** (нужны все 5 файлов в `~/Games/`) или [Porting Kit](https://www.paulthetall.com/jagged-aliance-3-for-mac/).
|
||||
|
||||
### Алгоритм экономии места (что делает скрипт)
|
||||
|
||||
1. **Перед стартом** — безопасная очистка cache (не трогает архивы в `~/Games`):
|
||||
- `scripts/games/cache/gog-galaxy-extract-gog-bottle`
|
||||
- `scripts/games/cache/ja3-extract-test`
|
||||
- `setup_galaxy.exe` в cache, если Galaxy уже установлен в bottle
|
||||
2. **Распаковка сразу в bottle** — `…/drive_c/GOG Games/Jagged Alliance 3/` (без дубликата в `cache/`).
|
||||
3. **По ходу `innoextract`** — при сообщении `Opening "…-2.bin"` удаляется `-1.bin`, при `-3.bin` → `-2.bin`, при `-4.bin` → `-3.bin` (освобождает ~4 GB за шаг).
|
||||
4. **Флаг `-m`** — не извлекать временные файлы Inno (`tmp/`).
|
||||
5. **После успеха** — удаляются оставшиеся `.bin` и `.exe` в `~/Games` (~15 GB). Отключить: `JA3_KEEP_BINS=1`.
|
||||
|
||||
Пиковая нагрузка на диск: **~16 GB** под игру в bottle + оставшиеся `.bin` (уменьшаются по шагам 3), а не **~31 GB** (архивы + полная копия в cache).
|
||||
|
||||
Минимум свободного места на Data: **~800 MB** во время работы (`JA3_MIN_FREE_MB`, по умолчанию 800), при условии что срабатывает удаление `.bin` по ходу. Если диск забит на 100% — сначала освободить 1–2 GB вручную (Корзина, cache), иначе `innoextract` упадёт на больших `Packs/*.hpk`.
|
||||
|
||||
### Команды (один раз)
|
||||
|
||||
```bash
|
||||
# 1) Bottle GOG-Install (если ещё нет)
|
||||
bash ~/code/local_machine/scripts/games/gog/list-bottles.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/use-bottle.sh <UUID> GOG-Install
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-ja3-bottle.sh # DXVK в GOG-bottle
|
||||
|
||||
# 2) Все 5 файлов в ~/Games/
|
||||
ls ~/Games/setup_jagged_alliance_3_1.5.2_\(80730\)*
|
||||
|
||||
# 3) Установка (innoextract + экономия диска)
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3-innoextract.sh
|
||||
# Лог: /tmp/ja3-innoextract-GOG-Install.log
|
||||
```
|
||||
|
||||
Обёртка с автодетектом multipart в `~/Games`:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3.sh
|
||||
```
|
||||
|
||||
Недостающий только `.exe` (~1 MB):
|
||||
|
||||
```bash
|
||||
# account.gog.com → JA3 → offline backup, первая часть
|
||||
# или:
|
||||
lgogdownloader --login
|
||||
bash ~/code/local_machine/scripts/games/gog/fetch-ja3-setup-exe.sh
|
||||
```
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
| Переменная | По умолчанию | Назначение |
|
||||
|------------|--------------|------------|
|
||||
| `JA3_GAMES_DIR` | `~/Games` | Папка с `setup_*.exe` и `.bin` |
|
||||
| `JA3_MIN_FREE_MB` | `800` | Стоп, если свободно меньше (MB) |
|
||||
| `JA3_KEEP_BINS` | `0` | `1` — не удалять архивы после установки |
|
||||
| `JA3_CLEANUP_PARTIAL` | `1` | Удалить незавершённую распаковку в bottle при ошибке |
|
||||
|
||||
### Графика: DirectX 12 (обязательно на Mac)
|
||||
|
||||
JA3 на Windows использует **D3D12**. **DXVK-macOS** (только D3D10/11) **не заменяет** D3D12.
|
||||
|
||||
#### Корневая причина «Failed to initialize graphics subsystem» (RCA 2026-05-29, M1 Pro)
|
||||
|
||||
| Слой | Что происходит |
|
||||
|------|----------------|
|
||||
| **Whisky / Gcenx Wine 11.9** | `d3d12.dll` = wined3d → **Vulkan** (`d3d12_get_vk_physical_device Could not find Vulkan physical device`). **Нет D3DMetal hooks** ([Gcenx#97](https://github.com/Gcenx/macOS_Wine_builds/issues/97)). |
|
||||
| **GPTK PE + Gcenx ntdll** | `__wine_unix_call` / `dxgi.dll not found` — PE DLL GPTK несовместимы с чужим Wine. |
|
||||
| **GPTK Wine 7.7 alone** | `comctl32.dll failed to initialize` на этой macOS — не запускается даже notepad. |
|
||||
| **CrossOver 26 + D3DMetal** | **Работает.** Встроенные `apple_gptk` + `WINED3DMETAL=1`. |
|
||||
|
||||
Сообщения GOG API (`MatchmakingServers`, `Music`, `Game name not defined`) — **не причина** краша.
|
||||
|
||||
#### Канон запуска (подтверждено на этой машине)
|
||||
|
||||
```bash
|
||||
brew install --cask crossover # один раз
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
CrossOver bottle **JA3-GOG** (win10_64, D3DMetal on). Игра клонируется APFS clonefile в bottle (Wine **не читает** symlink за пределы prefix → `CommonLua/Core/autorun.lua not found`).
|
||||
|
||||
**Whisky `run-ja3.sh` для D3D12 на Mac не работает** — только CrossOver (или Porting Kit GUI).
|
||||
|
||||
Альтернатива GUI: [Porting Kit](https://www.paulthetall.com/jagged-aliance-3-for-mac/) (тот же GPTK/D3DMetal стек).
|
||||
|
||||
### Запуск игры
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
Whisky (`run-ja3.sh`) — только установка innoextract в GOG-Install; **графика D3D12 → CrossOver**.
|
||||
|
||||
Обход (тормоза, на свой риск):
|
||||
|
||||
```bash
|
||||
JA3_IGNORE_SWAP=1 bash ~/code/local_machine/scripts/games/gog/run-ja3.sh
|
||||
```
|
||||
|
||||
Cmd+Tab → `wine64` / `JA3`. В графике при артефактах: **отключить heat haze**.
|
||||
|
||||
**Ошибка «Failed to initialize graphics subsystem»:** Whisky/Gcenx Wine **не поддерживает D3DMetal** для d3d12. Используйте **CrossOver**: `setup-ja3-crossover.sh` + `run-ja3-crossover.sh`. Из Whisky Run **не запускать** — только CrossOver bottle JA3-GOG с `start /d` (CWD + CommonLua).
|
||||
|
||||
### Итог на диске (после успеха на этой машине)
|
||||
|
||||
| До | После |
|
||||
|----|--------|
|
||||
| ~15 GB архивы в `~/Games` | архивы удалены |
|
||||
| bottle ~1.4 GB | bottle **+~16 GB** (`GOG Games/Jagged Alliance 3`) |
|
||||
| свободно ~10 GB | свободно **~12 GB** (архивы сняты, игра в bottle) |
|
||||
|
||||
---
|
||||
|
||||
## Скачивание (браузер на Mac)
|
||||
|
||||
1. https://www.gog.com/account → **Jagged Alliance 3**
|
||||
2. **Windows**, язык → **Download offline backup game installers**
|
||||
3. Скачать **все 5 частей** (`.exe` ~1 MB + четыре `.bin`) в одну папку, например `~/Games/`
|
||||
|
||||
---
|
||||
|
||||
## Альтернатива: один .exe без .bin
|
||||
|
||||
Если GOG отдал один большой `setup_jagged_alliance_3_*.exe` (без multipart):
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3-offline.sh ~/Downloads/setup_jagged_alliance_3_*.exe
|
||||
```
|
||||
|
||||
В Wine тоже может упасть — тогда тот же **`install-ja3-innoextract.sh`**, положив единственный `.exe` в `JA3_GAMES_DIR` (без `.bin` innoextract возьмёт данные из exe).
|
||||
|
||||
---
|
||||
|
||||
## Чего не делать
|
||||
|
||||
| ❌ | ✅ |
|
||||
|----|-----|
|
||||
| `GOG_Galaxy_Jagged_Alliance_3.exe` web-stub | offline backup в `~/Games` |
|
||||
| `install-ja3-multipart.sh` (Wine `/VERYSILENT`) | `install-ja3-innoextract.sh` |
|
||||
| Распаковка в `scripts/games/cache/ja3-*` | сразу в `drive_c/GOG Games/…` |
|
||||
| Держать `.bin` после установки без нужды | скрипт удалит (~15 GB) |
|
||||
| `launch-galaxy.sh` / Galaxy CEF для JA3 | **`run-ja3-crossover.sh`** ([JA3_RUNBOOK.md](JA3_RUNBOOK.md)) |
|
||||
| Whisky `run-ja3.sh` для D3D12 | CrossOver + D3DMetal |
|
||||
| `RetinaMode=y` + `LogPixels=192` для мыши JA3 | Клип курсора на середине экрана → **legacy** ([JA3_RUNBOOK.md](JA3_RUNBOOK.md) §9) |
|
||||
| Жёсткий стоп JA3 при swap 800 MB без Wine | Устарело; warn до 4 GB — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §4 |
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- **[JA3_RUNBOOK.md](JA3_RUNBOOK.md)** — канон запуска, RCA 28–29.05, антипаттерны
|
||||
- [WINE_APPS_LAUNCH_RUNBOOK.md](WINE_APPS_LAUNCH_RUNBOOK.md) — §5 JA3
|
||||
- [WINE_MAC_RUNBOOK.md](WINE_MAC_RUNBOOK.md) — §3.5
|
||||
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) — GOG Inno / swap
|
||||
- [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md)
|
||||
297
docs/games/JA3_RUNBOOK.md
Normal file
297
docs/games/JA3_RUNBOOK.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Jagged Alliance 3 (GOG) — runbook Mac / Apple Silicon
|
||||
|
||||
**Машина:** M1 Pro, macOS Sonoma+, bottle Whisky **GOG-Install** (`9ee55e7c-a58e-411e-a93f-30530d4f1073`).
|
||||
**Проверено:** 2026-05-28 (установка) · 2026-05-29 (запуск через CrossOver).
|
||||
|
||||
Связанные документы: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md) (установка offline), [TROUBLESHOOTING.md](TROUBLESHOOTING.md), [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Быстрый путь (канон)
|
||||
|
||||
### Установка (один раз, Whisky)
|
||||
|
||||
```bash
|
||||
# Offline backup (5 файлов) в ~/Games/
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3-innoextract.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/finish-ja3-layout.sh
|
||||
```
|
||||
|
||||
### Запуск (каждый раз, CrossOver + D3DMetal)
|
||||
|
||||
```bash
|
||||
brew install --cask crossover # один раз
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh # один раз
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
- **Cmd+Tab** → окно `JA3.exe`
|
||||
- В настройках графики: **отключить heat haze**
|
||||
- **Swap:** «залипший» swap после Wine (2–4 GB без процессов) **не блокирует** запуск — только предупреждение. Стоп: swap ≥ **4096 MB** без Wine, или swap ≥ **1200 MB** пока **Wine/JA3 ещё работают** → `reset-ja3-crossover.sh` / reboot. Обход: `JA3_IGNORE_SWAP=1`
|
||||
|
||||
### Два «контура»
|
||||
|
||||
| Контур | Роль | Инструмент |
|
||||
|--------|------|------------|
|
||||
| **GOG-Install** (Whisky) | innoextract, файлы игры, CommonLua, реестр GOG | `install-ja3-innoextract.sh`, `finish-ja3-layout.sh` |
|
||||
| **JA3-GOG** (CrossOver) | **запуск** D3D12 через D3DMetal | `setup-ja3-crossover.sh`, `run-ja3-crossover.sh` |
|
||||
|
||||
Whisky **`run-ja3.sh` не запускает JA3 с рабочей графикой** — только CrossOver (или Porting Kit GUI).
|
||||
|
||||
---
|
||||
|
||||
## 2. Почему CrossOver, а не Whisky
|
||||
|
||||
JA3 на Windows использует **DirectX 12**. На Apple Silicon единственный рабочий путь — **Apple D3DMetal** (Game Porting Toolkit), встроенный в **CrossOver 23+** с флагом `WINED3DMETAL=1`.
|
||||
|
||||
**Whisky** использует **Gcenx Wine** — maintainer явно пишет, что сборки **не содержат D3DMetal hooks** ([Gcenx/macOS_Wine_builds#97](https://github.com/Gcenx/macOS_Wine_builds/issues/97)). В этом случае `d3d12.dll` (94 KB) идёт через **wined3d → Vulkan**, а на Mac нет Vulkan physical device:
|
||||
|
||||
```text
|
||||
fixme:d3d12:d3d12_get_vk_physical_device Could not find Vulkan physical device for DXGI adapter.
|
||||
```
|
||||
|
||||
→ UI: **«Failed to initialize graphics subsystem»**
|
||||
→ лог JA3: **`Failed creating render device`**
|
||||
|
||||
Копирование `D3DMetal.framework` + `libd3dshared.dylib` + моста `d3d12.so` **не достаточно**, если PE `d3d12.dll` / `ntdll` от Gcenx — unix-вызовы D3DMetal не подключены.
|
||||
|
||||
---
|
||||
|
||||
## 3. RCA: злоключения 2026-05-28 — 2026-05-29
|
||||
|
||||
Хронология попыток на этой машине (чат + логи `JA3.exe-*.log`, Wine debug).
|
||||
|
||||
### Фаза A — установка и ложные следы (28.05)
|
||||
|
||||
| # | Симптом / действие | Реальная причина | Итог |
|
||||
|---|-------------------|------------------|------|
|
||||
| A1 | GOG web-stub, пустые кнопки | Нет **gdiplus** (winetricks) | ✅ `install-gdiplus.sh` + corefonts |
|
||||
| A2 | Galaxy CEF, окна нет | HWND/CEF в Wine, swap | Galaxy **не нужен** для JA3 offline |
|
||||
| A3 | Лог `Steam initialized … Beta branch GOG` | GOG API-шим (`steam_api64.dll`), **не** Steam.app | ❌ не причина краша |
|
||||
| A4 | `CreateSubsystem failed MatchmakingServers/Music` | GOG wrapper без Galaxy service | ❌ шум, не графика |
|
||||
| A5 | `Game name not defined` | innoextract не выполнил Inno-постскрипты | ⚠️ косметика; фикс: `register-ja3-registry.sh` |
|
||||
| A6 | Swap ~3 GB, скрипт блокирует запуск | «Залипший» swap после Wine; macOS не отдаёт без reboot | ✅ `gog_swap_guard_ja3_launch`: warn до 4 GB без процессов; reboot если Mac тормозит |
|
||||
|
||||
### Фаза B — «графика» в Whisky (28–29.05)
|
||||
|
||||
| # | Попытка | Лог / ошибка | Почему не работает |
|
||||
|---|---------|--------------|-------------------|
|
||||
| B1 | DXVK + `-graphicsapi d3d11` | `Failed creating render device` | DXVK = D3D10/11, **не D3D12** |
|
||||
| B2 | DXMT v0.80 | `dxgi.dll` **c000007b** | Несовместимость с Gcenx Wine 11 + overrides |
|
||||
| B3 | `upgrade-whisky-wine-gcenx.sh` (11.9) + GPTK bridge | `d3d12_get_vk_physical_device` | Gcenx d3d12 = **Vulkan**, не Metal |
|
||||
| B4 | GPTK PE `d3d12.dll`/`dxgi.dll` (114/86 KB) в `system32` | Всё ещё Vulkan path или `dxgi not found` | PE GPTK 7.7 ≠ ntdll Gcenx 11 |
|
||||
| B5 | GPTK PE в `wine/lib/x86_64-windows` | `Library dxgi.dll not found` (c0000135) | `__wine_unix_call` не реализован в Gcenx ntdll |
|
||||
| B6 | Замена `ntdll.so` на GPTK | `virtual_alloc_first_teb` / wine не стартует | Нельзя мешать ntdll разных Wine |
|
||||
| B7 | `ditto` всего GPTK `lib/` в Whisky | Wine **7.7 GPTK** поверх bottle | Перезаписал `wine64` → comctl32 и др. |
|
||||
| B8 | `prepare-ja3-d3dmetal.sh` (убрать d3d12 из system32) | Всё равно render device fail | Builtin Gcenx d3d12 без Metal |
|
||||
| B9 | `forceD3D11: true` в Metadata.plist | Противоречие с D3D12 backend | Исправлено на `false` — без эффекта на Gcenx |
|
||||
| B10 | GPTK Wine 7.7, отдельный prefix | `comctl32.dll failed to initialize` (c0000005) | GPTK Wine 7.7 **сломан** на этой macOS |
|
||||
| B11 | `fixmywhisky.sh` (ditto GPTK lib) | Как B7 | Только D3DMetal.framework + bridge **без** подмены всего Wine |
|
||||
|
||||
### Фаза C — CrossOver (29.05, **успех**)
|
||||
|
||||
| # | Шаг | Результат |
|
||||
|---|-----|-----------|
|
||||
| C1 | `brew install --cask crossover` | CrossOver 26.1, встроенный `apple_gptk` |
|
||||
| C2 | Bottle **JA3-GOG**, template **win10_64** (не win32 Wow64!) | `kernel32` c000007b на 32-bit bottle |
|
||||
| C3 | `WINED3DMETAL=1`, `WINEDXVK=0` в `cxbottle.conf` | D3D12 → Metal |
|
||||
| C4 | Symlink игры из Whisky bottle | `CommonLua/Core/autorun.lua: File Not Found` — Wine **не читает** symlink **вне** prefix |
|
||||
| C5 | `cp -cR` (APFS clonefile) игры в CrossOver bottle | Файлы видны Wine; **~0** доп. места на APFS |
|
||||
| C6 | `start /d "C:\GOG Games\Jagged Alliance 3" JA3.exe -unpacked` | CWD с `CommonLua/`; не `--cx-app` без `/d` |
|
||||
| C7 | Запуск | **JA3.exe** стабильно работает, нет `Failed creating render device` |
|
||||
| C8 | Курсор: смещение клика → `RetinaMode=y` + `LogPixels=192` | Курсор **не опускается ниже середины экрана** (Y делится пополам) | ✅ профиль **`legacy`**: `RetinaMode=n`, без LogPixels — **подтверждено 2026-05-29** |
|
||||
|
||||
### Сигнатуры в логах (диагностика)
|
||||
|
||||
| Строка | Значение |
|
||||
|--------|----------|
|
||||
| `Failed creating render device` | D3D12 не дошёл до GPU — Whisky/Gcenx, не CrossOver |
|
||||
| `d3d12_get_vk_physical_device Could not find Vulkan` | Gcenx wined3d path — нужен CrossOver D3DMetal |
|
||||
| `Failed to initialize graphics subsystem` (UI) | То же, пользовательский текст JA3 |
|
||||
| `Error opening 'CommonLua/Core/autorun.lua'` | Нет CWD (`start /d`) или symlink вместо клона в bottle |
|
||||
| `comctl32.dll failed to initialize` | GPTK Wine 7.7 — не использовать |
|
||||
| `__wine_unix_call` / `aborting` | GPTK PE на чужом Wine |
|
||||
| `dxgi.dll` **c000007b** | DXMT + неправильные overrides |
|
||||
| `Language: English` без `Failed creating` | Графика OK; смотреть CommonLua / crash дальше |
|
||||
| Курсор виден, клик смещён по Y | HiDPI без согласованного масштаба Wine | `fix-ja3-crossover-mouse.sh` → профиль **legacy** |
|
||||
| Курсор **не ниже середины экрана** | `RetinaMode=y` + `LogPixels=192` / двойной DPI | **legacy** (не retina); перезапуск JA3 |
|
||||
|
||||
Логи JA3:
|
||||
`…/AppData/Roaming/Jagged Alliance 3/logs/JA3.exe-*.log`
|
||||
Whisky: `…/Containers/com.franke.Whisky/Bottles/9ee55e7c-…/`
|
||||
CrossOver: `~/Library/Application Support/CrossOver/Bottles/JA3-GOG/`
|
||||
|
||||
---
|
||||
|
||||
## 4. Антипаттерны (полный список)
|
||||
|
||||
### Установка
|
||||
|
||||
| ❌ Не делать | ✅ Вместо |
|
||||
|-------------|----------|
|
||||
| GOG web-stub `GOG_Galaxy_Jagged_Alliance_3.exe` | Offline backup 5 частей в `~/Games/` |
|
||||
| `install-ja3-multipart.sh` (Wine Inno) | `install-ja3-innoextract.sh` |
|
||||
| Распаковка в `scripts/games/cache/` | Сразу в `drive_c/GOG Games/…` |
|
||||
| Bottle **HoMM-OldenEra** (Steam) | Bottle **GOG-Install** |
|
||||
| Удалять `steam_api64.dll` в папке игры | Это GOG-шим, **оставить** |
|
||||
| Steam client в GOG-bottle | `purge-steam-from-bottle.sh` |
|
||||
|
||||
### Графика / Wine
|
||||
|
||||
| ❌ Не делать | ✅ Вместо |
|
||||
|-------------|----------|
|
||||
| Whisky `run-ja3.sh` для игры | **`run-ja3-crossover.sh`** |
|
||||
| `upgrade-whisky-wine-gcenx.sh` «для D3DMetal» | Gcenx **не** поддерживает D3DMetal hooks |
|
||||
| `ditto` всего GPTK `lib/` в Whisky Libraries | Только `D3DMetal.framework` + `libd3dshared` + `.so` bridge (см. `install-gptk-whisky.sh`) |
|
||||
| GPTK PE `d3d12/dxgi` в system32 **Whisky** | CrossOver со встроенным `apple_gptk` |
|
||||
| Замена `ntdll.so` GPTK в Gcenx Wine | Ломает preloader |
|
||||
| DXVK / DXMT для JA3 как «замена D3D12» | Не подтверждено; JA3 = D3D12 |
|
||||
| `dxgi,d3d11=n,b` с DXMT | Ломает цепочку → c000007b |
|
||||
| Whisky Run **`JA3.exe`** без CWD | `start /d` или `run-ja3.cmd` (Whisky) / CrossOver `start /d` |
|
||||
| Symlink игры из Whisky в CrossOver bottle | **`cp -cR`** (APFS clone) |
|
||||
| CrossOver bottle **win32** / Wow64 | **`win10_64`** |
|
||||
| `--cx-app JA3.exe` без `start /d` | `start /d "…Jagged Alliance 3" JA3.exe -unpacked` |
|
||||
| `forceD3D11: true` + backend d3dMetal | `forceD3D11: false` (Metadata Whisky — не решает Gcenx) |
|
||||
|
||||
### Окружение
|
||||
|
||||
| ❌ Не делать | ✅ Вместо |
|
||||
|-------------|----------|
|
||||
| Считать «залипший» swap 2–4 GB блокером JA3 | Запуск разрешён (warn); reboot — если Mac тормозит |
|
||||
| Запуск JA3 при swap ≥ **1200 MB** + **Wine/JA3 ещё работают** | `reset-ja3-crossover.sh` → reboot |
|
||||
| Запуск JA3 при swap ≥ **4096 MB** без Wine | Reboot Mac или `JA3_IGNORE_SWAP=1` |
|
||||
| Жёсткий лимит **800 MB** для `run-ja3-crossover.sh` | Устарело; см. `gog_swap_guard_ja3_launch` в `config.sh` |
|
||||
| Путать лог Galaxy с JA3 | JA3 offline **без** Galaxy UI |
|
||||
| Чинить «graphics» через Galaxy service | `MatchmakingServers`/`Music` — шум GOG API |
|
||||
|
||||
### Мышь (CrossOver / Retina)
|
||||
|
||||
| ❌ Не делать | ✅ Вместо |
|
||||
|-------------|----------|
|
||||
| `RetinaMode=y` + `LogPixels=192` + `Win8DpiScaling=1` «по умолчанию» | **`fix-ja3-crossover-mouse.sh`** — профиль **legacy** (`RetinaMode=n`) |
|
||||
| CrossOver GUI **High Resolution Mode** без проверки | Если клип Y на середине → **legacy** |
|
||||
| Менять реестр мыши без перезапуска JA3 | `reset-ja3-crossover.sh` → `run-ja3-crossover.sh` |
|
||||
| Профиль **retina** при клипе на середине экрана | **legacy**; при смещении клика — **retina-lite** |
|
||||
| Ожидать фикс мыши «на лету» | Workaround: верхний левый угол окна = угол экрана |
|
||||
|
||||
---
|
||||
|
||||
## 5. Скрипты
|
||||
|
||||
| Скрипт | Назначение | Статус |
|
||||
|--------|------------|--------|
|
||||
| `install-ja3-innoextract.sh` | Multipart offline → bottle | ✅ канон установки |
|
||||
| `finish-ja3-layout.sh` | CommonLua, реестр, upscale DLL | ✅ после innoextract |
|
||||
| `setup-ja3-crossover.sh` | Bottle JA3-GOG, clone игры, D3DMetal | ✅ один раз |
|
||||
| **`run-ja3-crossover.sh`** | **Запуск игры** | ✅ **канон** |
|
||||
| `reset-ja3-crossover.sh` | Stop JA3 / Wine перед перезапуском | ✅ |
|
||||
| `fix-ja3-crossover-mouse.sh` | Профили мыши (legacy / retina-lite / retina) | ✅ default **legacy** |
|
||||
| `run-ja3.sh` | Whisky (эксперимент; графика fail) | ⚠️ только с предупреждением |
|
||||
| `prepare-ja3-d3dmetal.sh` | Whisky D3DMetal prep | ⚠️ не решает Gcenx d3d12 |
|
||||
| `upgrade-whisky-wine-gcenx.sh` | Wine 11.9 | ❌ для JA3 не использовать |
|
||||
| `run-ja3-gptk-wine.sh` | GPTK Wine 7.7 prefix | ❌ comctl32 на этой Mac |
|
||||
| `install-ja3-dxmt.sh` | DXMT fallback d3d11 | ❌ c000007b |
|
||||
| `emergency-stop.sh` | Kill Wine/GOG перед запуском | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 6. CrossOver bottle (детали)
|
||||
|
||||
**Путь:** `~/Library/Application Support/CrossOver/Bottles/JA3-GOG/`
|
||||
|
||||
**cxbottle.conf** (минимум):
|
||||
|
||||
```ini
|
||||
"WINED3DMETAL" = "1"
|
||||
"WINEDXVK" = "0"
|
||||
```
|
||||
|
||||
**Игра:** `drive_c/GOG Games/Jagged Alliance 3/` — APFS-клон из Whisky GOG-Install (не symlink).
|
||||
|
||||
**Проверка:**
|
||||
|
||||
```bash
|
||||
pgrep -fl 'JA3\.exe'
|
||||
grep -E 'Failed creating|autorun|CRASH' \
|
||||
"$HOME/Library/Application Support/CrossOver/Bottles/JA3-GOG/drive_c/users/"*/AppData/Roaming/Jagged\ Alliance\ 3/logs/*.log | tail
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Альтернативы
|
||||
|
||||
| Вариант | Комментарий |
|
||||
|---------|-------------|
|
||||
| [Porting Kit](https://www.paulthetall.com/jagged-aliance-3-for-mac/) + GOG offline | Тот же GPTK/D3DMetal; GUI, «просто работает» на M-series |
|
||||
| CrossOver GUI | Bottle JA3-GOG → Install → Run exe; D3DMetal в Advanced Settings |
|
||||
| Whisky + GPTK drag DMG | [Whisky#124](https://github.com/Whisky-App/Whisky/issues/124) — для **DX12** на Gcenx всё равно нет hooks |
|
||||
|
||||
---
|
||||
|
||||
## 9. Курсор: HiDPI / Retina (CrossOver)
|
||||
|
||||
**Проверено 2026-05-29** на M1 Pro + CrossOver 26.1, bottle **JA3-GOG**.
|
||||
|
||||
### Симптомы
|
||||
|
||||
| Симптом | Типичная причина |
|
||||
|---------|------------------|
|
||||
| Клик «выше/ниже» UI, курсор виден | Wine отдаёт logical pixels, игра рисует в другом масштабе |
|
||||
| **Курсор не опускается ниже середины экрана** | `RetinaMode=y` + `LogPixels=192` — координата Y делится пополам |
|
||||
|
||||
### Профили (`fix-ja3-crossover-mouse.sh`)
|
||||
|
||||
| Профиль | Реестр | Когда |
|
||||
|---------|--------|-------|
|
||||
| **`legacy`** (default) | `RetinaMode=n`, LogPixels/Win8Dpi **удалены** | **Канон** — полный ход курсора по Y; возможен небольшой сдвиг клика |
|
||||
| `retina-lite` | `RetinaMode=y`, без LogPixels | Если legacy смещает клик, но **нет** клипа на середине |
|
||||
| `retina` | `RetinaMode=y`, `LogPixels=192`, `Win8DpiScaling=1` | ❌ часто **клип Y на 50%** — не использовать как default |
|
||||
|
||||
```bash
|
||||
# Канон (после смены профиля — перезапуск JA3)
|
||||
bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
|
||||
# Если legacy смещает клик
|
||||
JA3_MOUSE_PROFILE=retina-lite bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
**Без перезапуска (workaround):** перетащите окно JA3 так, чтобы **верхний левый угол окна** совпал с **верхним левым углом экрана**.
|
||||
|
||||
CrossOver GUI: Bottle **JA3-GOG** → **High Resolution Mode** — при клипе на середине **выключить** (эквивалент **legacy**).
|
||||
|
||||
---
|
||||
|
||||
## 10. Быстрые команды (терминал)
|
||||
|
||||
| Действие | Команда |
|
||||
|----------|---------|
|
||||
| **Запуск** | `bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh` |
|
||||
| **Стоп / reset Wine** | `bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh` |
|
||||
| **Стоп всё GOG/Wine** | `bash ~/code/local_machine/scripts/games/gog/emergency-stop.sh` |
|
||||
| **Первичная настройка bottle** | `bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh` |
|
||||
| **Фикс мыши** | `bash ~/code/local_machine/scripts/games/gog/fix-ja3-crossover-mouse.sh` |
|
||||
| Swap «залип» 2–4 GB, Wine нет | просто `run-ja3-crossover.sh` (warn) |
|
||||
| Swap + Wine/JA3 живы ≥ 1200 MB | `reset-ja3-crossover.sh` → reboot |
|
||||
| Swap ≥ 4096 MB без Wine | reboot Mac |
|
||||
| Обход swap | `JA3_IGNORE_SWAP=1 bash …/run-ja3-crossover.sh` |
|
||||
| Профиль мыши | `JA3_MOUSE_PROFILE=legacy\|retina-lite\|retina` + fix + reset + run |
|
||||
|
||||
**Полный цикл перезапуска:**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/reset-ja3-crossover.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Ссылки
|
||||
|
||||
- [Gcenx — нет D3DMetal в packages](https://github.com/Gcenx/macOS_Wine_builds/issues/97)
|
||||
- [CrossOver — D3DMetal advanced settings](https://support.codeweavers.com/miscellanous/advanced-settings-in-crossover-mac-26)
|
||||
- [Whisky — GPU / GPTK tracking](https://github.com/Whisky-App/Whisky/issues/124)
|
||||
- [Proton JA3 — graphics / LocalStorage](https://github.com/ValveSoftware/Proton/issues/7039) (Linux; отражения Off/Low — аналог для Wine при других GPU)
|
||||
0
docs/games/README.md
Normal file
0
docs/games/README.md
Normal file
962
docs/games/TROUBLESHOOTING.md
Normal file
962
docs/games/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,962 @@
|
||||
# HoMM Olden Era — troubleshooting (Whisky / Wine)
|
||||
|
||||
- **Запуск приложений:** [WINE_APPS_LAUNCH_RUNBOOK.md](./WINE_APPS_LAUNCH_RUNBOOK.md)
|
||||
- **Подготовка Wine/GOG:** [WINE_MAC_RUNBOOK.md](./WINE_MAC_RUNBOOK.md)
|
||||
- **GOG web-stub (отдельный bottle):** [`../../scripts/games/gog/README.md`](../../scripts/games/gog/README.md)
|
||||
- **JA3 (GOG):** [JA3_RUNBOOK.md](./JA3_RUNBOOK.md)
|
||||
- **Мышь / Unity:** [UNITY_POINTER_MOUSE_FIX.md](./UNITY_POINTER_MOUSE_FIX.md)
|
||||
- **Скрипты:** [`../../scripts/games`](../../scripts/games)
|
||||
|
||||
Bottle HoMM/Steam: `38412122-AAE1-4831-9F2E-6A0EB364B3F9` · Steam App ID: `3105440`
|
||||
GOG web-stub: bottle **`GOG-Install`** (`scripts/games/gog/bottle.env`) — **не** HoMM UUID.
|
||||
|
||||
---
|
||||
|
||||
## Быстрая таблица
|
||||
|
||||
| Симптом | Первый шаг | Раздел |
|
||||
|---------|------------|--------|
|
||||
| Пустой UI Steam (кнопки без текста) | `fix-steam-ui` → `reset` → `steam-ui` | [§ Кривой UI Steam](#кривой-ui-steam-пустой-vgui) |
|
||||
| **Окно Steam не появляется** | `diagnose-steam-ui` → kill Mac Steam → `reset` → `steam-ui` | [§ Нет окна Steam](#нет-окна-steam-ui) |
|
||||
| Игра есть, **сейвы пустые** | `diagnose-saves` → login → `play` | [§ Сейвы](#игра-запустилась-сейвы-не-подтянулись) |
|
||||
| `play` висит, exe не стартует | `reset` → `steam-ui` → login → `play` | [§ Нет прогресса](#нет-прогресса-play-не-доходит-до-игры) |
|
||||
| Игра не стартует / D3D11 crash | **не** Play в Steam → `play` | [§ Запуск игры](#запуск-игры-канон) |
|
||||
| DirectX 11 | `dxvk` → `play` | [§ Графика](#графика-directx-11--failed-to-initialize) |
|
||||
| ЛКМ не работает | `diagnose` | [UNITY_POINTER_MOUSE_FIX.md](./UNITY_POINTER_MOUSE_FIX.md) |
|
||||
| **Connection Problem** в Steam | `osascript` quit Steam → `reset` → `steam-ui` → Retry / Offline | [§ Connection Problem](#connection-problem-steam-не-подключается-к-серверам) |
|
||||
| Steam завис / нет места | `disk` → `reset` | [§ Steam зависает](#steam-зависает--мало-места) |
|
||||
| Пустой текст / `FontFamily` / `winFont` | см. профиль: Steam → `dwrite`; GOG stub → `gdiplus` | [§ Шрифты — антипаттерны](#шрифты-антипаттерны-сводка) |
|
||||
| GOG stub: пустые кнопки / `FontFamily` | `gog/install-gdiplus` + `gog/install-corefonts` в **GOG-bottle** | [§ GOG web-stub](#gog-web-stub-отдельный-bottle) |
|
||||
| **GOG Galaxy: окна нет**, скрипт OK | offline installer JA3; Galaxy — best effort | [§ GOG Galaxy — окно](#gog-galaxy-окно-не-открывается) · [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) |
|
||||
| GOG Galaxy → **swap**, Mac «встал» | `gog/emergency-stop.sh` → перезагрузка | [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) § Swap |
|
||||
| GOG stub: «запустил, окна нет» | **Cmd+Tab** → `GOG_Galaxy_*.exe` / `wine64` | [§ GOG — проверка успеха](#gog-web-stub-отдельный-bottle) |
|
||||
| **JA3: Failed to initialize graphics subsystem** | CrossOver D3DMetal, не Whisky | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §2–4 |
|
||||
| **JA3: курсор не ниже середины / смещение клика** | профиль мыши **legacy** vs retina | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §9 |
|
||||
| **JA3 без Galaxy** | install → Whisky; **запуск → CrossOver** | [JA3_RUNBOOK.md](JA3_RUNBOOK.md) |
|
||||
| Установить через web-stub | только GalaxySetup (ломается в Wine) | [§ GOG web-stub](#gog-web-stub-отдельный-bottle) |
|
||||
| `fork failed`, Mac «встал», swap 20+ GB | `winedbg` / Steam assert loop → § Swap | [§ Swap и winedbg](#захламление-swap-и-утечка-winedbg) |
|
||||
| «20 GB съело» после GOG | обычно **не** stub — swap + дампы Steam | [§ Swap](#захламление-swap-и-утечка-winedbg) · [§ GOG](#gog-web-stub-отдельный-bottle) |
|
||||
|
||||
---
|
||||
|
||||
## Антипаттерны (не делать)
|
||||
|
||||
| ❌ Антипаттерн | Почему ломается | ✅ Вместо этого |
|
||||
|----------------|-----------------|-----------------|
|
||||
| **Play** у Olden Era **в окне Steam UI** | applaunch **без** `export_wine_env_game` → D3D11 / «Bad EXE format» в `Player.log` | Steam **Logged On** → **`start.sh play`** (Terminal) |
|
||||
| **`start.sh game`** / exe **без** Steam в bottle | `SteamApi_Init failed` · слоты пустые | Whisky → Steam → Logged On → **`play`** |
|
||||
| **`start.sh play`** при **Logged Off** | нет Steam API → пустые сейвы / отказ | `diagnose-saves` → login / Offline → `play` |
|
||||
| **`-applaunch 3105440`** до login | триггер CEF `message_window.cc` · UI мёртв | сначала **Logged On**, потом **`play`** |
|
||||
| **`HOMM_STEAM_VDESKTOP=1`** | CEF хуже · `Failed to set primary display settings` | **`steam-ui`** / Whisky Run (**не** VDESKTOP) |
|
||||
| **Terminal `steam-ui` + Whisky Run** параллельно | двойной Wine · зомби | **один** контур: Whisky **или** Terminal |
|
||||
| **`reset` без** `.exe`-зомби | `pkill` не ловит `C:\…\SteamService.exe` | **`reset`** (2026-05-27) или nuclear recovery |
|
||||
| Запуск с **`-silent`** (старые скрипты / ручной `Steam.exe -silent`) | `forcing desktopui via -silent` → VGUI + `winFont` → пустой диалог | `steam-ui` или `play` **без** `-silent` |
|
||||
| Только скопировать шрифты в `Fonts/` без `fix-steam-ui` | `winFont` остаётся — DWrite не отключён | **`bash start.sh fix-steam-ui`** (реестр + шрифты + dwrite override) |
|
||||
| Игра «открылась» = сейвы работают | FileManager может видеть папку `Steam_<ID>/saves`, но **Store** не инициализирован без Steam | Проверять `Player.log` и `diagnose-saves` |
|
||||
| Закрыть пустой диалог Steam крестиком | Steam остаётся **Logged Off**; следующий запуск снова без сейвов | **Правая** кнопка внизу или Tab → Enter |
|
||||
| Нативный **Mac Steam** + bottle | Connection Problem · `steamid=0` | **`killall steam_osx ipcserver`** → только Wine-Steam |
|
||||
| **`osascript quit Steam`** при отмене (-128) | Mac Steam жив | **Cmd+Q** / **`killall steam_osx`** |
|
||||
| Импорт bottle **isaacmarovitz**, Whisky **franke** пустой | другой контейнер | **`migrate-bottle-to-franke-whisky.sh`** |
|
||||
| Whisky «Launched steam.exe» = UI OK | нужен **steamwebhelper** (CEF) | `diagnose-steam-ui` · `cef_log` |
|
||||
| **`HOMM_GAME_FORCE=1`** каждый день | пустые слоты | только отладка графики |
|
||||
| Смотреть только `cache/*.tsv` | не диагностика bottle | `Steam/logs/*.txt`, **`Player.log`**, **`diagnose-*`** |
|
||||
| **GOG web-stub** в bottle **HoMM-OldenEra** | общий префикс со **Steam** → `winedbg`, дампы `Steam/dumps/`, шрифты из `Steam/resource/fonts`, `export_wine_env_steam` ломает .NET | **`scripts/games/gog/`** + новый bottle → `gog/use-bottle.sh` |
|
||||
| **`setup-wine-bottle.sh`** для GOG JA3 | скрипт для **Steam/HoMM**, не для GOG stub | `gog/setup-bottle.sh` |
|
||||
| **`start.sh reset`** после GOG в том же bottle | убивает **Steam** и игру; не лечит GOG-шрифты | `gog/reset.sh` в GOG-bottle; HoMM: отдельная сессия |
|
||||
| **`export_wine_env_steam`** / `dwrite=n,b` для GOG .NET installer | `FontFamilyNotFound` / пустой WinForms | `gog/install-gdiplus.sh` + `gog/run-web-installer.sh` (`gdiplus=n,b`) |
|
||||
| GOG stub **без** `winetricks gdiplus` | `GenericSansSerif` / unimplemented GDI+ | `bash …/gog/install-gdiplus.sh` в GOG-bottle |
|
||||
| Диагностика GOG через **`Steam/dumps`** | ложный след; stub **не** пишет туда | `/tmp/gog-web-installer-*.log`, `gog/diagnose-session.sh` |
|
||||
| Думать, что stub **скачал 20 GB** | stub ~500 KB; при `FontFamily` падает **до** загрузки игры | `df -h`; искать swap / `setup_galaxy.exe`×2 / `Steam/dumps` |
|
||||
| **`wineboot -k`** «на всякий случай» при каждом GOG | трогает **весь** префикс (Steam, CEF-кэш) | GOG-bottle: `gog/setup-bottle.sh --fonts-only`; HoMM: `reset` только когда нужен Steam |
|
||||
| Параллельно **isaac** + **franke** Whisky / старый `wineserver` | второй `wineserver`, сотни `winedbg`, swap под 100% | `pkill -9 winedbg`; `pkill wineserver`; один контейнер Whisky |
|
||||
| **`pkill -f GOG`** в HoMM-bottle при живом Steam | не связано со stub; мешает отладке | разнести bottle; GOG: `gog/reset.sh` |
|
||||
| Только `install-corefonts` **без** `install-gdiplus` | шрифты в `Fonts/` есть, но .NET падает на **GenericSansSerif** (Wine GDI+ stub) | **`gog/install-gdiplus.sh`** → `WINEDLLOVERRIDES=gdiplus=n,b` |
|
||||
| Debian URL для `arial32.exe` (404) | corefonts неполные | зеркало SourceForge в `gog/install-corefonts.sh` |
|
||||
| **Whisky → Fonts / winetricks corefonts** | зеркала Microsoft **404** ([Whisky #515](https://github.com/Whisky-App/Whisky/issues/515)) | macOS → `Fonts/` + наши скрипты; GOG: `gog/install-corefonts.sh` |
|
||||
| **`WINEDLLOVERRIDES=gdiplus=b`** для GOG **web-stub** | builtin gdiplus не даёт **GenericSansSerif** для .NET | **`gdiplus=n,b`** (`export_wine_env_gog`) |
|
||||
| **`--profile steam`** / `dwrite=n,b` для GOG .NET | другой стек, не GDI+ | `run-wine-exe.sh --profile gog` или `gog/run-web-installer.sh` |
|
||||
| Только **`winetricks liberation`** без macOS Fonts + реестр | частично; Steam/GOG UI всё ещё пустые | `setup-wine-bottle.sh` / `gog/setup-bottle.sh` + `fix-steam-ui` (Steam) |
|
||||
| Скопировать `.ttf` **без** `reg add …\Fonts` | GDI не видит файлы | `fix-steam-ui`, `gog/wine-fonts.sh` (`--fonts-only`) |
|
||||
| **`install-corefonts`** без **`cabextract`** | `.exe` MS fonts не распакуются | `brew install cabextract` |
|
||||
| **`install-gdiplus`** до **`setup-bottle`** | нет базовых шрифтов/реестра в bottle | порядок: `setup-bottle` → `install-gdiplus` → `install-corefonts` |
|
||||
| «26 файлов в Fonts» = stub OK | без **native gdiplus** .NET всё равно падает | маркер `.gog_gdiplus_installed` + нет `FontFamily` в логе |
|
||||
| Пустой **GalaxySetup** (красный X, gdi32) | **не** нехватка Arial — **Inno/botva2 + gdi32** | `gog/install-galaxy-extract.sh` + `launch-galaxy.sh`; см. [§ Setup gdi32](#setup-красный-x-пустое-окно-gdi32-rca-2026-05-28) |
|
||||
|
||||
Полная сводка по шрифтам и `WINEDLLOVERRIDES`: [§ Шрифты — антипаттерны](#шрифты-антипаттерны-сводка).
|
||||
|
||||
---
|
||||
|
||||
## Шрифты: антипаттерны (сводка)
|
||||
|
||||
Три **разных** механизма — не смешивать лечение:
|
||||
|
||||
| Контур | API / лог | Профиль DLL | Скрипты |
|
||||
|--------|-----------|-------------|---------|
|
||||
| **Steam VGUI** (login, опросы) | `winFont` в `console_log.txt` | **`dwrite=n,b`** + `DWriteEnable=0` | `fix-steam-ui`, `setup-wine-bottle.sh`, `steam-ui` |
|
||||
| **GOG web-stub** (.NET WinForms) | `FontFamilyNotFound`, `GenericSansSerif`, `GdipGetGenericFontFamilySansSerif` | **`gdiplus=n,b`** (native gdiplus) | `gog/install-gdiplus.sh`, `gog/install-corefonts.sh`, `gog/setup-bottle.sh` |
|
||||
| **GOG Galaxy** (CEF, login) | обычно **не** `FontFamily`; сеть/auth | `gdiplus=n,b` при запуске клиента | `gog/launch-galaxy.sh` |
|
||||
| **Inno + botva2** (`GalaxySetup.exe`) | `gdi32.dll` AV, пустой MsgBox | `gdiplus=b` **не спасает** | **не Wine-Inno** → `innoextract` / клиент Galaxy |
|
||||
|
||||
### Антипаттерны (шрифты и overrides)
|
||||
|
||||
| ❌ | Почему | ✅ |
|
||||
|----|--------|-----|
|
||||
| Копировать шрифты в `C:\windows\Fonts` **без** реестра `…\CurrentVersion\Fonts` | Wine/GDI не подхватывает файлы | `fix-steam-ui` / `gog/setup-bottle.sh` / `setup-wine-bottle.sh --fonts-only` |
|
||||
| Только файлы в `Fonts/`, **без** `DWriteEnable=0` (Steam) | DWrite-заглушка, пустой VGUI | **`start.sh fix-steam-ui`** |
|
||||
| Steam **без** `WINEDLLOVERRIDES=dwrite=n,b` при запуске | подгружается сломанный `dwrite.dll` | `export_wine_env_steam` в `config.sh` → `steam-ui` / `play` |
|
||||
| GOG stub **без** winetricks **gdiplus** | Wine GDI+ stub → **GenericSansSerif** | **`gog/install-gdiplus.sh`** (~900 MB) |
|
||||
| GOG stub с **`gdiplus=b`** (только builtin) | нет native **GenericSansSerif** | **`gdiplus=n,b`** |
|
||||
| GOG stub с **`dwrite=n,b`** (профиль Steam) | не тот стек для .NET WinForms | **`export_wine_env_gog`** |
|
||||
| Только **`install-corefonts`**, без **gdiplus** | `.ttf` есть, лог всё равно `FontFamilyNotFound` | оба скрипта + порядок ниже |
|
||||
| **`setup-wine-bottle.sh`** для GOG web-stub | DWrite/Steam-реестр, **нет** gdiplus | **`gog/setup-bottle.sh`** |
|
||||
| **`setup-gog-bottle.sh`** (HoMM) для JA3 | устаревшая обёртка | **`gog/setup-bottle.sh`** |
|
||||
| **winetricks corefonts** из Whisky GUI | 404 на CDN Microsoft | SourceForge в **`gog/install-corefonts.sh`**; macOS Fonts |
|
||||
| **Debian-only** URL `msttcorefonts` | 404 | primary: `downloads.sourceforge.net/corefonts/…` |
|
||||
| **`winetricks liberation`** как единственный шаг | мало гарантий для VGUI/.NET | macOS + corefonts + профиль DLL |
|
||||
| **`WINE_SETUP_WINETRICKS=1`** вместо наших скриптов | долго, зеркала падают | `setup-wine-bottle.sh` (macOS Fonts); GOG: `gog/*` |
|
||||
| **`wineboot -k`** «обновить шрифты» в HoMM-bottle | сбрасывает Steam/CEF | GOG: `gog/setup-bottle.sh --fonts-only`; HoMM: `--fonts-only` без `-k` |
|
||||
| GOG в **HoMM-bottle** «шрифты уже есть от Steam» | **`export_wine_env_steam`**, чужие `Steam/resource/fonts` | отдельный bottle **`GOG-Install`** |
|
||||
| Считать **GalaxySetup** пустым окном проблемой **шрифтов** | это **gdi32 + botva2**, не Arial | [§ Setup gdi32](#setup-красный-x-пустое-окно-gdi32-rca-2026-05-28) |
|
||||
| **`run-wine-exe.sh --profile plain`** для .NET installer | нет overrides | **`--profile gog`** или **`steam`** по таблице выше |
|
||||
| Запуск stub **без** проверки лога | «OK» в shell, процесс уже умер | `grep -i FontFamily /tmp/gog-web-installer-*.log` |
|
||||
| **`launch-galaxy.sh` OK**, окна нет | Second client / lock | `gog/reset.sh` → снова launch; [§ GOG Galaxy — окно](#gog-galaxy-окно-не-открывается) |
|
||||
| Повторный launch без **`reset.sh`** | `Second client instance` → exit | `reset` убивает процессы и `lock-files/*.lock` |
|
||||
|
||||
### Порядок (GOG-bottle, web-stub)
|
||||
|
||||
```bash
|
||||
bash …/gog/setup-bottle.sh # macOS Fonts + реестр + сеть
|
||||
bash …/gog/install-gdiplus.sh # native gdiplus, gdiplus=n,b
|
||||
brew install cabextract # для corefonts
|
||||
bash …/gog/install-corefonts.sh # arial32 / times32 / andale32
|
||||
bash …/gog/run-web-installer.sh …/GOG_Galaxy_*.exe
|
||||
```
|
||||
|
||||
Маркеры: `.gog_gdiplus_installed`, `.gog_corefonts_installed`, `.gog_setup_v1`.
|
||||
|
||||
### Порядок (HoMM-bottle, Steam VGUI)
|
||||
|
||||
```bash
|
||||
bash …/setup-wine-bottle.sh # один раз
|
||||
bash …/start.sh fix-steam-ui # DWriteEnable=0 + шрифты
|
||||
bash …/start.sh reset && steam-ui # перезапуск с dwrite=n,b
|
||||
```
|
||||
|
||||
### Проверка
|
||||
|
||||
```bash
|
||||
# Steam
|
||||
grep winFont …/Steam/logs/console_log.txt | tail -5
|
||||
|
||||
# GOG stub
|
||||
grep -i FontFamily /tmp/gog-web-installer-GOG-Install.log || echo "OK: нет FontFamily"
|
||||
|
||||
# Сколько файлов в Fonts (не доказательство gdiplus!)
|
||||
ls …/drive_c/windows/Fonts | wc -l
|
||||
test -f …/Bottles/<GOG-UUID>/.gog_gdiplus_installed && echo "gdiplus marker OK"
|
||||
```
|
||||
|
||||
Детали: [§ Кривой UI Steam](#кривой-ui-steam-пустой-vgui) · [§ GOG web-stub](#gog-web-stub-отдельный-bottle) · [WINE_MAC_RUNBOOK.md §2](./WINE_MAC_RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
### Канонический контур (GOG web-stub / JA3)
|
||||
|
||||
**Проверено 2026-05-28:** stub `GOG_Galaxy_Jagged_Alliance_3.exe` в bottle **`GOG-Install`** — окно с подписями, скачивание `GalaxySetup.exe` (~99 MB).
|
||||
|
||||
```bash
|
||||
# один раз (создаёт bottle, gdiplus ~900 MB, шрифты, запускает stub):
|
||||
bash ~/code/local_machine/scripts/games/gog/install-all.sh \
|
||||
~/path/to/GOG_Galaxy_Jagged_Alliance_3.exe
|
||||
|
||||
# или по шагам — см. scripts/games/gog/README.md
|
||||
```
|
||||
|
||||
После stub: **Cmd+Tab** → установщик → дождаться JA3 (**≥20 GB** на диске).
|
||||
|
||||
### Канонический контур (HoMM / Steam)
|
||||
|
||||
1. **`killall steam_osx ipcserver`** (если был Mac Steam)
|
||||
2. **Whisky** → **HoMM-OldenEra** → **Run** → **Logged On** (или Offline)
|
||||
3. **`bash ~/code/local_machine/scripts/games/start.sh play`**
|
||||
4. В игре: **Загрузить** → skirmish → **1 / 2 / 3**
|
||||
|
||||
**Recovery CEF/UI:**
|
||||
|
||||
```bash
|
||||
osascript -e 'quit app "Whisky"' 2>/dev/null; sleep 2
|
||||
pkill -9 -f 'SteamService.exe|steamwebhelper|38412122|Steam.exe' 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
open -a Whisky
|
||||
# Run Steam только из Whisky — не steam-ui параллельно
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Кривой UI Steam (пустой VGUI)
|
||||
|
||||
**Кратко:** Steam в Wine рисует кнопки/radio, но **без текста** → login/applaunch зависают. Лечится **один раз** `fix-steam-ui`, затем всегда запуск через **`steam-ui`** (не `-silent`).
|
||||
|
||||
### Симптомы
|
||||
|
||||
- Тёмное окно: **5 radio слева**, **2 кнопки внизу**, подписей нет.
|
||||
- Иногда после частичного фикса: **Connection Problem** / login **с текстом** — это уже другой слой (сеть), VGUI-фикс сработал.
|
||||
- Это **Steam VGUI**, не Heroes Olden Era.
|
||||
|
||||
Лог `…/Steam/logs/console_log.txt`:
|
||||
|
||||
```text
|
||||
src\vgui2\src\surface_gdiwin32.cpp (1336) : winFont
|
||||
```
|
||||
|
||||
(много строк подряд = шрифты/DWrite всё ещё ломают отрисовку)
|
||||
|
||||
### Причина
|
||||
|
||||
| Слой | Что ломается |
|
||||
|------|----------------|
|
||||
| **DirectWrite** | Steam на Windows ждёт DWrite; в WhiskyWine заглушка → GDI не рисует глифы |
|
||||
| **GDI / winFont** | VGUI-диалоги (опрос, interstitial, часть login) остаются «пустыми» |
|
||||
| **`-silent`** | Форсит `desktopui`/VGUI — усугубляет проблему |
|
||||
|
||||
Только копировать шрифты в `Fonts/` **без** `DWriteEnable=0` и **без** `dwrite=n,b` — **недостаточно** → [§ Шрифты — антипаттерны](#шрифты-антипаттерны-сводка).
|
||||
|
||||
---
|
||||
|
||||
### Решение (канон, один раз + перезапуск)
|
||||
|
||||
```bash
|
||||
osascript -e 'quit app "Steam"' 2>/dev/null || true
|
||||
bash ~/code/local_machine/scripts/games/start.sh fix-steam-ui
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
```
|
||||
|
||||
#### Шаг A — `fix-steam-ui` (скрипт `fix-homm-steam-ui.sh`)
|
||||
|
||||
| # | Действие | Зачем |
|
||||
|---|----------|--------|
|
||||
| 1 | `HKCU\Software\Valve\Steam` → **`DWriteEnable = 0`** | Steam не использует сломанный DWrite в Wine |
|
||||
| 2 | Копия **Arial, Tahoma, Verdana, Times, Courier, Marlett, GoNoto** → `drive_c/windows/Fonts/` | GDI находит базовые шрифты |
|
||||
| 3 | При каждом `steam-ui` / `play`: **`WINEDLLOVERRIDES=dwrite=n,b`** (`config.sh` → `export_wine_env_steam`) | Wine не подгружает native dwrite.dll |
|
||||
|
||||
Повторять `fix-steam-ui` нужно только после **нового bottle** или если вручную сбросили реестр Wine.
|
||||
|
||||
#### Шаг B — `reset`
|
||||
|
||||
Обязателен после фикса: старый `Steam.exe` стартовал **без** новых overrides и с кэшем VGUI.
|
||||
|
||||
#### Шаг C — `steam-ui`
|
||||
|
||||
| ✅ Правильно | ❌ Неправильно |
|
||||
|-------------|----------------|
|
||||
| `bash ~/code/local_machine/scripts/games/start.sh steam-ui` | `Steam.exe -silent` |
|
||||
| Whisky → Run Steam → **`start.sh play`** | **Play** у Olden Era в Steam UI (нет DXVK env) |
|
||||
| `start.sh play` при **Logged On** | `play` / `game` при **Logged Off** |
|
||||
| `start.sh game` (= то же что `play` при login) | `HOMM_GAME_FORCE=1` для сейвов |
|
||||
|
||||
---
|
||||
|
||||
### Проверка, что UI починился
|
||||
|
||||
1. **Визуально:** login / **Connection Problem** / библиотека — **есть читаемый текст** (не пустые прямоугольники).
|
||||
2. **Лог:** после нового запуска в `console_log.txt` **нет** новой пачки `winFont` при открытии того же диалога.
|
||||
3. **Login:** в `connection_log.txt` eventually `Logged On` или осмысленный **Retry** / **Offline Mode** (см. [§ Connection Problem](#connection-problem-steam-не-подключается-к-серверам)).
|
||||
|
||||
```bash
|
||||
tail -20 ~/Library/Containers/com.franke.Whisky/Bottles/38412122-*/drive_c/Program\ Files\ \(x86\)/Steam/logs/console_log.txt | grep winFont || echo "winFont нет — хорошо"
|
||||
```
|
||||
|
||||
*(Если bottle ещё в `com.isaacmarovitz.Whisky` — замените префикс контейнера.)*
|
||||
|
||||
---
|
||||
|
||||
### Если текст **всё ещё** пустой (обход до перезапуска)
|
||||
|
||||
Диалог **вслепую** (опрос GPU / interstitial **до** login):
|
||||
|
||||
1. Верхний radio — оставить (дефолт).
|
||||
2. **Правая** кнопка внизу → OK / Continue / Launch.
|
||||
3. Или: `Tab` несколько раз → `Enter`.
|
||||
|
||||
Затем снова **`reset` + `fix-steam-ui` + `steam-ui`** — часто VGUI остаётся пустым, пока висит процесс от старой сессии.
|
||||
|
||||
---
|
||||
|
||||
### После починки UI → сейвы
|
||||
|
||||
Пустой UI и **Connection Problem** — разные проблемы. Когда текст появился, но сейвы пустые → [§ Сейвы](#игра-запустилась-сейвы-не-подтянулись).
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Не путать окна
|
||||
|
||||
| Окно | Признак |
|
||||
|------|---------|
|
||||
| **Steam VGUI (кривой)** | Серый flat UI, 5 radio, 2 кнопки **без** текста |
|
||||
| **Steam CEF (норм)** | Login, Connection Problem, библиотека — **с текстом** |
|
||||
| **Heroes Olden Era** | 3D, Unfrozen, меню игры |
|
||||
|
||||
---
|
||||
|
||||
## Нет окна Steam UI
|
||||
|
||||
### Whisky пустой / UI не появляется из Terminal
|
||||
|
||||
Bottle мог быть в **старом** контейнере `isaacmarovitz` — **Whisky 3 (franke) его не видит** → окна нет.
|
||||
|
||||
**Один раз:**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/migrate-bottle-to-franke-whisky.sh
|
||||
```
|
||||
|
||||
Затем:
|
||||
|
||||
1. **Открыть Whisky.app** — в списке должен быть **HoMM-OldenEra**.
|
||||
2. В bottle: **Run** → `C:\Program Files (x86)\Steam\Steam.exe` (или закреплённый Steam).
|
||||
3. Либо Terminal (если окно появилось):
|
||||
|
||||
```bash
|
||||
killall steam_osx ipcserver 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
```
|
||||
|
||||
Диагностика:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-steam-ui
|
||||
```
|
||||
|
||||
### Симптом
|
||||
|
||||
`bash …/start.sh steam-ui` — в Terminal пара строк и **нет окна**; или окно не найти.
|
||||
|
||||
### Диагностика
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-steam-ui
|
||||
```
|
||||
|
||||
| Строка | Плохо | Что делать |
|
||||
|--------|-------|------------|
|
||||
| Mac Steam | ❌ steam_osx | `killall steam_osx ipcserver` (см. § Connection Problem) |
|
||||
| Steam.exe | ❌ | `reset` → `steam-ui` снова |
|
||||
| steamwebhelper | ❌ | CEF не поднялся — см. ниже |
|
||||
| winFont | ⚠️ | `fix-steam-ui` |
|
||||
|
||||
### Запуск (копипаст по порядку)
|
||||
|
||||
```bash
|
||||
killall steam_osx ipcserver 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh fix-steam-ui
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
```
|
||||
|
||||
- Terminal **зависнет** на `steam-ui` — так и должно быть, Steam работает.
|
||||
- Окно искать: **Cmd+Tab** (wine64), **Mission Control** — не в Whisky.
|
||||
- Если `diagnose-steam-ui` показывает ❌ steamwebhelper — **CEF не поднялся** (см. [§ CEF / steamwebhelper](#cef--steamwebhelper-пустой-ui-после-update)):
|
||||
|
||||
```bash
|
||||
osascript -e 'quit app "Whisky"' 2>/dev/null; sleep 2
|
||||
pkill -9 -f 'SteamService.exe|steamwebhelper|38412122|Steam.exe' 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
open -a Whisky
|
||||
# HoMM-OldenEra → Run Steam (не Terminal steam-ui параллельно)
|
||||
```
|
||||
|
||||
❌ **Не использовать** `HOMM_STEAM_VDESKTOP=1` — ухудшает CEF (`Failed to set primary display settings`).
|
||||
|
||||
- Тёмное окно **без текста** — [§ Кривой UI Steam](#кривой-ui-steam-пустой-vgui): Tab → Enter или правая кнопка.
|
||||
|
||||
---
|
||||
|
||||
## CEF / steamwebhelper (пустой UI после update)
|
||||
|
||||
### Симптомы
|
||||
|
||||
- Whisky пишет «Launched: steam.exe», но **нет библиотеки / login** (чёрное окно или только VGUI).
|
||||
- `diagnose-steam-ui`: ❌ **steamwebhelper**.
|
||||
- `cef_log.txt`:
|
||||
|
||||
```text
|
||||
message_window.cc: Failed to create a message-only window: Class does not exist
|
||||
```
|
||||
|
||||
→ **crash loop** steamwebhelper → нет CEF UI.
|
||||
|
||||
### Типичные триггеры (антипаттерны)
|
||||
|
||||
1. **`Steam.exe -applaunch 3105440`** при **Logged Off** (первый applaunch без UI).
|
||||
2. Параллельный запуск **Terminal + Whisky**.
|
||||
3. Зомби **`SteamService.exe` / `steamwebhelper`** после `reset` со старыми `pkill`-паттернами.
|
||||
4. Авто-update Steam client (утренний build ~242 MB) + грязная сессия.
|
||||
|
||||
### Recovery (единственный надёжный порядок)
|
||||
|
||||
```bash
|
||||
killall steam_osx ipcserver 2>/dev/null
|
||||
osascript -e 'quit app "Whisky"' 2>/dev/null; sleep 2
|
||||
pkill -9 -f 'SteamService.exe|steamwebhelper|38412122|Steam.exe' 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
pgrep -lf Steam || echo "OK: процессов Steam нет"
|
||||
open -a Whisky
|
||||
```
|
||||
|
||||
В Whisky: **HoMM-OldenEra** → **Run** на `Steam.exe` — **не** `steam-ui` из Terminal одновременно.
|
||||
|
||||
Если после чистой сессии CEF всё ещё мёртв:
|
||||
|
||||
- Заблокировать авто-update: `steam.cfg` с `BootStrapperInhibitAll=1` в папке Steam.
|
||||
- Откат Steam client на предыдущий build (операторский скрипт `downgrade-steam-client.sh` — по запросу).
|
||||
|
||||
Проверка login:
|
||||
|
||||
```bash
|
||||
tail -3 ~/Library/Containers/com.franke.Whisky/Bottles/38412122-*/drive_c/Program\ Files\ \(x86\)/Steam/logs/connection_log.txt
|
||||
```
|
||||
|
||||
Нужна строка **`Logged On`**, не `Logged Off`.
|
||||
|
||||
---
|
||||
|
||||
## Игра запустилась — сейвы не подтянулись
|
||||
|
||||
- Окно Heroes открывается, меню **Загрузить** пустое.
|
||||
- На Mac файлы **есть**: `1.saveskirmish`, `2.saveskirmish`, `3.saveskirmish`.
|
||||
- В `Player.log`:
|
||||
|
||||
```text
|
||||
SteamApi_Init failed with FailedGeneric - error: ConnectToGlobalUser failed.
|
||||
Store state changed to Failed
|
||||
```
|
||||
|
||||
### Причина (корень)
|
||||
|
||||
Сейвы лежат в `users/Steam_<ID>/saves/singleplayer/`, но **слоты в UI** требуют живый **Steam-клиент в том же bottle** (`Logged On`). Прямой exe обходит Steam → Store не инициализируется.
|
||||
|
||||
### Диагностика одной командой
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
```
|
||||
|
||||
| Проверка | OK | Плохо |
|
||||
|----------|-----|-------|
|
||||
| Файлы `*.saveskirmish` | 3 файла в singleplayer | папка пустая |
|
||||
| `connection_log.txt` | `[Logged On, …]` | `[Logged Off, 0, 0]` |
|
||||
| `Player.log` | нет `SteamApi_Init failed` | `ConnectToGlobalUser failed` |
|
||||
|
||||
### Рабочий цикл (сейвы 1 / 2 / 3)
|
||||
|
||||
1. Steam в bottle: **Logged On** (Whisky → Run или `steam-ui`).
|
||||
2. **Не** жмите Play в Steam UI.
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
```
|
||||
|
||||
В игре: **Загрузить** → **skirmish / одиночная** (не кампания) → слоты **1**, **2**, **3**.
|
||||
|
||||
Путь к файлам:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh saves
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Запуск игры (канон)
|
||||
|
||||
| Шаг | Действие |
|
||||
|-----|----------|
|
||||
| 1 | Mac Steam **выключен** (`pgrep -x Steam` → пусто) |
|
||||
| 2 | Wine-Steam **Logged On** (Whisky → Run или `steam-ui` → Retry / Offline) |
|
||||
| 3 | **`bash ~/code/local_machine/scripts/games/start.sh play`** — **не** кнопка Play в Steam |
|
||||
|
||||
`play` при **Logged On** вызывает **`game`** (DXVK-macOS + Unity windowed). Это тот же контур, что работал «утром».
|
||||
|
||||
### Если игра падает на D3D11
|
||||
|
||||
`Player.log`: `GfxDevice: creating device client` / `d3d11.dll` — вы запустили через **Steam UI Play**, не через `play`.
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
```
|
||||
|
||||
Альтернатива (явный exe, Steam не закрывать):
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh game
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Нет прогресса: `play` не доходит до игры
|
||||
|
||||
### Чеклист по логам
|
||||
|
||||
| Проверка | Где | Плохо | Хорошо |
|
||||
|----------|-----|-------|--------|
|
||||
| Steam login | `connection_log.txt` | `Logged Off`, `steamid=0` | `Logged On` |
|
||||
| applaunch | `console_log.txt` | нет `GameAction` / `Game process added` | `AppID 3105440 … HeroesOldenEra.exe` |
|
||||
| Steam API | `Player.log` | `SteamApi_Init failed` | нет ошибки Store |
|
||||
|
||||
### Рабочий цикл
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh fix-steam-ui
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
# login / Offline Mode → затем play (не Play в Steam UI)
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Захламление swap и утечка winedbg
|
||||
|
||||
### Симптомы
|
||||
|
||||
| Признак | Что это |
|
||||
|---------|---------|
|
||||
| Terminal: **`fork failed: resource temporarily unavailable`** | ядро не создаёт процессы — часто **swap исчерпан** |
|
||||
| `sysctl vm.swapusage` → **used ≈ total** (например 22/23 GB) | macOS вынесла память в swap; система «тяжёлая» |
|
||||
| **`pgrep winedbg \| wc -l`** → сотни / тысячи | отладчики Wine после **assert/crash Steam** (не от GOG stub) |
|
||||
| `wineserver` из **`com.isaacmarovitz.Whisky`** при bottle во **franke** | legacy-процесс, лишний CPU |
|
||||
| Диск **98%**, «пропало 20 GB» | чаще **не одна** операция, а swap + дубликаты файлов (см. ниже) |
|
||||
|
||||
GOG web-stub **сам по себе** почти не ест RAM: при `FontFamilyNotFound` процесс **сразу выходит**; загрузка JA3 **не начинается**.
|
||||
|
||||
### Типичная цепочка (антипаттерн)
|
||||
|
||||
1. Bottle **HoMM-OldenEra** (Steam + HoMM + эксперименты с GOG в том же префиксе).
|
||||
2. Steam CEF / VGUI в цикле → `Steam/dumps/assert_steam.exe_*.dmp` (сотни MB).
|
||||
3. На каждый assert Wine поднимает **`winedbg --auto`** → сотни процессов → **swap**.
|
||||
4. Параллельно Cursor, Zoom, GOG Galaxy CEF → усиление.
|
||||
5. Любой `bash` / `brew` → **`fork failed`**.
|
||||
|
||||
Это **не** «JA3 скачал 20 GB», а **давление памяти + мусор на диске**.
|
||||
|
||||
### Что реально занимает диск (проверка)
|
||||
|
||||
```bash
|
||||
# swap
|
||||
sysctl vm.swapusage
|
||||
|
||||
# утечка процессов
|
||||
pgrep winedbg | wc -l
|
||||
pgrep -lf wineserver
|
||||
|
||||
# HoMM-bottle (если GOG гоняли там — будет мусор)
|
||||
BOTTLE=~/Library/Containers/com.franke.Whisky/Bottles/38412122-AAE1-4831-9F2E-6A0EB364B3F9
|
||||
du -sh "$BOTTLE/drive_c/Program Files (x86)/Steam/dumps" 2>/dev/null
|
||||
du -sh "$BOTTLE/drive_c/setup_galaxy.exe" 2>/dev/null # дубликат ~277M — можно rm
|
||||
du -sh ~/code/local_machine/scripts/games/cache/setup_galaxy.exe 2>/dev/null
|
||||
```
|
||||
|
||||
| Путь | Ориентир | Действие |
|
||||
|------|----------|----------|
|
||||
| `Steam/dumps/*.dmp` | до **~400 MB** | удалить старые `.dmp` (Steam пересоздаст при новом crash) |
|
||||
| `drive_c/setup_galaxy.exe` | **~277 MB** | `rm` если есть копия в `scripts/games/cache/` |
|
||||
| `cache/setup_galaxy.exe` | **~277 MB** | оставить один экземпляр |
|
||||
| Swap **used** | десятки GB | см. экстренный сброс ниже |
|
||||
|
||||
### Экстренный сброс (копипаст)
|
||||
|
||||
```bash
|
||||
# 1) Убить зомби-отладчики (безопасно для данных bottle)
|
||||
pkill -9 winedbg 2>/dev/null
|
||||
pkill -9 wineserver 2>/dev/null
|
||||
|
||||
# 2) HoMM/Steam bottle — только если не нужен сейчас живой Steam
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
|
||||
# 3) Дампы Steam (опционально, освобождает сотни MB)
|
||||
rm -f ~/Library/Containers/com.franke.Whisky/Bottles/38412122-*/drive_c/Program\ Files\ \(x86\)/Steam/dumps/*.dmp
|
||||
|
||||
# 4) Дубликат установщика GOG в drive_c (если копировали в HoMM-bottle)
|
||||
rm -f ~/Library/Containers/com.franke.Whisky/Bottles/38412122-*/drive_c/setup_galaxy.exe
|
||||
```
|
||||
|
||||
После этого: **перезагрузка Mac** — самый надёжный способ **освободить swap** (macOS не отдаёт swap мгновенно только kill процессов).
|
||||
|
||||
Профилактика:
|
||||
|
||||
- **Не** гонять GOG web-stub в HoMM-bottle → [§ GOG web-stub](#gog-web-stub-отдельный-bottle).
|
||||
- После серии падений Steam: `reset` + очистка `Steam/dumps`.
|
||||
- Держать **≥15–20 GB свободно** на Data перед установкой игр (не путать с «занятым swap»).
|
||||
|
||||
---
|
||||
|
||||
## GOG web-stub (отдельный bottle)
|
||||
|
||||
Файл `GOG_Galaxy_*.exe` (~500 KB) — загрузчик полного установщика с gog.com, **не** игра. Скачивает `GalaxySetup.exe` и ставит игру через GOG installer в Wine.
|
||||
|
||||
**Не путать с:** GOG Galaxy 2.0 (лаунчер в HoMM-bottle, `start.sh gog`, логин через CEF) — к web-stub **не** относится.
|
||||
|
||||
### Что сработало (RCA 2026-05-28, JA3)
|
||||
|
||||
| Попытка | Результат |
|
||||
|---------|-----------|
|
||||
| HoMM-bottle + `setup-wine-bottle.sh` + stub | `FontFamilyNotFound` / пустые кнопки; в логе **FATAL** `GenericSansSerif` |
|
||||
| Тот же bottle + 26 шрифтов (macOS + corefonts) **без gdiplus** | снова `FontFamilyNotFound` — файлы есть, Wine **не отдаёт** generic font family |
|
||||
| Bottle **`GOG-Install`** + `install-gdiplus.sh` + `install-corefonts.sh` + stub | **OK:** процесс жив, подписи в UI, в `Temp/GalaxyInstaller_*/GalaxySetup.exe` **~99 MB** |
|
||||
|
||||
**Корневая причина:** .NET WinForms вызывает `GdipGetGenericFontFamilySansSerif` → без **native `gdiplus.dll`** (winetricks) Wine отвечает `FontFamilyNotFound`, даже при полном `windows/Fonts`.
|
||||
|
||||
**Обязательный порядок в GOG-bottle:**
|
||||
|
||||
1. `gog/create-bottle.sh` или `gog/use-bottle.sh <UUID>` → `bottle.env`
|
||||
2. `gog/setup-bottle.sh` — шрифты macOS + реестр + сеть Wine
|
||||
3. **`gog/install-gdiplus.sh`** — winetricks, ~900 MB скачивание, `gdiplus=n,b`
|
||||
4. `gog/install-corefonts.sh` — SourceForge `arial32` / `times32` / `andale32`
|
||||
5. `gog/run-web-installer.sh` путь к `GOG_Galaxy_*.exe`
|
||||
|
||||
Одной командой: `bash …/gog/install-all.sh /path/GOG_Galaxy_*.exe`
|
||||
|
||||
### Проверка успеха (после запуска stub)
|
||||
|
||||
```bash
|
||||
# процесс
|
||||
pgrep -fl 'GOG_Galaxy_Jagged'
|
||||
|
||||
# лог без FontFamily (имя bottle в пути)
|
||||
grep -i FontFamily /tmp/gog-web-installer-GOG-Install.log || echo "OK: нет FontFamily"
|
||||
|
||||
# скачан полный setup (путь меняется: GalaxyInstaller_<random>)
|
||||
find ~/Library/Containers/com.franke.Whisky/Bottles -path '*/GalaxyInstaller_*/GalaxySetup.exe' -ls 2>/dev/null
|
||||
```
|
||||
|
||||
| Признак | Значение |
|
||||
|---------|----------|
|
||||
| Лог | `/tmp/gog-web-installer-GOG-Install.log` — нет `FontFamily could not be found` |
|
||||
| Temp | `…/users/eternal/AppData/Local/Temp/GalaxyInstaller_*/GalaxySetup.exe` растёт (~100 MB+) |
|
||||
| Окно | **Cmd+Tab** → `GOG_Galaxy_Jagged_Alliance_3.exe` (не в окне Whisky) |
|
||||
| Bottle | только `gog/bottle.env` — **не** смотреть `Steam/dumps` в HoMM-bottle |
|
||||
|
||||
### Антипаттерны (кратко)
|
||||
|
||||
Полный список по шрифтам: [§ Шрифты — антипаттерны](#шрифты-антипаттерны-сводка).
|
||||
|
||||
| ❌ | ✅ |
|
||||
|----|-----|
|
||||
| HoMM-bottle + `setup-wine-bottle.sh` + stub | `gog/install-all.sh` или `gog/README.md` |
|
||||
| Только corefonts, **без** gdiplus | `setup-bottle` → **`install-gdiplus`** → `install-corefonts` |
|
||||
| `dwrite=n,b` или `gdiplus=b` для stub | **`gdiplus=n,b`** (`export_wine_env_gog`) |
|
||||
| GalaxySetup пустой X = «добавить шрифты» | **gdi32/Inno** → `install-galaxy-extract` + `launch-galaxy` |
|
||||
| Смотреть `Steam/dumps` | `/tmp/gog-web-installer-*.log`, `gog/diagnose-session.sh` |
|
||||
| `start.sh reset` для GOG | `bash …/gog/reset.sh` |
|
||||
| 20 GB «съел stub» при `FontFamily` в логе | падение **до** загрузки игры — [§ Swap](#захламление-swap-и-утечка-winedbg) |
|
||||
|
||||
### Симптомы и фикс
|
||||
|
||||
| Симптом | Причина | Действие |
|
||||
|---------|---------|----------|
|
||||
| Пустые radio/кнопки, окно есть | нет corefonts **или** нет gdiplus | `install-gdiplus.sh` **и** `install-corefonts.sh` |
|
||||
| `FontFamilyNotFound` / `GenericSansSerif` | нет native **gdiplus** (типично) | `bash …/gog/install-gdiplus.sh` → снова stub |
|
||||
| Скрипт пишет OK, окна нет | см. [§ GOG Galaxy — окно](#gog-galaxy-окно-не-открывается) | `reset.sh` → `launch-galaxy.sh` |
|
||||
| «Installer Already Running» | второй stub | `gog/reset.sh` |
|
||||
| Нет `bottle.env` | GOG-bottle не создан | `gog/create-bottle.sh` или `gog/use-bottle.sh <UUID>` |
|
||||
| Stub завис на загрузке | мало места на Data | `df -h` → **≥20 GB** свободно |
|
||||
|
||||
### GOG Galaxy: окно не открывается
|
||||
|
||||
**RCA за 7 часов (2026-05-28):** [GOG_GALAXY_7H_RCA.md](GOG_GALAXY_7H_RCA.md) — хронология запусков, что **подтверждено в логах** (процесс + login form), и что **не** подтверждено (видимое окно на macOS).
|
||||
|
||||
Собрать логи: `bash …/gog/collect-gog-logs-7h.sh` → `temp/gog-logs-7h-*`.
|
||||
|
||||
`launch-galaxy.sh` пишет OK, а окна нет — чаще всего **не «нет приложения»**, а **CEF/HWND в Wine** или второй экземпляр убил новое окно.
|
||||
|
||||
| Лог (`GalaxyInitialization.log`) | Что значит |
|
||||
|----------------------------------|------------|
|
||||
| `Second client instance detected` → `The client will exit` | висел старый `GalaxyClient` / lock; **новое окно не создаётся** |
|
||||
| `will continue` + в `ps` есть `GalaxyClient.exe` | клиент **жив** — **Cmd+Tab** → `GalaxyClient` / `wine64` |
|
||||
| `Failed to locate Galaxy Client window` | restore не нашёл окно (часто после «Second client») |
|
||||
| `DeelevateStrategy` / `Privilege de-elevation` / exit **-31** | нет `/deelevated` или сервис 1053 | `launch-galaxy.sh` (уже передаёт `/runWithoutUpdating /deelevated`) |
|
||||
|
||||
**Канон (2026-05-28):**
|
||||
|
||||
```bash
|
||||
# 1) Whisky GUI (лучше для окна на Mac):
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy-whisky.sh
|
||||
# → pin «gog-galaxy» → Run
|
||||
|
||||
# 2) Терминал (процесс; окно может не появиться):
|
||||
bash ~/code/local_machine/scripts/games/gog/reset.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy.sh
|
||||
|
||||
# 3) Если окна нет — foreground + vdesktop:
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy-ui.sh
|
||||
```
|
||||
|
||||
В `GalaxyClient.log` бывает `Successfully brought window to the foreground`, а на Mac пусто — CEF в `cef.log`: `Received message from source not matching parent window` (HWND родителя в Wine). **Виртуальный desktop** (`explorer /desktop=GOGGalaxy,1280x800`) даёт отдельное окно Wine; **foreground** (`launch-galaxy-ui.sh`) привязывает его к Terminal.
|
||||
|
||||
RCA: `bash …/gog/diagnose-galaxy-window.sh`
|
||||
|
||||
Один раз: `bash …/gog/pin-galaxy-whisky.sh` → Whisky → bottle **GOG-Install** → pin **gog-galaxy** → Run.
|
||||
|
||||
A/B CEF: `GOG_CEF_PROFILE=steam bash …/launch-galaxy-ui.sh` (добавляет `-cef-in-process-gpu`, как Steam).
|
||||
|
||||
| ❌ Антипаттерн | ✅ |
|
||||
|----|-----|
|
||||
| `reset.sh` старый (не убивал `GalaxyClient.exe`) | обновлённый `gog/reset.sh` + `gog-process.sh` |
|
||||
| Повторный `launch-galaxy` без reset | всегда `reset` → `launch` |
|
||||
| Смотреть только Whisky, не Cmd+Tab | окно Wine **вне** главного окна Whisky |
|
||||
| Запуск из `~/code/local_machine` без `cd drive_c` | `launch-galaxy.sh` сам ставит cwd в `drive_c` |
|
||||
|
||||
**JA3 на Mac:** установка — offline backup + `install-ja3-innoextract.sh` (Whisky GOG-Install). **Запуск — CrossOver D3DMetal:** `setup-ja3-crossover.sh` + `run-ja3-crossover.sh`. Whisky `run-ja3.sh` **не** даёт рабочую D3D12. Runbook: [JA3_RUNBOOK.md](JA3_RUNBOOK.md).
|
||||
|
||||
### Setup: красный X, пустое окно, gdi32 (RCA 2026-05-28)
|
||||
|
||||
После успешного **web-stub** (скачан `GalaxySetup.exe` ~290 MB) второй этап — **Inno Setup + botva2.dll**. В Wine типично:
|
||||
|
||||
| Лог Inno (`Setup Log … #00N.txt`) | Значение |
|
||||
|-----------------------------------|----------|
|
||||
| `Found GalaxyClientExePath` + `Showing Galaxy already running dialog` | рядом висит `GalaxyInstaller.exe` или в реестре уже есть путь к Galaxy |
|
||||
| `GalaxyClient registry found, but executable returned an empty version` | Wine не отдаёт `FileVersion` для `GalaxyClient.exe` |
|
||||
| `Access violation … gdi32.dll` Read of address `00000068` | отрисовка MsgBox (текст пустой — тот же класс багов, что и пустой UI) |
|
||||
|
||||
**Рабочий обход (проверено):**
|
||||
|
||||
1. **GOG Galaxy** — `innoextract` + VC++ redist из `setup_galaxy.exe`, **не** `GalaxySetup.exe` в Wine:
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/install-galaxy-extract.sh
|
||||
```
|
||||
2. **Jagged Alliance 3** — из клиента Galaxy после входа (CEF, не botva2):
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/launch-galaxy.sh
|
||||
# → Cmd+Tab → GalaxyClient → Install JA3
|
||||
```
|
||||
Или одной цепочкой: `bash …/gog/finish-ja3-install.sh`
|
||||
|
||||
Web-stub + `GalaxySetup /VERYSILENT` на этом Wine **не доводят** установку до конца (gdi32 на любом bottle).
|
||||
|
||||
Снять логи: `bash …/gog/collect-setup-rca.sh` → `temp/gog-setup-rca-*`
|
||||
|
||||
### Повторный запуск stub
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/reset.sh
|
||||
# Предпочтительно без stub:
|
||||
bash ~/code/local_machine/scripts/games/gog/finish-ja3-install.sh
|
||||
# Или принудительно stub (часто упирается в gdi32 на GalaxySetup):
|
||||
GOG_USE_WEB_STUB=1 bash ~/code/local_machine/scripts/games/gog/install-all.sh \
|
||||
~/code/hunabapp-dev/temp/GOG_Galaxy_Jagged_Alliance_3.exe
|
||||
```
|
||||
|
||||
Диагностика:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/diagnose-session.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/collect-setup-rca.sh
|
||||
```
|
||||
|
||||
См. также: [`scripts/games/gog/README.md`](../../scripts/games/gog/README.md), [WINE_MAC_RUNBOOK.md §3.3](./WINE_MAC_RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## Steam зависает / мало места
|
||||
|
||||
### Симптомы
|
||||
|
||||
`Failed creating file mapping`, `CSteamEngine::BMainLoop stalled`, `wineserver crashed`, **`winedbg`** размножается → см. [§ Swap](#захламление-swap-и-утечка-winedbg).
|
||||
|
||||
### Действия
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh disk # ≥10 GB
|
||||
pkill -9 winedbg 2>/dev/null
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
```
|
||||
|
||||
Очистка: `Steam/dumps/*.dmp`, `scripts/games/cache/*.dmg`, `Libraries.tar.gz` (после установки Wine), Корзина.
|
||||
|
||||
---
|
||||
|
||||
## Графика DirectX 11 / Failed to initialize
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh dxvk
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
```
|
||||
|
||||
Только **Gcenx DXVK-macOS**, не vanilla DXVK 2.5. Запуск — **`play`** (DXVK env), **не** Play в Steam UI.
|
||||
|
||||
---
|
||||
|
||||
## Мышь: курсор двигается, ЛКМ нет
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose
|
||||
```
|
||||
|
||||
→ [UNITY_POINTER_MOUSE_FIX.md](./UNITY_POINTER_MOUSE_FIX.md)
|
||||
|
||||
---
|
||||
|
||||
## Whisky «Zero KB»
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/install-whiskywine-manual.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## Connection Problem: Steam не подключается к серверам
|
||||
|
||||
### Симптомы
|
||||
|
||||
Диалог **Connection Problem** — *There was a problem communicating with Steam*. Кнопки **Retry** и **Enter Offline Mode**.
|
||||
|
||||
Интернет на Mac есть, но Wine-Steam **не логинится** → сейвы в игре пустые.
|
||||
|
||||
### Что видно в логах
|
||||
|
||||
`Steam/logs/connection_log.txt`:
|
||||
|
||||
| Этап | OK | Плохо |
|
||||
|------|-----|-------|
|
||||
| HTTP probe | `Connectivity test … OK!` | timeout |
|
||||
| CM / WebSocket | `ConnectionCompleted` → `Logged On` | `ConnectionDisconnected('Disconnected By Remote Host')` |
|
||||
| console_log | — | `LogonFailure No Connection` |
|
||||
|
||||
Утром (04:08–04:10) было **2 обрыва** WebSocket, на **3-й** попытке (CM `cmp2-lhr1.steamserver.net:443`) — `Logged On`. Днём сессии часто **не доходят** до CM, если параллельно открыт Mac Steam.
|
||||
|
||||
### Причины (по частоте)
|
||||
|
||||
1. **Запущен нативный Steam.app** на Mac (сейчас типичный случай) — конфликт с bottle.
|
||||
2. **WebSocket к Steam CM** нестабилен в Wine — нужны повторные Retry (как в 04:08–04:10).
|
||||
3. VPN / смена сети — CM отваливается (`Disconnected By Remote Host`).
|
||||
4. IPv6 в Wine падает (не критично, если WebSocket:443 жив).
|
||||
|
||||
### Что делать (по порядку)
|
||||
|
||||
Команды ниже — **копировать в Terminal целиком по шагам** (путь абсолютный, переменная `G` не нужна).
|
||||
|
||||
**1. Полностью закрыть Mac Steam**
|
||||
|
||||
Удаление **Steam.app** из `/Applications` **не убирает** клиент: он часто остаётся в
|
||||
`~/Library/Application Support/Steam/Steam.AppBundle/` и процессах `steam_osx` / `ipcserver`.
|
||||
|
||||
```bash
|
||||
killall steam_osx 2>/dev/null
|
||||
killall ipcserver 2>/dev/null
|
||||
pkill -f 'Steam.AppBundle/Steam/Contents/MacOS' 2>/dev/null
|
||||
pgrep -f 'Steam.AppBundle/Steam/Contents/MacOS/steam_osx' >/dev/null && echo "⚠️ Mac Steam ещё жив" || echo "OK: Mac Steam остановлен"
|
||||
```
|
||||
|
||||
(Если был установлен: `osascript -e 'quit app "Steam"'` — при ошибке `-128` используйте `killall` выше.)
|
||||
|
||||
**2. Перезапуск bottle**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
```
|
||||
|
||||
**3. В диалоге Connection Problem** (окно Wine-Steam)
|
||||
|
||||
- **Retry** — 2–3 раза, пауза **15–30 с** между попытками.
|
||||
- Если не коннектится — **Enter Offline Mode**.
|
||||
|
||||
Offline Mode для Heroes **нормален**: сейвы **локальные** (`1/2/3.saveskirmish` уже на диске), облако Steam не обязательно.
|
||||
|
||||
После **Logged On** или **Offline Mode**:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
```
|
||||
|
||||
❌ **Не** нажимать **Play** у Olden Era в библиотеке Steam — applaunch без game-env → D3D11 crash.
|
||||
|
||||
**4. Проверка**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
```
|
||||
|
||||
Нужно **не** `Logged Off`, а **`Logged On`** или Steam в offline с поднятым `Steam.exe`.
|
||||
|
||||
**Всё одним блоком** (шаг 3 — вручную в окне Steam):
|
||||
|
||||
```bash
|
||||
osascript -e 'quit app "Steam"'
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
# → Retry 2–3× или Enter Offline Mode → затем:
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
```
|
||||
|
||||
### Антипаттерн
|
||||
|
||||
❌ Жать **Retry** при открытом Mac Steam — почти всегда снова Connection Problem.
|
||||
|
||||
---
|
||||
|
||||
## Steam offline (ручной режим)
|
||||
|
||||
1. Whisky → Kill All Bottles
|
||||
2. Закрыть **Steam.app** на Mac
|
||||
3. `bash ~/code/local_machine/scripts/games/start.sh steam-ui`
|
||||
|
||||
---
|
||||
|
||||
## Полезные команды
|
||||
|
||||
**HoMM / Steam (bottle HoMM-OldenEra):**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh fix-steam-ui
|
||||
bash ~/code/local_machine/scripts/games/start.sh diagnose-saves
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset
|
||||
bash ~/code/local_machine/scripts/games/start.sh steam-ui
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
bash ~/code/local_machine/scripts/games/start.sh saves
|
||||
```
|
||||
|
||||
**GOG web-stub (отдельный bottle):**
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/install-all.sh /path/GOG_Galaxy_*.exe
|
||||
bash ~/code/local_machine/scripts/games/gog/install-gdiplus.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/install-corefonts.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-web-installer.sh /path/GOG_Galaxy_*.exe
|
||||
bash ~/code/local_machine/scripts/games/gog/diagnose-session.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/reset.sh
|
||||
```
|
||||
|
||||
**Память / swap:**
|
||||
|
||||
```bash
|
||||
sysctl vm.swapusage
|
||||
pgrep winedbg | wc -l
|
||||
pkill -9 winedbg; pkill -9 wineserver
|
||||
```
|
||||
|
||||
Логи:
|
||||
|
||||
```
|
||||
# Steam / HoMM
|
||||
~/Library/Containers/…/Bottles/38412122-…/drive_c/Program Files (x86)/Steam/logs/
|
||||
~/Library/Containers/…/…/AppData/LocalLow/Unfrozen/HeroesOldenEra/Player.log
|
||||
|
||||
# GOG web-stub (имя bottle в имени файла)
|
||||
/tmp/gog-web-installer-GOG-Install.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Последняя сверка: 2026-05-28 (§ шрифты: сводка антипаттернов Steam dwrite / GOG gdiplus; GOG-Install stub OK; GalaxySetup → gdi32) · Whisky 3 · WhiskyWine 11*
|
||||
48
docs/games/UNITY_POINTER_MOUSE_FIX.md
Normal file
48
docs/games/UNITY_POINTER_MOUSE_FIX.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# HoMM Olden Era: курсор двигается, клики не работают (Whisky / Wine на macOS)
|
||||
|
||||
## Корневая причина (не «битая мышь»)
|
||||
|
||||
Современный Unity (в т.ч. ветки с Unity 6 input stack) вызывает Windows API **`EnableMouseInPointer`** и дальше ждёт сообщения **`WM_POINTER`**. В типичном Wine из Whisky эта функция сейчас **заглушка** → в `Player.log` появляется строка:
|
||||
|
||||
```text
|
||||
EnableMouseInPointer failed with the following error: Call not implemented.
|
||||
```
|
||||
|
||||
Пока это так, **никакие** танцы с `WineBus` / SDL / виртуальным desktop / оконным fullscreen **не обязаны** починить ЛКМ: курсор может двигаться, а нажатия и hover в UI игнорируются.
|
||||
|
||||
Проверка одной командой (после хотя бы одного запуска игры):
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/diagnose-homm-mouse.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что реально работает сегодня
|
||||
|
||||
### Вариант A — CrossOver (рекомендуется для Unity с этим симптомом)
|
||||
|
||||
1. Установить **CrossOver** для macOS (CodeWeavers).
|
||||
2. При необходимости применить готовый патч слоя Wine под `EnableMouseInPointer` (см. репозиторий с инструкцией и бинарниками под конкретную версию CrossOver):
|
||||
[dabielf/crossover-unity-mouse-fix](https://github.com/dabielf/crossover-unity-mouse-fix)
|
||||
3. Запускать игру **из CrossOver**, а не из «голого» WhiskyWine, если патч ставился в CrossOver.
|
||||
|
||||
Важно: патч из репозитория рассчитан на **пути внутри `CrossOver.app`**, а не на `~/Library/Application Support/com.isaacmarovitz.Whisky/Libraries/`. Копирование DLL из CrossOver в Whisky **вручную** — высокий риск несовместимости ABI (краши Wine).
|
||||
|
||||
### Вариант B — ждать/переезжать на Wine с upstream-фиксом
|
||||
|
||||
Следить за [Wine Bug #53847](https://bugs.winehq.org/show_bug.cgi?id=53847) и обновлениями **WhiskyWine**, когда туда попадёт полноценная реализация (или использовать сборку Wine, где она уже есть), и только потом снова тестировать в Whisky.
|
||||
|
||||
---
|
||||
|
||||
## Связь с нашими скриптами
|
||||
|
||||
- `start.sh input` / `fix-homm-wine-input.sh` — про **HID/SDL и геймпады**, не про `EnableMouseInPointer`.
|
||||
- `start.sh game` + DXVK-macOS — про **графику (D3D11)**, не про pointer stack Unity.
|
||||
|
||||
---
|
||||
|
||||
## Быстрый чеклист
|
||||
|
||||
1. `bash ~/code/local_machine/scripts/games/diagnose-homm-mouse.sh`
|
||||
2. Если exit code **2** и есть строка про `EnableMouseInPointer` → идём в CrossOver/другой Wine, а не в «ещё один SDL-твик».
|
||||
225
docs/games/WINE_APPS_LAUNCH_RUNBOOK.md
Normal file
225
docs/games/WINE_APPS_LAUNCH_RUNBOOK.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# Runbook: запуск Windows-приложений в Wine (Whisky)
|
||||
|
||||
**Главный документ по пуску** любых `.exe` в bottle на Mac. Подготовка bottle, шрифты, GOG/сеть — [WINE_MAC_RUNBOOK.md](./WINE_MAC_RUNBOOK.md). Сбои — [TROUBLESHOOTING.md](./TROUBLESHOOTING.md). **Шрифты / пустой UI:** [TROUBLESHOOTING § шрифты](./TROUBLESHOOTING.md#шрифты-антипаттерны-сводка) (`dwrite` для Steam, `gdiplus=n,b` для GOG stub).
|
||||
|
||||
- **Скрипты:** [`../../scripts/games`](../../scripts/games)
|
||||
- **Универсальный запуск:** `bash …/scripts/games/run-wine-exe.sh` (см. §2)
|
||||
|
||||
---
|
||||
|
||||
## 1. Новая программа — пройти по порядку
|
||||
|
||||
Скопируйте чеклист; для каждого пункта есть команда ниже.
|
||||
|
||||
| # | Вопрос | Действие |
|
||||
|---|--------|----------|
|
||||
| 1 | **Какой тип?** | Таблица §1.1 → профиль Wine |
|
||||
| 2 | **Свой bottle?** | Steam/Unity/много игр → часто **да**; GOG web-stub → **отдельный** `GOG-Install`; один офлайн-setup → можно общий |
|
||||
| 3 | **Место на диске** | `bash …/start.sh disk` → **≥20 GB** для установщиков/игр, **≥3 GB** для лаунчера |
|
||||
| 4 | **Один раз: bottle** | Whisky → **Windows 10 64-bit** → Sync **MSync**; Wine: `install-whiskywine-manual.sh` |
|
||||
| 5 | **Один раз: шрифты/сеть** | `bash …/setup-wine-bottle.sh` (HoMM/Steam bottle) или `gog/setup-bottle.sh` (GOG bottle) |
|
||||
| 6 | **Установка** | Steam/GOG/офлайн `.exe` — §1.1; **не** два установщика GOG одновременно |
|
||||
| 7 | **Запуск** | §2 — `run-wine-exe.sh`, `start.sh …`, или Whisky → Run |
|
||||
| 8 | **Зафиксировать** | Добавить строку в §6 и при необходимости обёртку в `scripts/games/` |
|
||||
|
||||
### 1.1 Тип приложения → установка и профиль
|
||||
|
||||
| Тип | Примеры | Установка (один раз) | Профиль запуска (`run-wine-exe.sh`) |
|
||||
|-----|---------|----------------------|-------------------------------------|
|
||||
| **Steam + игра** | HoMM Olden Era | `install-steam-extract.sh`, игра из Steam | `steam` → UI; `game` → exe + DXVK |
|
||||
| **GOG Galaxy (лаунчер)** | Galaxy 2.0 | `start.sh gog-install` или `gog/install-galaxy-extract.sh` | `gog` |
|
||||
| **GOG web-stub** | `GOG_Galaxy_*.exe` | Только bottle `gog/` — [gog/README.md](../../scripts/games/gog/README.md) | `gog` |
|
||||
| **Офлайн setup .exe** | backup с gog.com | Скопировать в `drive_c/`, запустить установщик | `plain` или `gog` |
|
||||
| **GOG multipart + innoextract** | JA3 offline backup | `gog/install-ja3-innoextract.sh` — [JA3 runbook](JA3_RUNBOOK.md) | **CrossOver:** `run-ja3-crossover.sh` |
|
||||
| **.NET WinForms installer** | web-stub, мастера GOG | **`gog/install-gdiplus.sh`** + corefonts | `gog` |
|
||||
| **CEF (Steam/GOG UI)** | Steam.exe, GalaxyClient | CEF-флаги в `config.sh` / `gog/config.sh` | `steam` / `gog` |
|
||||
| **DX11 игра (Unity и т.д.)** | Olden Era | `install-dxvk-macos.sh` | `game` |
|
||||
| **Прочий Win32 .exe** | утилиты, старые игры | Обычно только setup bottle + шрифты | `plain` |
|
||||
|
||||
**Inno Setup + botva2** (GOG `GalaxySetup.exe`, **JA3 offline setup**): в Wine часто **gdi32** — не полагаться на `/VERYSILENT`. Galaxy — **innoextract**; **JA3 multipart** — `gog/install-ja3-innoextract.sh`. Запуск JA3 — [JA3_RUNBOOK.md](JA3_RUNBOOK.md). См. [TROUBLESHOOTING.md § Setup](./TROUBLESHOOTING.md).
|
||||
|
||||
Подробная подготовка по типам: [WINE_MAC_RUNBOOK.md §3](./WINE_MAC_RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## 2. Запуск (любая программа)
|
||||
|
||||
### 2.1 Универсально: `run-wine-exe.sh`
|
||||
|
||||
```bash
|
||||
# Справка
|
||||
bash ~/code/local_machine/scripts/games/run-wine-exe.sh --help
|
||||
|
||||
# Программа уже в drive_c (путь Wine)
|
||||
bash ~/code/local_machine/scripts/games/run-wine-exe.sh \
|
||||
--bottle HoMM-OldenEra \
|
||||
--profile game \
|
||||
'C:\Program Files (x86)\Steam\steamapps\common\My Game\game.exe'
|
||||
|
||||
# Установщик с Mac (копируется в C:\)
|
||||
bash ~/code/local_machine/scripts/games/run-wine-exe.sh \
|
||||
--bottle GOG-Install \
|
||||
--profile gog \
|
||||
~/Downloads/Setup_Game.exe
|
||||
|
||||
# Bottle GOG из gog/bottle.env (без --bottle)
|
||||
bash ~/code/local_machine/scripts/games/run-wine-exe.sh --profile gog 'C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe'
|
||||
```
|
||||
|
||||
| `--profile` | `WINEDLLOVERRIDES` / env | Когда |
|
||||
|-------------|--------------------------|--------|
|
||||
| `plain` | по умолчанию Wine | утилиты, простые exe |
|
||||
| `steam` | `dwrite=n,b` | Steam.exe, VGUI |
|
||||
| `gog` | `gdiplus=n,b` | GOG Galaxy, .NET installers |
|
||||
| `game` | DXVK + `d3d11=n,b` | DX11/Unity после `install-dxvk-macos.sh` |
|
||||
|
||||
Переменные bottle: `WINE_BOTTLE_ID=<UUID>` или `WINE_BOTTLE_NAME=HoMM-OldenEra`. GOG-bottle: `GOG_BOTTLE=1` (читает `gog/bottle.env`).
|
||||
|
||||
Фон + лог:
|
||||
|
||||
```bash
|
||||
bash …/run-wine-exe.sh --background --log /tmp/my-app.log --profile plain 'C:\app\foo.exe'
|
||||
```
|
||||
|
||||
### 2.2 Через Whisky GUI
|
||||
|
||||
1. Открыть bottle → **Run** → выбрать `.exe` из `drive_c` **или** с Mac (Whisky скопирует).
|
||||
2. Окно: **Cmd+Tab** → `wine64` / имя процесса, не только окно Whisky.
|
||||
|
||||
### 2.3 Готовые обёртки (`start.sh`)
|
||||
|
||||
| Задача | Команда |
|
||||
|--------|---------|
|
||||
| Steam UI | `bash …/start.sh steam-ui` |
|
||||
| HoMM (игра + сейвы) | `bash …/start.sh play` |
|
||||
| GOG Galaxy | `bash …/start.sh gog` |
|
||||
| Сброс процессов bottle | `bash …/start.sh reset` |
|
||||
| Место на диске | `bash …/start.sh disk` |
|
||||
| GOG bottle: клиент | `bash …/gog/launch-galaxy.sh` |
|
||||
| GOG bottle: JA3 установка | `bash …/gog/install-ja3-innoextract.sh` |
|
||||
| GOG bottle: JA3 запуск | `bash …/gog/run-ja3-crossover.sh` — [JA3_RUNBOOK.md](JA3_RUNBOOK.md) |
|
||||
|
||||
Полный список: `bash …/start.sh` без аргументов.
|
||||
|
||||
### 2.4 Вручную (если нет скрипта)
|
||||
|
||||
```bash
|
||||
export WINEPREFIX="$HOME/Library/Containers/com.franke.Whisky/Bottles/<UUID>"
|
||||
WINE64="$HOME/Library/Application Support/com.franke.Whisky/Libraries/Wine/bin/wine64"
|
||||
export DYLD_LIBRARY_PATH="$HOME/Library/Application Support/com.franke.Whisky/Libraries/Wine/lib"
|
||||
export WINEMSYNC=1 WINEESYNC=1
|
||||
"$WINE64" 'C:\path\app.exe'
|
||||
```
|
||||
|
||||
Профили overrides — [WINE_MAC_RUNBOOK.md §4](./WINE_MAC_RUNBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## 3. Инфраструктура (один раз на машину)
|
||||
|
||||
| Компонент | Путь |
|
||||
|-----------|------|
|
||||
| Whisky 3 | `brew install --cask frankea/whisky/whisky` |
|
||||
| Wine | `bash …/install-whiskywine-manual.sh` |
|
||||
| Bottle | `~/Library/Containers/com.franke.Whisky/Bottles/<UUID>/` |
|
||||
| Кэш установщиков | `scripts/games/cache/` |
|
||||
|
||||
Проверка места: `bash …/start.sh disk`.
|
||||
|
||||
Создание bottle: Whisky **+** → Windows 10 → имя; для GOG: `bash …/gog/create-bottle.sh GOG-Install`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Шаблон: добавить программу в репозиторий
|
||||
|
||||
1. **Имя и bottle** — зафиксировать UUID в `config.sh` или `gog/bottle.env`.
|
||||
2. **Скрипт установки** (если повторяемо) — `scripts/games/install-<app>.sh` или секция в `gog/`.
|
||||
3. **Скрипт запуска** — команда в `start.sh` *или* документированный `run-wine-exe.sh --profile …`.
|
||||
4. **Строка в §6** ниже — тип, bottle, команда запуска, путь к exe.
|
||||
5. **Сбои** — краткий симптом в [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
|
||||
|
||||
---
|
||||
|
||||
## 5. Уже настроенные приложения
|
||||
|
||||
| Приложение | Bottle | Запуск | Примечание |
|
||||
|------------|--------|--------|------------|
|
||||
| **Steam** | HoMM-OldenEra | `start.sh steam-ui` | Не параллельно с Steam.app |
|
||||
| **HoMM Olden Era** | HoMM-OldenEra | `start.sh play` | Не Play в Steam UI; нужен DXVK — §7 |
|
||||
| **GOG Galaxy 2.0** | HoMM-OldenEra | `start.sh gog` | Логин в Wine — best effort |
|
||||
| **Jagged Alliance 3** | GOG-Install + CrossOver **JA3-GOG** | `gog/run-ja3-crossover.sh` | Установка: Whisky `install-ja3-innoextract.sh` ([runbook](JA3_RUNBOOK.md)) |
|
||||
| **GOG Galaxy 2.0** | GOG-Install | `gog/launch-galaxy.sh` | Best effort; JA3 — **не** через Galaxy на этом Mac |
|
||||
| **Любой новый .exe** | свой / общий | `run-wine-exe.sh` | §2.1 |
|
||||
|
||||
---
|
||||
|
||||
## 6. Справочник скриптов
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| **run-wine-exe.sh** | Универсальный пуск exe в выбранном bottle |
|
||||
| **start.sh** | Steam / HoMM / GOG / reset / disk / dxvk |
|
||||
| **setup-wine-bottle.sh** | Шрифты + реестр + сеть (HoMM bottle) |
|
||||
| **config.sh** | UUID HoMM, `export_wine_env_*` |
|
||||
| **gog/** | Отдельный bottle, gdiplus, web-stub |
|
||||
| **install-dxvk-macos.sh** | DX11 для Unity |
|
||||
|
||||
---
|
||||
|
||||
## 7. HoMM Olden Era (ежедневно)
|
||||
|
||||
Краткая выжимка; детали сейвов/графики — прежние §4–5 методички.
|
||||
|
||||
### Однократно
|
||||
|
||||
```bash
|
||||
brew install p7zip
|
||||
bash ~/code/local_machine/scripts/games/install-whiskywine-manual.sh
|
||||
bash ~/code/local_machine/scripts/games/install-steam-extract.sh
|
||||
bash ~/code/local_machine/scripts/games/install-dxvk-macos.sh
|
||||
bash ~/code/local_machine/scripts/games/setup-wine-bottle.sh
|
||||
```
|
||||
|
||||
Bottle **HoMM-OldenEra**, Sync **MSync**.
|
||||
|
||||
### Каждый запуск
|
||||
|
||||
Steam **Logged On** → затем:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh play
|
||||
```
|
||||
|
||||
Не **Play** у Olden Era в окне Steam (нет DXVK).
|
||||
|
||||
Альтернатива: `start.sh game`; сейвы: `start.sh saves`; диагностика: `start.sh diagnose-saves`.
|
||||
|
||||
### Переменные (`start.sh game`)
|
||||
|
||||
| Переменная | По умолчанию | Назначение |
|
||||
|------------|----------------|------------|
|
||||
| `HOMM_VDESKTOP` | `1920x1080` | Виртуальный стол Wine |
|
||||
| `HOMM_USE_WINEVDESKTOP` | `1` | `0` — без explorer /desktop |
|
||||
| `HOMM_UNITY_WINDOWED` | `1` | Оконный режим Unity |
|
||||
| `HOMM_UNITY_EXTRA` | *(пусто)* | напр. `-popupwindow` |
|
||||
|
||||
### Сейвы (singleplayer 1/2/3)
|
||||
|
||||
```
|
||||
~/Library/Containers/com.franke.Whisky/Bottles/38412122-AAE1-4831-9F2E-6A0EB364B3F9/drive_c/users/eternal/AppData/LocalLow/Unfrozen/HeroesOldenEra/users/Steam_<ID>/saves/singleplayer/
|
||||
```
|
||||
|
||||
*Bottle ID: `38412122-AAE1-4831-9F2E-6A0EB364B3F9` · Whisky 3 · WhiskyWine 3*
|
||||
|
||||
---
|
||||
|
||||
## 8. Шпаргалка
|
||||
|
||||
```bash
|
||||
G=~/code/local_machine/scripts/games
|
||||
bash "$G/run-wine-exe.sh" --help
|
||||
bash "$G/start.sh" disk
|
||||
bash "$G/setup-wine-bottle.sh"
|
||||
bash "$G/start.sh" play
|
||||
bash "$G/gog/launch-galaxy.sh"
|
||||
```
|
||||
205
docs/games/WINE_MAC_RUNBOOK.md
Normal file
205
docs/games/WINE_MAC_RUNBOOK.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Runbook: подготовка Windows-приложений в Wine на macOS (Whisky)
|
||||
|
||||
Один раз: bottle, шрифты, сеть, установка по **типу** приложения. **Ежедневный запуск** любых `.exe` — [WINE_APPS_LAUNCH_RUNBOOK.md](./WINE_APPS_LAUNCH_RUNBOOK.md).
|
||||
|
||||
Антипаттерны (GOG в HoMM-bottle, swap/winedbg): [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
|
||||
|
||||
Источники: [Whisky](https://github.com/Whisky-App/Whisky) (архив, Apple Silicon + macOS 14+), [Whisky #1220 CEF](https://github.com/IsaacMarovitz/Whisky/issues/1220), [CodeWeavers GOG Galaxy tip](https://www.codeweavers.com/compatibility/crossover/tips/gog-galaxy/gog-galaxy-working), практика bottle на этой машине (2026-05).
|
||||
|
||||
---
|
||||
|
||||
## 1. Жёсткие требования (без этого не обещаем успех)
|
||||
|
||||
| Условие | Значение |
|
||||
|--------|----------|
|
||||
| Mac | **Apple Silicon** (M1+) |
|
||||
| macOS | **14 Sonoma+** (Whisky 3 / GPTK) |
|
||||
| Whisky | Установлен, bottle **Windows 10, 64-bit** |
|
||||
| Свободное место | **≥20 GB** для GOG/игр (web-stub + установка); **≥3 GB** минимум для Steam/лаунчеров |
|
||||
| Один лаунчер | Не запускать **нативный Steam.app** параллельно с Wine Steam |
|
||||
| Один установщик | Не открывать **два** `GOG_Galaxy_*.exe` — будет «Installer Already Running» |
|
||||
| GOG web-stub | **Отдельный bottle** + `scripts/games/gog/` (не HoMM/Steam) |
|
||||
| Подготовка HoMM/Steam | **Один раз:** `setup-wine-bottle.sh` |
|
||||
|
||||
---
|
||||
|
||||
## 2. Один раз: подготовка bottle
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/setup-wine-bottle.sh
|
||||
```
|
||||
|
||||
Делает автоматически:
|
||||
|
||||
- Копирует **Arial, Tahoma, Verdana** и др. из macOS в `C:\windows\Fonts`
|
||||
- По возможности `winetricks liberation` (GOG web-installer / .NET WinForms)
|
||||
- Реестр: **MS Shell Dlg → Tahoma**, GenericSansSerif → Arial
|
||||
- **DnsIPv6Enabled=0**, ProxyEnable=0 в bottle
|
||||
- Steam **DWriteEnable=0** (пустой текст VGUI)
|
||||
|
||||
Повтор при сбое шрифтов:
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/setup-wine-bottle.sh --force
|
||||
```
|
||||
|
||||
> **Не полагайтесь на `winetricks corefonts` из GUI Whisky** — зеркала Microsoft часто отдают 404 ([Whisky #515](https://github.com/Whisky-App/Whisky/issues/515)). Наш скрипт копирует шрифты с Mac.
|
||||
>
|
||||
> Опционально (медленно): `WINE_SETUP_WINETRICKS=1 bash …/setup-wine-bottle.sh --force`
|
||||
|
||||
---
|
||||
|
||||
## 3. Типы приложений и как запускать
|
||||
|
||||
### 3.1 Steam + Unity (HoMM Olden Era)
|
||||
|
||||
| Шаг | Команда |
|
||||
|-----|---------|
|
||||
| DXVK в bottle (один раз) | `bash …/start.sh dxvk` |
|
||||
| Steam UI + логин | `bash …/start.sh steam-ui` |
|
||||
| Пустой текст Steam | `bash …/start.sh fix-steam-ui` |
|
||||
| Игра (сейвы 1/2/3) | `bash …/start.sh play` |
|
||||
| Зависло | `bash …/start.sh reset` |
|
||||
|
||||
**CEF Steam (обязательно):** `-allosarches -cef-force-32bit -cef-in-process-gpu -no-cef-sandbox -cef-disable-gpu` — уже в `config.sh`.
|
||||
|
||||
**Игра:** DXVK + `d3d11=n,b`. **Не** смешивать с `export_wine_env_steam` при запуске exe.
|
||||
|
||||
### 3.2 GOG Galaxy 2.0 (лаунчер)
|
||||
|
||||
| Шаг | Команда |
|
||||
|-----|---------|
|
||||
| Установка (один раз) | `bash …/start.sh gog-install` |
|
||||
| Запуск | `bash …/start.sh gog` |
|
||||
| Перезапуск | `bash …/start.sh gog-restart` |
|
||||
| Логин сбрасывает пароль | `bash …/start.sh fix-gog-auth` + `diagnose-gog-auth` |
|
||||
|
||||
**CEF GOG:** `-allosarches -cef-force-32bit -no-cef-sandbox -cef-disable-gpu` (без `-cef-in-process-gpu` — иначе CPU spin).
|
||||
|
||||
**Ограничение Wine:** нативный HTTP клиент Galaxy (`auth.gog.com/token`, `cfg.gog.com`) часто **таймаут 15–20 с** → логин в клиенте **ненадёжен**. Обходы ниже в §5.
|
||||
|
||||
### 3.3 GOG web-stub (~500 KB) — **отдельный bottle, без Steam**
|
||||
|
||||
**Не** запускать в bottle HoMM-OldenEra (там Steam, дампы, `winedbg`). См. `scripts/games/gog/README.md`.
|
||||
|
||||
```bash
|
||||
# Whisky → New Bottle → Windows 10 64-bit → «GOG-Install»
|
||||
bash ~/code/local_machine/scripts/games/gog/list-bottles.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/use-bottle.sh <UUID> GOG-Install
|
||||
|
||||
bash ~/code/local_machine/scripts/games/gog/create-bottle.sh GOG-Install
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-bottle.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/install-gdiplus.sh # обязательно для .NET
|
||||
brew install cabextract
|
||||
bash ~/code/local_machine/scripts/games/gog/install-corefonts.sh
|
||||
|
||||
bash ~/code/local_machine/scripts/games/gog/run-web-installer.sh \
|
||||
~/path/to/GOG_Galaxy_Jagged_Alliance_3.exe
|
||||
```
|
||||
|
||||
Старые обёртки (`run-gog-web-installer.sh`, `setup-wine-bottle.sh` для GOG) перенаправляют в `gog/`.
|
||||
|
||||
**Типичные сбои:**
|
||||
|
||||
| Симптом | Действие |
|
||||
|---------|----------|
|
||||
| Пустые подписи / `FontFamilyNotFound` | `gog/install-corefonts.sh` → `gog/setup-bottle.sh --force` |
|
||||
| «Installer Already Running» | `bash …/gog/reset.sh` |
|
||||
| Нет bottle.env | `gog/use-bottle.sh <UUID>` |
|
||||
|
||||
Лог: `/tmp/gog-web-installer-GOG-Install.log`
|
||||
|
||||
### 3.4 GOG offline backup (рекомендуется при проблемах с сетью)
|
||||
|
||||
1. На [gog.com](https://www.gog.com) → библиотека → игра → **offline backup installer** (полный `.exe`, ГБ).
|
||||
2. Скопировать в bottle: `…/drive_c/Setup_Game.exe`
|
||||
3. Запуск:
|
||||
|
||||
```bash
|
||||
source ~/code/local_machine/scripts/games/config.sh
|
||||
export_wine_env
|
||||
"$WINE64" 'C:\Setup_Game.exe'
|
||||
```
|
||||
|
||||
Или через Whisky: Run → выбрать exe в `drive_c`.
|
||||
|
||||
### 3.5 JA3 multipart — innoextract, экономия диска
|
||||
|
||||
Для **Jagged Alliance 3** (и похожих GOG multipart backup): Wine-Inno **не доводит**; канон — **`gog/install-ja3-innoextract.sh`**.
|
||||
|
||||
Кратко:
|
||||
|
||||
1. Пять файлов в **`~/Games/`**: `setup_jagged_alliance_3_1.5.2_(80730).exe` + `-1.bin` … `-4.bin`.
|
||||
2. Bottle **GOG-Install** (`gog/bottle.env`).
|
||||
3. `bash …/gog/install-ja3-innoextract.sh` — распаковка в `drive_c/GOG Games/Jagged Alliance 3/`, `.bin` удаляются по мере чтения, после успеха — все архивы в `~/Games` (~15 GB).
|
||||
4. Запуск: `bash …/gog/run-ja3-crossover.sh` (swap guard: см. [JA3_RUNBOOK.md](JA3_RUNBOOK.md) §4, §10).
|
||||
|
||||
Полный runbook: [JA3_MAC_WITHOUT_GALAXY.md](JA3_MAC_WITHOUT_GALAXY.md).
|
||||
|
||||
---
|
||||
|
||||
## 4. Переменные окружения (уже в скриптах)
|
||||
|
||||
```bash
|
||||
export WINEPREFIX="<bottle>"
|
||||
export WINEMSYNC=1
|
||||
export WINEESYNC=1
|
||||
export DYLD_LIBRARY_PATH="<Whisky>/Libraries/Wine/lib:…"
|
||||
```
|
||||
|
||||
| Профиль | WINEDLLOVERRIDES | Когда |
|
||||
|---------|------------------|--------|
|
||||
| Steam / GOG UI | `dwrite=n,b` | steam-ui, gog |
|
||||
| Unity / DX11 игра | `d3d11,d3d10core=n,b` | play, game |
|
||||
|
||||
---
|
||||
|
||||
## 5. GOG: если «не подключается» / сброс пароля
|
||||
|
||||
1. Логи: `bash …/start.sh diagnose-gog-auth`
|
||||
2. `bash …/start.sh fix-gog-auth` → `gog-restart`
|
||||
3. Если в логе `timeout … auth.gog.com/token` — **вход в Galaxy под Wine часто не сохраняется**; используйте **offline installer** или покупку/ключ через браузер на Mac.
|
||||
4. Mac до `auth.gog.com` отвечает <1 с — проблема в **Poco/Qt в Wine**, не в пароле.
|
||||
|
||||
---
|
||||
|
||||
## 6. Диагностика (чеклист)
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/start.sh disk # место
|
||||
bash ~/code/local_machine/scripts/games/start.sh reset # убить Wine Steam/GOG
|
||||
pgrep -fl 'GalaxyClient|GOG_Galaxy|steam\.exe' # дубликаты
|
||||
```
|
||||
|
||||
| Лог | Путь |
|
||||
|-----|------|
|
||||
| GOG client | `…/ProgramData/GOG.com/Galaxy/logs/GalaxyClient.log` |
|
||||
| GOG CEF | `…/ProgramData/GOG.com/Galaxy/logs/cef.log` |
|
||||
| Web-stub | `/tmp/gog-web-installer.log` |
|
||||
| Steam | `…/Steam/logs/connection_log.txt` |
|
||||
|
||||
---
|
||||
|
||||
## 7. Команды (шпаргалка)
|
||||
|
||||
```bash
|
||||
BASE=~/code/local_machine/scripts/games
|
||||
|
||||
bash "$BASE/setup-wine-bottle.sh" # один раз
|
||||
bash "$BASE/start.sh" steam-ui # Steam
|
||||
bash "$BASE/start.sh" play # HoMM
|
||||
bash "$BASE/start.sh" gog-install && gog # GOG Galaxy
|
||||
bash "$BASE/run-gog-web-installer.sh" …/GOG_Galaxy_*.exe
|
||||
bash "$BASE/start.sh" reset # всё убить в bottle
|
||||
```
|
||||
|
||||
Документация: [WINE_APPS_LAUNCH_RUNBOOK.md](WINE_APPS_LAUNCH_RUNBOOK.md) · [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||
|
||||
---
|
||||
|
||||
## 8. Честные ограничения
|
||||
|
||||
- **Whisky архивен** ([maintenance notice](https://docs.getwhisky.app/maintenance-notice)); альтернатива — [Moonshine](https://github.com/ybmeng/moonshine) (Wine 11.x).
|
||||
- **GOG Galaxy login** в Wine на этой конфигурации — **best effort**, не гарантия.
|
||||
- **Web-stub** требует сеть из Wine; при таймаутах — только **offline backup**.
|
||||
- **Диск <10% свободно** — CEF crash loop, freeze; см. `docs/disk_cleanup/`.
|
||||
@@ -1,14 +1,17 @@
|
||||
# Gitea Complete Guide - Hunab.app
|
||||
# Gitea Complete Guide — local_machine
|
||||
|
||||
> **Для репозитория legal на сервере 206.189.35.205** см. **[GITEA_LEGAL_206_SERVER_GUIDE.md](./GITEA_LEGAL_206_SERVER_GUIDE.md)** (единый стандарт: push, пути, мониторинг).
|
||||
> Ниже — гайд по Gitea для Hunab.app (основной сервер 209.38.32.21).
|
||||
> **Этот гайд** — справочник по использованию Gitea для репозитория **local_machine** (документация и скрипты локальной машины).
|
||||
> Сервер: **gitea.hunab.app** (209.38.32.21).
|
||||
> **Быстрый старт для local_machine:** [LOCAL_MACHINE_GITEA_SETUP.md](./LOCAL_MACHINE_GITEA_SETUP.md).
|
||||
|
||||
> Для других репозиториев: **legal** на 206.189.35.205 — [GITEA_LEGAL_206_SERVER_GUIDE.md](./GITEA_LEGAL_206_SERVER_GUIDE.md); **hunabapp** — свои скрипты пуша в том репо.
|
||||
|
||||
---
|
||||
|
||||
**Дата:** 2026-01-02
|
||||
**Версия Gitea:** 1.25.3
|
||||
**Статус:** ✅ Полностью развернут и работает
|
||||
**Последнее обновление:** Резервный способ коммитов через временный скрипт (2026-02-16)
|
||||
**Последнее обновление:** Адаптация под проект local_machine (2026-03-14)
|
||||
|
||||
---
|
||||
|
||||
@@ -39,16 +42,27 @@ Gitea успешно развернут на production сервере как se
|
||||
|
||||
## 🚀 Развертывание
|
||||
|
||||
### Быстрый старт
|
||||
### Быстрый старт для local_machine
|
||||
|
||||
Репозиторий **local_machine** не разворачивает Gitea — он только пушит в уже работающий сервер. Первая настройка и пуш:
|
||||
|
||||
```bash
|
||||
# Развертывание Gitea
|
||||
cd /Users/eternal/code/local_machine
|
||||
# 1. Создать репо на https://gitea.hunab.app (New Repository → local_machine)
|
||||
# 2. Инициализация и пуш:
|
||||
bash scripts/deployment/gitea/setup-and-push.sh
|
||||
```
|
||||
|
||||
Подробно: **[LOCAL_MACHINE_GITEA_SETUP.md](./LOCAL_MACHINE_GITEA_SETUP.md)**.
|
||||
|
||||
### Развертывание самого Gitea (для админов сервера)
|
||||
|
||||
Скрипты развертывания Gitea на сервере находятся в других репозиториях (например hunabapp). Пример для справки:
|
||||
|
||||
```bash
|
||||
# Развертывание Gitea (в репо с инфраструктурой)
|
||||
bash scripts/deployment/gitea/deploy-gitea.sh
|
||||
|
||||
# Настройка
|
||||
bash scripts/deployment/gitea/setup-gitea.sh
|
||||
|
||||
# Тестирование
|
||||
bash scripts/deployment/gitea/test-gitea.sh
|
||||
```
|
||||
|
||||
@@ -365,6 +379,26 @@ Value: 0 issue "letsencrypt.org"
|
||||
|
||||
## 🔄 Push репозиториев (КРИТИЧЕСКИ ВАЖНО)
|
||||
|
||||
### ✅ Для репозитория local_machine
|
||||
|
||||
В этом проекте пуш делается из корня **local_machine**:
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/local_machine
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
**Если из РФ push по HTTPS зависает** — переключите remote на SSH (порт 2223):
|
||||
|
||||
```bash
|
||||
git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
Если и SSH недоступен — используйте [метод через bundle](#резервный-способ-push-через-bundle) ниже. Полная настройка: **[LOCAL_MACHINE_GITEA_SETUP.md](./LOCAL_MACHINE_GITEA_SETUP.md)**.
|
||||
|
||||
---
|
||||
|
||||
### ❌ Проблема: Push через HTTPS зависает
|
||||
|
||||
При попытке `git push` через HTTPS возникают таймауты:
|
||||
@@ -386,9 +420,9 @@ fatal: the remote end hung up unexpectedly
|
||||
|
||||
POST запрос доходит до Gitea, но соединение обрывается во время передачи данных.
|
||||
|
||||
### ✅ Систематическое решение: Универсальный скрипт
|
||||
### ✅ Универсальный скрипт пуша (в других репо)
|
||||
|
||||
**РЕКОМЕНДУЕТСЯ:** Используйте универсальный скрипт, который автоматически выбирает лучший метод:
|
||||
В репозиториях с полной инфраструктурой (например hunabapp) используется скрипт, который автоматически выбирает метод (SSH → HTTPS → bundle):
|
||||
|
||||
```bash
|
||||
# Обычный скрипт (для работы не из РФ)
|
||||
@@ -398,50 +432,11 @@ bash scripts/deployment/gitea/git-push-gitea.sh [branch]
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]
|
||||
```
|
||||
|
||||
**⚠️ КРИТИЧЕСКИ ВАЖНО:** При работе из Российской Федерации **ПРЕДПОЧТИТЕЛЬНО** использовать скрипт с суффиксом `-via-proxy`!
|
||||
**Для local_machine** такого скрипта нет — используйте `git push origin main` или SSH (см. выше).
|
||||
|
||||
**🇷🇺 ПРАВИЛО ДЛЯ РФ:**
|
||||
- ✅ **ВСЕГДА используйте прокси-версии** для push и создания тегов из РФ
|
||||
- ✅ **Ускорение в 5-10 раз** по сравнению с прямым подключением
|
||||
- ✅ **Стабильное соединение** через российский прокси-сервер (149.154.64.19)
|
||||
- ✅ **Избежание таймаутов** - прямое подключение из РФ часто нестабильно
|
||||
|
||||
**Скрипт автоматически:**
|
||||
1. Пытается SSH push (если доступен, через прокси для `-via-proxy`)
|
||||
2. Пытается HTTPS push с оптимизированными настройками (только на Linux с `timeout`)
|
||||
3. Использует bundle метод (всегда работает, особенно на macOS)
|
||||
4. ✅ **Мгновенная верификация** - обновляет локальный tracking ref напрямую через `git update-ref` (без медленного `git fetch`)
|
||||
|
||||
**Пример:**
|
||||
```bash
|
||||
# Push текущей ветки (по умолчанию dev)
|
||||
bash scripts/deployment/gitea/git-push-gitea.sh
|
||||
|
||||
# 🇷🇺 ДЛЯ РФ: Push через прокси (рекомендуется из РФ)
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
|
||||
# Push конкретной ветки
|
||||
bash scripts/deployment/gitea/git-push-gitea.sh main
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh main # 🇷🇺 ДЛЯ РФ
|
||||
|
||||
# Режим проверки без пуша (ничего не меняет, только показывает сколько коммитов уйдёт)
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run dev
|
||||
```
|
||||
|
||||
**Безопасность скрипта пуша:**
|
||||
- Не трогает текущую ветку, HEAD, рабочую копию и локальные коммиты.
|
||||
- Не выполняет: `git reset`, `git checkout --hard`, `git clean`, `git branch -D`, `push --force`.
|
||||
- Единственная запись в репо: обновление `refs/remotes/origin/<branch>` только после успешного пуша и только на кончик текущей ветки (все «ждущие пуша» коммиты при этом уже на сервере).
|
||||
- Перед пушем можно проверить: `--dry-run` — показывает число коммитов к пушу, ничего не отправляет и не меняет.
|
||||
|
||||
**Git Alias (для удобства):**
|
||||
Добавьте в `~/.gitconfig`:
|
||||
```ini
|
||||
[alias]
|
||||
gpush = !bash -c 'cd "$(git rev-parse --show-toplevel)" && bash scripts/deployment/gitea/git-push-gitea.sh "$@"' -
|
||||
```
|
||||
|
||||
Использование: `git gpush dev`
|
||||
**🇷🇺 Из РФ:**
|
||||
- Предпочтительно **SSH** (порт 2223) — часто стабильнее HTTPS.
|
||||
- Либо метод через bundle (см. раздел ниже).
|
||||
|
||||
### ✅ Резервный способ: Временный скрипт для коммита и пуша
|
||||
|
||||
@@ -453,41 +448,25 @@ bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run dev
|
||||
**Процесс:**
|
||||
1. **Агент создает временный скрипт** в `temp/commit-and-push.sh` с полным коммитом и push командой
|
||||
2. **Пользователь выполняет скрипт** вручную в терминале
|
||||
3. **Скрипт автоматически** делает `git add`, `git commit` и `git push` через универсальный скрипт
|
||||
3. **Скрипт автоматически** делает `git add`, `git commit` и `git push`
|
||||
|
||||
**Формат временного скрипта:**
|
||||
**Формат временного скрипта (для local_machine):**
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Скрипт для коммита и пуша изменений
|
||||
|
||||
set -e
|
||||
|
||||
echo "📦 Committing changes..."
|
||||
git add -A
|
||||
|
||||
echo "💾 Creating commit..."
|
||||
git commit -m "🎨 feat(frontend): центрирование нод при tidyUp с учетом реальных ширин v3.10.4
|
||||
|
||||
- ✅ Улучшена функция calculateLevelPositions для центрирования нод
|
||||
- ✅ Добавлен учет реальных ширин нод (node.width или NODE_WIDTH)
|
||||
- ✅ Вся группа нод на уровне центрируется относительно START_X
|
||||
- ✅ Более широкие ноды правильно центрированы относительно узких
|
||||
- ✅ Обновлена документация (TROUBLESHOOTING_COMPLETE.md, CHANGELOG.md)
|
||||
|
||||
Проблема: Более широкие ноды выравнивались по левому краю
|
||||
Решение: Центрирование с учетом реальных ширин нод
|
||||
Результат: Ноды центрируются на уровне, визуально выглядит красиво"
|
||||
|
||||
git commit -m "docs: описание изменений"
|
||||
echo "🚀 Pushing to Gitea..."
|
||||
bash scripts/deployment/gitea/git-push-gitea.sh dev
|
||||
|
||||
git push origin main
|
||||
echo "✅ Done!"
|
||||
```
|
||||
|
||||
**Требования к скрипту:**
|
||||
- ✅ Должен быть исполняемым (`chmod +x`)
|
||||
- ✅ Должен содержать полное сообщение коммита с описанием изменений
|
||||
- ✅ Должен использовать универсальный скрипт `git-push-gitea.sh` для push
|
||||
- ✅ Для local_machine: `git push origin main` (или SSH remote, см. выше)
|
||||
- ✅ Должен быть в папке `temp/` для временных скриптов
|
||||
- ✅ Должен иметь понятные echo сообщения для пользователя
|
||||
|
||||
@@ -514,41 +493,15 @@ rm temp/commit-and-push.sh
|
||||
- `Made-with: Cursor` и аналогичные метки инструмента
|
||||
- `Co-authored-by:`, `Signed-off-by:` и т.п., если это не требуется явно политикой репозитория
|
||||
|
||||
Коммиты должны соответствовать [COMMIT_STANDARDS.md](../../standards/COMMIT_STANDARDS.md) и содержать только заголовок и при необходимости тело сообщения в стандартном формате. См. также раздел «Commit Quality Standards» в `.cursorrules`.
|
||||
Коммиты должны содержать только заголовок и при необходимости тело сообщения в читаемом формате.
|
||||
|
||||
### Неблокирующая проверка типов (при коммитах)
|
||||
### Проверки перед коммитом (опционально)
|
||||
|
||||
Перед коммитом или пушем можно быстро проверить типы (frontend и/или backend) **без блокировки коммита** — скрипт всегда выходит с кодом 0 и только выводит ошибки в консоль.
|
||||
Если в проекте есть проверка типов или линтеры — их можно запускать перед коммитом по желанию. Для local_machine (документация и скрипты) обычно достаточно `git add` и `git commit`.
|
||||
|
||||
```bash
|
||||
# Frontend + backend (полный отчёт)
|
||||
bash scripts/maintenance/typecheck-report.sh
|
||||
(В проекте local_machine нет frontend/backend — при необходимости добавьте свои проверки в скрипты.)
|
||||
|
||||
# Только изменённая часть (быстрее)
|
||||
bash scripts/maintenance/typecheck-report.sh frontend
|
||||
bash scripts/maintenance/typecheck-report.sh backend
|
||||
```
|
||||
|
||||
Отдельно в пакетах:
|
||||
- `cd frontend && pnpm typecheck` — только frontend
|
||||
- `cd backend && pnpm typecheck` — только backend
|
||||
|
||||
**Рекомендация:** запускать при коммитах бэкенда/фронта или перед пушем; коммит не блокируется, но ошибки видны в выводе.
|
||||
|
||||
### Проверка прав на .env после деплоя (SECRETS_AND_FILE_PERMISSIONS_POLICY.md §4)
|
||||
|
||||
После деплоя бэкенда на production выполнить проверку прав на `.env.production` (и при необходимости `.env.staging`) на сервере. Встроена в скрипты `deploy-backend-core-only.sh` и `deploy-backend-core-only-via-proxy.sh`; при необходимости запустить вручную:
|
||||
|
||||
```bash
|
||||
# После деплоя backend на прод (или вручную)
|
||||
bash scripts/deployment/verify-env-permissions.sh hunab-prod
|
||||
# либо с явным хостом
|
||||
bash scripts/deployment/verify-env-permissions.sh hunab@209.38.32.21
|
||||
```
|
||||
|
||||
Ожидается: права `600` и владелец `hunab:hunab` для всех `.env*` в `/opt/app/`. При несоответствии скрипт выводит предупреждение и команду для исправления; деплой не блокируется (exit 0).
|
||||
|
||||
**Чеклист коммитов/деплоя:** после деплоя backend на production — выполнить проверку прав (или убедиться, что она уже выполнена скриптом деплоя). См. [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md) §4.
|
||||
*Для других репо:* проверка прав на `.env` после деплоя и typecheck-скрипты — в документации того проекта.
|
||||
|
||||
### Метод 1: HTTPS с оптимизированными настройками
|
||||
|
||||
@@ -559,28 +512,28 @@ git config http.lowSpeedLimit 0
|
||||
git config http.lowSpeedTime 0
|
||||
git config http.timeout 600
|
||||
|
||||
# Попытка push
|
||||
git push origin dev
|
||||
# Попытка push (для local_machine — ветка main)
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**⚠️ На macOS:** HTTPS push часто не работает из-за отсутствия команды `timeout`. Скрипт автоматически пропускает HTTPS и использует bundle метод.
|
||||
|
||||
### Метод 2: Bundle метод (надежный fallback)
|
||||
### Метод 2: Bundle метод (надежный fallback) {#резервный-способ-push-через-bundle}
|
||||
|
||||
Если HTTPS не работает, используется прямой доступ через файловую систему:
|
||||
|
||||
```bash
|
||||
# Создать bundle
|
||||
git bundle create /tmp/push.bundle origin/dev..dev
|
||||
# Создать bundle (для local_machine — ветка main)
|
||||
git bundle create /tmp/push.bundle origin/main..main
|
||||
|
||||
# Скопировать на сервер
|
||||
# Скопировать на сервер (hunab-prod — хост с доступом к Docker Gitea)
|
||||
scp /tmp/push.bundle hunab-prod:/tmp/
|
||||
|
||||
# Применить в Gitea
|
||||
# Применить в Gitea (репо local_machine)
|
||||
ssh hunab-prod "docker cp /tmp/push.bundle gitea:/tmp/push.bundle && \
|
||||
docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && \
|
||||
docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && \
|
||||
git bundle unbundle /tmp/push.bundle && \
|
||||
git update-ref refs/heads/dev \$(git bundle list-heads /tmp/push.bundle | grep dev | cut -d\" \" -f1) && \
|
||||
git update-ref refs/heads/main \$(git bundle list-heads /tmp/push.bundle | grep main | cut -d\" \" -f1) && \
|
||||
rm /tmp/push.bundle' && rm /tmp/push.bundle"
|
||||
```
|
||||
|
||||
@@ -598,59 +551,50 @@ rm /tmp/push.bundle' && rm /tmp/push.bundle"
|
||||
|
||||
### Миграция репозиториев
|
||||
|
||||
**Для регулярных push используйте скрипт:**
|
||||
```bash
|
||||
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
|
||||
```
|
||||
**Для регулярных push в local_machine:** `git push origin main` или SSH (см. раздел «Для репозитория local_machine» выше). В других репо может быть скрипт `git-push-gitea.sh`.
|
||||
|
||||
#### Шаг 1: Создание bundle из локального репозитория
|
||||
|
||||
```bash
|
||||
cd /Users/eternal/code/hunabapp-dev
|
||||
git bundle create /tmp/hunabapp-migration.bundle --all
|
||||
cd /Users/eternal/code/local_machine
|
||||
git bundle create /tmp/local_machine-migration.bundle --all
|
||||
```
|
||||
|
||||
**Результат:** Bundle ~553M
|
||||
|
||||
#### Шаг 2: Копирование bundle на сервер
|
||||
|
||||
```bash
|
||||
scp /tmp/hunabapp-migration.bundle hunab-prod:/tmp/hunabapp-migration.bundle
|
||||
scp /tmp/local_machine-migration.bundle hunab-prod:/tmp/local_machine-migration.bundle
|
||||
```
|
||||
|
||||
#### Шаг 3: Распаковка bundle на сервере
|
||||
|
||||
```bash
|
||||
ssh hunab-prod "cd /tmp && git clone --mirror hunabapp-migration.bundle hunabapp-mirror.git"
|
||||
ssh hunab-prod "cd /tmp && git clone --mirror local_machine-migration.bundle local_machine-mirror.git"
|
||||
```
|
||||
|
||||
**Результат:** Bare репозиторий ~557M
|
||||
|
||||
#### Шаг 4: Копирование в директорию Gitea
|
||||
|
||||
```bash
|
||||
ssh hunab-prod "
|
||||
docker exec gitea mkdir -p /data/git/repositories/hunabgit/
|
||||
docker cp /tmp/hunabapp-mirror.git gitea:/data/git/repositories/hunabgit/hunabapp.git
|
||||
docker exec gitea chown -R git:git /data/git/repositories/hunabgit/hunabapp.git
|
||||
docker cp /tmp/local_machine-mirror.git gitea:/data/git/repositories/hunabgit/local_machine.git
|
||||
docker exec gitea chown -R git:git /data/git/repositories/hunabgit/local_machine.git
|
||||
"
|
||||
```
|
||||
|
||||
**Результат:** Репозиторий в Gitea (556.6M)
|
||||
|
||||
#### Шаг 5: Распаковка refs из packed-refs
|
||||
|
||||
**КРИТИЧЕСКИ ВАЖНО:** Gitea не видит ветки в `packed-refs`, нужно распаковать:
|
||||
|
||||
```bash
|
||||
ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'"
|
||||
ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'"
|
||||
```
|
||||
|
||||
#### Шаг 6: Обновление базы данных
|
||||
|
||||
```bash
|
||||
ssh hunab-prod "
|
||||
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'hunabapp';\"
|
||||
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'local_machine';\"
|
||||
docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini
|
||||
docker restart gitea
|
||||
"
|
||||
@@ -661,14 +605,14 @@ ssh hunab-prod "
|
||||
```bash
|
||||
# Проверка веток через API
|
||||
curl -s -H "Authorization: token YOUR_TOKEN" \
|
||||
https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/branches | jq '.[] | .name'
|
||||
https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/branches | jq '.[] | .name'
|
||||
|
||||
# Проверка файлов через API
|
||||
# Проверка файлов через API (ветка main)
|
||||
curl -s -H "Authorization: token YOUR_TOKEN" \
|
||||
https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/git/trees/dev?recursive=0 | jq '.tree[] | .path'
|
||||
https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/git/trees/main?recursive=0 | jq '.tree[] | .path'
|
||||
|
||||
# Проверка через веб-интерфейс
|
||||
https://gitea.hunab.app/hunabgit/hunabapp
|
||||
# Веб-интерфейс
|
||||
# https://gitea.hunab.app/hunabgit/local_machine
|
||||
```
|
||||
|
||||
**✅ Результат:** Репозиторий полностью мигрирован, все файлы доступны через веб-интерфейс и API.
|
||||
@@ -686,9 +630,9 @@ https://gitea.hunab.app/hunabgit/hunabapp
|
||||
#### Использование токена в git
|
||||
|
||||
```bash
|
||||
# В локальном репозитории
|
||||
cd /Users/eternal/code/hunabapp-dev
|
||||
git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/hunabapp.git
|
||||
# В локальном репозитории local_machine
|
||||
cd /Users/eternal/code/local_machine
|
||||
git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/local_machine.git
|
||||
```
|
||||
|
||||
---
|
||||
@@ -705,7 +649,7 @@ git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/hunabapp.git
|
||||
|
||||
```bash
|
||||
# 1. Получить ID репозитория
|
||||
REPO_ID=$(ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -t -c \"SELECT id FROM repository WHERE lower_name = 'hunabapp';\" | tr -d ' '")
|
||||
REPO_ID=$(ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -t -c \"SELECT id FROM repository WHERE lower_name = 'local_machine';\" | tr -d ' '")
|
||||
|
||||
# 2. Создать Release в базе данных
|
||||
ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"
|
||||
@@ -744,7 +688,7 @@ ssh hunab-prod "docker restart gitea"
|
||||
```
|
||||
|
||||
**Параметры:**
|
||||
- `repo_id` - ID репозитория (получить через `SELECT id FROM repository WHERE lower_name = 'hunabapp';`)
|
||||
- `repo_id` - ID репозитория (получить через `SELECT id FROM repository WHERE lower_name = 'local_machine';`)
|
||||
- `publisher_id` - ID пользователя (обычно 1 для первого пользователя)
|
||||
- `tag_name` - Имя тега (например, `v1.3.7-CRM`)
|
||||
- `lower_tag_name` - Имя тега в нижнем регистре (например, `v1.3.7-crm`)
|
||||
@@ -758,7 +702,7 @@ ssh hunab-prod "docker restart gitea"
|
||||
|
||||
Если нужно создать тег вручную:
|
||||
|
||||
1. Откройте: https://gitea.hunab.app/hunabgit/hunabapp/releases/new
|
||||
1. Откройте: https://gitea.hunab.app/hunabgit/local_machine/releases/new
|
||||
2. В поле "Tag" введите имя тега (например, `v1.3.7-CRM`)
|
||||
3. В поле "Target" выберите commit из списка или введите SHA
|
||||
4. Заполните Title и Description
|
||||
@@ -773,7 +717,7 @@ ssh hunab-prod "docker restart gitea"
|
||||
TOKEN=$(ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin user generate-access-token --username hunabgit --scopes 'write:repository' --name 'api-tag-creation' 2>&1 | grep -o '[a-z0-9]\{40\}' | head -1")
|
||||
|
||||
# 2. Создать Release через API
|
||||
curl -X POST "https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/releases" \
|
||||
curl -X POST "https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/releases" \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
@@ -807,7 +751,7 @@ git push origin v1.3.7-CRM # Даже после push тег не появит
|
||||
- [ ] Получить SHA коммита для тега
|
||||
- [ ] Создать Release в таблице `release` базы данных
|
||||
- [ ] Перезапустить Gitea для обновления индекса
|
||||
- [ ] Проверить что тег появился в веб-интерфейсе: https://gitea.hunab.app/hunabgit/hunabapp/releases
|
||||
- [ ] Проверить что тег появился в веб-интерфейсе: https://gitea.hunab.app/hunabgit/local_machine/releases
|
||||
|
||||
### 🔧 Скрипт для автоматического создания тега
|
||||
|
||||
@@ -886,13 +830,13 @@ openssl s_client -connect gitea.hunab.app:443 -servername gitea.hunab.app </dev/
|
||||
|
||||
```bash
|
||||
# Ветки
|
||||
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a | wc -l"
|
||||
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a | wc -l"
|
||||
|
||||
# Размер
|
||||
ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/hunabapp.git"
|
||||
ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/local_machine.git"
|
||||
|
||||
# Последний коммит
|
||||
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git log -1 --pretty=format:'%H %s'"
|
||||
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git log -1 --pretty=format:'%H %s'"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -939,10 +883,10 @@ ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin regenerate hooks --
|
||||
**Проверка:**
|
||||
```bash
|
||||
# Проверка прав на hooks
|
||||
ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/hunabapp.git/hooks/pre-receive"
|
||||
ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive"
|
||||
|
||||
# Проверка содержимого hooks
|
||||
ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/hunabapp.git/hooks/pre-receive.d/gitea"
|
||||
ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive.d/gitea"
|
||||
```
|
||||
|
||||
**Дополнительно:** Если проблема сохраняется, проверьте:
|
||||
@@ -1025,7 +969,7 @@ ssh hunab-prod "
|
||||
2. ✅ **PostgreSQL настроен** (отдельная БД hunabgit)
|
||||
3. ✅ **SSL/TLS настроен** (Let's Encrypt, A rating)
|
||||
4. ✅ **Nginx reverse proxy** (с оптимизацией)
|
||||
5. ✅ **Репозиторий hunabapp мигрирован** (556.8M, 23 ветки, все файлы доступны)
|
||||
5. ✅ **Репозиторий local_machine** (и при необходимости другие репо) доступен на Gitea
|
||||
6. ✅ **DNS настроен** (gitea.hunab.app → 209.38.32.21)
|
||||
7. ✅ **Веб-интерфейс работает** (файлы отображаются корректно)
|
||||
8. ✅ **API полностью функционален** (все endpoints работают)
|
||||
@@ -1097,20 +1041,21 @@ ssh hunab-prod "
|
||||
|
||||
## 📚 Полезные команды
|
||||
|
||||
**Для local_machine:**
|
||||
```bash
|
||||
# Push репозитория (РЕКОМЕНДУЕТСЯ)
|
||||
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
|
||||
cd /Users/eternal/code/local_machine
|
||||
git push origin main
|
||||
# или SSH (если HTTPS таймаутит): git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
|
||||
```
|
||||
|
||||
# 🇷🇺 ДЛЯ РФ: Push через прокси (ускоряет в 5-10 раз)
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]
|
||||
|
||||
# Gitea Admin Commands
|
||||
**Для админов сервера (Gitea Admin):**
|
||||
```bash
|
||||
docker exec gitea /usr/local/bin/gitea admin --help
|
||||
docker exec gitea /usr/local/bin/gitea admin regenerate hooks
|
||||
|
||||
# Git операции в Gitea
|
||||
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a
|
||||
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git log --oneline -10
|
||||
# Git операции в Gitea (репо local_machine)
|
||||
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a
|
||||
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git log --oneline -10
|
||||
|
||||
# PostgreSQL операции
|
||||
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c "SELECT * FROM repository;"
|
||||
@@ -1176,38 +1121,32 @@ bash scripts/deployment/mirror-all-repos-to-external.sh
|
||||
|
||||
- **Веб-интерфейс:** https://gitea.hunab.app
|
||||
- **API:** https://gitea.hunab.app/api/v1/
|
||||
- **Репозиторий:** https://gitea.hunab.app/hunabgit/hunabapp
|
||||
- **Репозиторий local_machine:** https://gitea.hunab.app/hunabgit/local_machine
|
||||
- **Быстрый старт local_machine:** [LOCAL_MACHINE_GITEA_SETUP.md](./LOCAL_MACHINE_GITEA_SETUP.md)
|
||||
- **Документация Gitea:** https://docs.gitea.io/
|
||||
- **SSL Report:** https://www.ssllabs.com/ssltest/analyze.html?d=gitea.hunab.app
|
||||
- **Зеркалирование:** [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md)
|
||||
- **Миграция на зеркальный сервер:** [Gitea Migration Plan](./GITEA_MIGRATION_TO_MIRROR_SERVER.md)
|
||||
- **Индекс документации:** [Gitea Index](./INDEX.md) - Перелинкованный индекс всех документов Gitea
|
||||
- **Зеркалирование:** [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md) (если есть)
|
||||
- **Миграция на зеркальный сервер:** [Gitea Migration Plan](./GITEA_MIGRATION_TO_MIRROR_SERVER.md) (если есть)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Текущий статус (2025-12-27)
|
||||
## ✅ Текущий статус
|
||||
|
||||
### Репозиторий hunabapp
|
||||
- ✅ **Размер:** 556.8M
|
||||
- ✅ **Ветки:** 23 (dev, main, и другие)
|
||||
- ✅ **Файлы:** Все файлы доступны через веб-интерфейс и API
|
||||
- ✅ **Веб-интерфейс:** Полностью функционален
|
||||
- ✅ **API:** Работает корректно
|
||||
- ✅ **Git операции:** Клонирование, push, pull работают
|
||||
### Репозиторий local_machine
|
||||
- **URL:** https://gitea.hunab.app/hunabgit/local_machine
|
||||
- **Ветка по умолчанию:** main
|
||||
- **Пуш:** `git push origin main` или SSH (порт 2223) — см. [LOCAL_MACHINE_GITEA_SETUP.md](./LOCAL_MACHINE_GITEA_SETUP.md)
|
||||
|
||||
### Система
|
||||
- ✅ Gitea контейнер работает стабильно
|
||||
- ✅ PostgreSQL подключение стабильно
|
||||
- ✅ SSL/TLS работает корректно
|
||||
- ✅ Nginx reverse proxy настроен правильно
|
||||
- ✅ DNS настроен (gitea.hunab.app → 209.38.32.21)
|
||||
### Система (сервер Gitea)
|
||||
- ✅ Gitea контейнер на 209.38.32.21
|
||||
- ✅ PostgreSQL, SSL/TLS, Nginx
|
||||
- ✅ DNS: gitea.hunab.app → 209.38.32.21
|
||||
|
||||
**Последнее обновление:** 2026-02-27
|
||||
**Версия документа:** 1.4
|
||||
**Статус:** ✅ Gitea полностью развернут и работает стабильно
|
||||
**Последнее обновление:** 2026-03-14
|
||||
**Версия документа:** 2.0 (адаптация под проект local_machine)
|
||||
**Изменения:**
|
||||
- Добавлен резервный способ коммитов через временный скрипт (2026-02-16)
|
||||
- Добавлен гайд по очистке истории Git от build-артефактов (2026-02-27)
|
||||
- Гайд переориентирован на репозиторий local_machine; примеры и команды приведены к этому проекту
|
||||
- Добавлена ссылка на LOCAL_MACHINE_GITEA_SETUP.md для быстрого старта
|
||||
|
||||
---
|
||||
|
||||
@@ -1217,7 +1156,9 @@ bash scripts/deployment/mirror-all-repos-to-external.sh
|
||||
|
||||
**99% случаев:** Файлы уже в `.gitignore`, но Git их отслеживает (закоммичены до добавления в `.gitignore`)
|
||||
|
||||
**Решение:** ✅ **Простое удаление из tracking** (см. ниже) - БЕЗ переписывания истории!
|
||||
**Решение:** ✅ **Простое удаление из tracking** (см. ниже) — БЕЗ переписывания истории!
|
||||
|
||||
*Пример ниже — для репо с frontend (build-артефакты). В local_machine: убедиться, что путь в .gitignore, затем `git rm --cached -r путь/` и коммит.*
|
||||
|
||||
**1% случаев:** Нужно удалить файлы из ВСЕЙ истории (например, случайно закоммитили секреты)
|
||||
|
||||
@@ -1258,8 +1199,8 @@ git commit -m "chore(frontend): удаление dist-* build-артефакто
|
||||
- Файлы были в .gitignore, но отслеживались Git (закоммичены до добавления в .gitignore)
|
||||
- Build-артефакты генерируются при деплое и не должны храниться в репозитории"
|
||||
|
||||
# 5. Push в Gitea
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev # 🇷🇺 ДЛЯ РФ
|
||||
# 5. Push в Gitea (для local_machine: git push origin main или SSH)
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev # в других репо; для local_machine: git push origin main
|
||||
|
||||
# 6. Проверка
|
||||
git ls-files | grep -E "^frontend/dist-(zero-downtime|fast-no-image|via-proxy|cache-busting)/" | wc -l
|
||||
@@ -1374,7 +1315,7 @@ brew install git-filter-repo
|
||||
# pip3 install git-filter-repo
|
||||
|
||||
# Запуск (бэкап создаётся скриптом автоматически)
|
||||
bash scripts/maintenance/git-history-cleanup-secrets.sh /path/to/hunabapp-dev
|
||||
bash scripts/maintenance/git-history-cleanup-secrets.sh /path/to/local_machine
|
||||
```
|
||||
|
||||
**Файлы:**
|
||||
@@ -1387,7 +1328,7 @@ bash scripts/maintenance/git-history-cleanup-secrets.sh /path/to/hunabapp-dev
|
||||
**Критично:** на production-сервере перед заменой репозитория в Gitea обязательно создать бэкап:
|
||||
|
||||
```bash
|
||||
GITEA_REPO="/data/git/repositories/hunabgit/hunabapp.git"
|
||||
GITEA_REPO="/data/git/repositories/hunabgit/local_machine.git"
|
||||
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"
|
||||
```
|
||||
|
||||
@@ -1611,7 +1552,7 @@ scp -i ~/.ssh/hunab_deploy_key /tmp/repo-clean.bundle hunab@209.38.32.21:/tmp/
|
||||
ssh hunab-prod "cd /tmp && git clone --mirror /tmp/repo-clean.bundle repo-clean.git"
|
||||
|
||||
# 4. Бэкап текущего Gitea репозитория
|
||||
GITEA_REPO="/data/git/repositories/hunabgit/hunabapp.git"
|
||||
GITEA_REPO="/data/git/repositories/hunabgit/local_machine.git"
|
||||
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"
|
||||
|
||||
# 5. Копировать очищенный репозиторий в контейнер
|
||||
@@ -1625,7 +1566,7 @@ ssh hunab-prod "docker exec gitea sh -c '
|
||||
cp -r /tmp/repo-clean.git/packed-refs . 2>/dev/null || true
|
||||
cp -r /tmp/repo-clean.git/refs/heads refs/ 2>/dev/null || true
|
||||
cp -r /tmp/repo-clean.git/refs/tags refs/ 2>/dev/null || true
|
||||
echo \"ref: refs/heads/dev\" > HEAD
|
||||
echo \"ref: refs/heads/main\" > HEAD
|
||||
rm -rf refs/original filter-repo
|
||||
git reflog expire --expire=now --all 2>/dev/null
|
||||
git gc --prune=now 2>&1 | tail -5
|
||||
@@ -1642,7 +1583,7 @@ ssh hunab-prod "docker exec gitea sh -c 'cd $GITEA_REPO && \
|
||||
echo \"Файлов dist в истории: \$(git rev-list --all --objects | grep -cE \"dist-zero-downtime|dist-fast-no-image|dist-via-proxy|dist-cache-busting\" || echo 0)\" && \
|
||||
echo \"Веток: \$(git branch | wc -l)\" && \
|
||||
echo \"Тегов: \$(git tag | wc -l)\" && \
|
||||
git log --oneline -3 dev'"
|
||||
git log --oneline -3 main'"
|
||||
```
|
||||
|
||||
**✅ Результат:** Gitea репозиторий обновлен очищенной версией
|
||||
@@ -1811,7 +1752,7 @@ ssh hunab-prod "docker exec gitea rm -rf $GITEA_REPO && \
|
||||
- [ ] Удалены файлы из tracking: `git rm --cached -r frontend/dist-*/`
|
||||
- [ ] Проверено что файлы в staging для удаления: `git status --short | grep "^D "`
|
||||
- [ ] Создан коммит с описанием удаления
|
||||
- [ ] Push в Gitea через скрипт: `bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev`
|
||||
- [ ] Push в Gitea (local_machine: `git push origin main` или SSH)
|
||||
- [ ] Проверено что файлы больше не отслеживаются: `git ls-files | grep dist- | wc -l` = 0
|
||||
- [ ] Обновлен CHANGELOG с описанием операции
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ bash scripts/deployment/gitea/setup-and-push.sh --create-only
|
||||
|
||||
### 2. Инициализация и первый пуш
|
||||
|
||||
**Важно:** сначала создай репозиторий на Gitea (шаг 1), иначе `git push` выдаст ошибку.
|
||||
|
||||
Из корня **local_machine** (не из родительского репо):
|
||||
|
||||
```bash
|
||||
@@ -36,14 +38,24 @@ bash scripts/deployment/gitea/setup-and-push.sh
|
||||
- добавляет `origin` (SSH или HTTPS);
|
||||
- делает первый коммит и `git push -u origin main`.
|
||||
|
||||
Если пуш упал (таймаут или «repository not found»): создай репо по шагу 1, затем снова выполни `git push -u origin main` из каталога `local_machine`.
|
||||
|
||||
### 3. Если пуш из РФ зависает
|
||||
|
||||
По гайду [GITEA_COMPLETE_GUIDE.md](./GITEA_COMPLETE_GUIDE.md):
|
||||
|
||||
- предпочтительно **SSH** (порт 2223):
|
||||
`git remote set-url origin git@gitea.hunab.app:hunabgit/local_machine.git`
|
||||
`git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git`
|
||||
и снова `git push -u origin main`;
|
||||
- либо использовать прокси/скрипты пуша из основного репо (bundle-метод), если они у вас настроены.
|
||||
- либо **пуш через прокси на зеркальный сервер 206.189.35.205** (как в hunabapp):
|
||||
|
||||
```bash
|
||||
# Из корня local_machine. Требуется: прокси hsites-ahau, ключ к 206, репо local_machine создан на Gitea на 206.
|
||||
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
# или с явным хостом: GITEA_SERVER=user@206.189.35.205 bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
|
||||
```
|
||||
|
||||
Скрипт пробует по порядку: SSH через прокси → HTTPS → bundle (копирование на сервер и `docker exec` в контейнер Gitea). Режим проверки без пуша: `bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run`.
|
||||
|
||||
## .gitignore
|
||||
|
||||
|
||||
211
scripts/deployment/gitea/git-push-gitea-via-proxy.sh
Executable file
211
scripts/deployment/gitea/git-push-gitea-via-proxy.sh
Executable file
@@ -0,0 +1,211 @@
|
||||
#!/bin/bash
|
||||
# Push local_machine to Gitea on server 206.189.35.205 via Proxy (🇷🇺 ДЛЯ РФ)
|
||||
# Same logic as hunabapp's script: SSH → HTTPS → Bundle
|
||||
# Repo: local_machine, branch: main, target: 206.189.35.205
|
||||
#
|
||||
# === БЕЗОПАСНОСТЬ (SAFETY) ===
|
||||
# - НЕ трогает текущую ветку, HEAD, рабочую копию, локальные коммиты.
|
||||
# - НЕ выполняет: git reset, git checkout --hard, git clean, push --force.
|
||||
# - Обновляет refs/remotes/origin/main только после успешного пуша.
|
||||
# - --dry-run: только показать, что будет запушено.
|
||||
|
||||
set -e
|
||||
|
||||
DRY_RUN=0
|
||||
[[ "${1:-}" = "--dry-run" ]] && DRY_RUN=1
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
BRANCH="${2:-main}"
|
||||
else
|
||||
BRANCH="${1:-main}"
|
||||
fi
|
||||
|
||||
# Target: Gitea on 206.189.35.205 (mirror server)
|
||||
# Optional: in ~/.ssh/config set Host gitea-mirror, HostName 206.189.35.205, User <your_user>
|
||||
# Then: export GITEA_SERVER=gitea-mirror
|
||||
GITEA_SSH_HOST="${GITEA_SSH_HOST:-206.189.35.205}"
|
||||
GITEA_SSH_PORT="${GITEA_SSH_PORT:-2223}"
|
||||
GITEA_SSH_URL="ssh://git@${GITEA_SSH_HOST}:${GITEA_SSH_PORT}/hunabgit/local_machine.git"
|
||||
# Host for scp/ssh (docker exec on 206). Default: IP. Or set GITEA_SERVER=gitea-mirror if in SSH config.
|
||||
SERVER="${GITEA_SERVER:-206.189.35.205}"
|
||||
# HTTPS (if mirror has own URL, set GITEA_HTTPS_BASE)
|
||||
GITEA_HTTPS_BASE="${GITEA_HTTPS_BASE:-https://gitea.hunab.app/hunabgit/local_machine.git}"
|
||||
|
||||
# Proxy (🇷🇺 ДЛЯ РФ)
|
||||
PROXY_SERVER="${PROXY_SERVER:-hsites-ahau}"
|
||||
PROXY_SSH_KEY="${PROXY_SSH_KEY:-$HOME/.ssh/id_ed25519}"
|
||||
TARGET_SSH_KEY="${TARGET_SSH_KEY:-$HOME/.ssh/hunab_deploy_key}"
|
||||
|
||||
PROXY_SSH_KEY_EXPANDED="${PROXY_SSH_KEY/#\~/$HOME}"
|
||||
TARGET_SSH_KEY_EXPANDED="${TARGET_SSH_KEY/#\~/$HOME}"
|
||||
PROXY_CMD="ssh -i $PROXY_SSH_KEY_EXPANDED -o StrictHostKeyChecking=accept-new -W %h:%p $PROXY_SERVER"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}=== Push local_machine to Gitea via Proxy (206.189.35.205) ===${NC}"
|
||||
echo -e "Branch: ${YELLOW}$BRANCH${NC}"
|
||||
echo -e "Proxy: ${YELLOW}$PROXY_SERVER${NC}"
|
||||
echo -e "Target: ${YELLOW}$GITEA_SSH_HOST${NC} (repo: local_machine)"
|
||||
echo ""
|
||||
|
||||
# Must be in local_machine repo
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [[ ! -f "$REPO_ROOT/.gitignore" ]] || [[ ! -d "$REPO_ROOT/docs" ]]; then
|
||||
echo -e "${RED}❌ Run from local_machine repo root.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}⚠️ origin/$BRANCH not found, fetching...${NC}"
|
||||
git fetch origin 2>/dev/null || true
|
||||
fi
|
||||
|
||||
LOCAL_COMMIT=$(git rev-parse $BRANCH 2>/dev/null || echo "")
|
||||
REMOTE_COMMIT=$(git rev-parse "origin/$BRANCH" 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$LOCAL_COMMIT" ]; then
|
||||
echo -e "${RED}❌ Error: Branch $BRANCH not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$REMOTE_COMMIT" ]; then
|
||||
echo -e "${YELLOW}⚠️ Remote branch not found, will create it${NC}"
|
||||
REMOTE_COMMIT=""
|
||||
elif [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
|
||||
echo -e "${GREEN}✅ No commits to push${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "Local commit: ${BLUE}$LOCAL_COMMIT${NC}"
|
||||
if [ -n "$REMOTE_COMMIT" ]; then
|
||||
echo -e "Remote commit: ${BLUE}$REMOTE_COMMIT${NC}"
|
||||
fi
|
||||
PENDING_COUNT=$(git rev-list --count "origin/$BRANCH..$BRANCH" 2>/dev/null || echo "?")
|
||||
echo -e "Commits to push: ${BLUE}$PENDING_COUNT${NC}"
|
||||
echo ""
|
||||
|
||||
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||
echo -e "${GREEN}✅ --dry-run: would push $PENDING_COUNT commit(s). No changes made.${NC}"
|
||||
echo " Run without --dry-run to push: bash $0 $BRANCH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Method 1: SSH via proxy
|
||||
echo -e "${YELLOW}[1/3] Trying SSH push...${NC}"
|
||||
CURRENT_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
|
||||
if ssh -p "$GITEA_SSH_PORT" -i "$TARGET_SSH_KEY_EXPANDED" -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -o ProxyCommand="$PROXY_CMD" "git@$GITEA_SSH_HOST" "echo 'OK'" >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ SSH connection available${NC}"
|
||||
if [ -z "$CURRENT_URL" ] || [[ ! "$CURRENT_URL" =~ ssh:// ]]; then
|
||||
echo -e "${YELLOW}Switching to SSH URL...${NC}"
|
||||
git remote set-url origin "$GITEA_SSH_URL"
|
||||
fi
|
||||
GIT_SSH_COMMAND="ssh -i $TARGET_SSH_KEY_EXPANDED -p $GITEA_SSH_PORT -o ProxyCommand='$PROXY_CMD' -o StrictHostKeyChecking=accept-new"
|
||||
if GIT_SSH_COMMAND="$GIT_SSH_COMMAND" git push origin "$BRANCH" 2>&1; then
|
||||
echo -e "${GREEN}✅ Push successful via SSH!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ SSH push failed, trying HTTPS...${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ SSH not available, trying HTTPS...${NC}"
|
||||
fi
|
||||
|
||||
# Method 2: HTTPS
|
||||
echo -e "${YELLOW}[2/3] Trying HTTPS push...${NC}"
|
||||
if [ -z "$CURRENT_URL" ] || [[ "$CURRENT_URL" =~ ssh:// ]]; then
|
||||
git remote set-url origin "$GITEA_HTTPS_BASE"
|
||||
fi
|
||||
git config http.postBuffer 524288000
|
||||
git config http.lowSpeedLimit 0
|
||||
git config http.lowSpeedTime 0
|
||||
git config http.timeout 600
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
if timeout 30 git push origin "$BRANCH" 2>&1; then
|
||||
echo -e "${GREEN}✅ Push successful via HTTPS!${NC}"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Skipping HTTPS (no timeout cmd), using bundle...${NC}"
|
||||
fi
|
||||
|
||||
# Method 3: Bundle
|
||||
echo -e "${YELLOW}[3/3] Using bundle method...${NC}"
|
||||
BUNDLE_BASE="$REMOTE_COMMIT"
|
||||
if git remote get-url gitea >/dev/null 2>&1; then
|
||||
GIT_SSH_COMMAND_FETCH="ssh -i $TARGET_SSH_KEY_EXPANDED -p $GITEA_SSH_PORT -o ProxyCommand='$PROXY_CMD' -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15"
|
||||
if GIT_SSH_COMMAND="$GIT_SSH_COMMAND_FETCH" git fetch gitea "$BRANCH" 2>/dev/null; then
|
||||
GITEA_COMMIT=$(git rev-parse "gitea/$BRANCH" 2>/dev/null || echo "")
|
||||
[ -n "$GITEA_COMMIT" ] && BUNDLE_BASE="$GITEA_COMMIT" && echo -e "Bundle base (gitea/$BRANCH): ${BLUE}$GITEA_COMMIT${NC}"
|
||||
fi
|
||||
fi
|
||||
if [ "$BUNDLE_BASE" = "$REMOTE_COMMIT" ]; then
|
||||
echo -e "${YELLOW}Querying Gitea refs/heads/$BRANCH on server...${NC}"
|
||||
GITEA_COMMIT=$(ssh -i "$TARGET_SSH_KEY_EXPANDED" -o ProxyCommand="$PROXY_CMD" "$SERVER" "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && git rev-parse refs/heads/$BRANCH'" 2>/dev/null || echo "")
|
||||
if [ -n "$GITEA_COMMIT" ]; then
|
||||
BUNDLE_BASE="$GITEA_COMMIT"
|
||||
echo -e "Bundle base (Gitea on server): ${BLUE}$GITEA_COMMIT${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Could not get server ref (repo may be new). Using full branch bundle.${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$REPO_ROOT/temp"
|
||||
BUNDLE_FILE="$REPO_ROOT/temp/gitea-push-${BRANCH}-$(date +%s).bundle"
|
||||
echo "Creating bundle: $BUNDLE_FILE"
|
||||
if [ -n "$BUNDLE_BASE" ] && ! git cat-file -e "$BUNDLE_BASE^{commit}" 2>/dev/null; then
|
||||
echo -e "${YELLOW}⚠️ Server commit not in local repo. Creating full branch bundle.${NC}"
|
||||
BUNDLE_BASE=""
|
||||
fi
|
||||
BUNDLE_FAILED=0
|
||||
if [ -n "$BUNDLE_BASE" ]; then
|
||||
git bundle create "$BUNDLE_FILE" "$BUNDLE_BASE..$BRANCH" || BUNDLE_FAILED=1
|
||||
else
|
||||
git bundle create "$BUNDLE_FILE" "$BRANCH" || BUNDLE_FAILED=1
|
||||
fi
|
||||
if [ "$BUNDLE_FAILED" -eq 1 ]; then
|
||||
echo -e "${RED}❌ Bundle creation failed.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUNDLE_SIZE=$(du -h "$BUNDLE_FILE" | cut -f1)
|
||||
echo "Bundle size: $BUNDLE_SIZE"
|
||||
echo "Copying bundle to server via proxy..."
|
||||
scp -i "$TARGET_SSH_KEY_EXPANDED" -o ProxyCommand="$PROXY_CMD" "$BUNDLE_FILE" "$SERVER:/tmp/$(basename "$BUNDLE_FILE")" || {
|
||||
echo -e "${RED}❌ Failed to copy bundle.${NC}"
|
||||
echo -e " Check: GITEA_SERVER=$SERVER (e.g. 206.189.35.205 or Host from ~/.ssh/config), proxy $PROXY_SERVER reachable."
|
||||
rm -f "$BUNDLE_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
NEW_COMMIT=$(git bundle list-heads "$BUNDLE_FILE" | grep "^$BRANCH" | cut -d" " -f1)
|
||||
[ -z "$NEW_COMMIT" ] && NEW_COMMIT=$(git bundle list-heads "$BUNDLE_FILE" | head -1 | cut -d" " -f1)
|
||||
if [ -z "$NEW_COMMIT" ]; then
|
||||
echo -e "${RED}❌ Could not find commit in bundle${NC}"
|
||||
rm -f "$BUNDLE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUNDLE_BASENAME=$(basename "$BUNDLE_FILE")
|
||||
APPLY_OUT=$(mktemp 2>/dev/null || echo "/tmp/gitea-apply-$$.out")
|
||||
if ! ssh -i "$TARGET_SSH_KEY_EXPANDED" -o ProxyCommand="$PROXY_CMD" "$SERVER" "docker cp /tmp/$BUNDLE_BASENAME gitea:/tmp/push-bundle.bundle && docker exec -e BRANCH='$BRANCH' -e NEW_COMMIT='$NEW_COMMIT' gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && git bundle unbundle /tmp/push-bundle.bundle && git update-ref refs/heads/\$BRANCH \$NEW_COMMIT && rm -f /tmp/push-bundle.bundle' && rm -f /tmp/$BUNDLE_BASENAME" 2>"$APPLY_OUT"; then
|
||||
echo -e "${RED}❌ Failed to apply bundle${NC}"
|
||||
[ -s "$APPLY_OUT" ] && cat "$APPLY_OUT"
|
||||
echo -e "${YELLOW}→ Ensure repo exists on 206: Gitea UI → New Repository → local_machine (empty).${NC}"
|
||||
echo " Then: git remote add gitea $GITEA_SSH_URL && git fetch gitea $BRANCH"
|
||||
echo " And re-run: bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh $BRANCH"
|
||||
rm -f "$APPLY_OUT" "$BUNDLE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$APPLY_OUT" "$BUNDLE_FILE"
|
||||
|
||||
if [ "$NEW_COMMIT" = "$LOCAL_COMMIT" ]; then
|
||||
echo -e "${YELLOW}Updating local tracking ref (origin/$BRANCH)...${NC}"
|
||||
git update-ref "refs/remotes/origin/$BRANCH" "$NEW_COMMIT" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Push completed via bundle!${NC}"
|
||||
echo -e " Commit: ${BLUE}$NEW_COMMIT${NC}"
|
||||
0
scripts/games/README.md
Normal file
0
scripts/games/README.md
Normal file
2
scripts/games/cache/.gitignore
vendored
Normal file
2
scripts/games/cache/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
214
scripts/games/config.sh
Executable file
214
scripts/games/config.sh
Executable file
@@ -0,0 +1,214 @@
|
||||
# Shared paths for HoMM / Whisky (source from other scripts in this folder).
|
||||
GAMES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GAMES_CACHE_DIR="${GAMES_DIR}/cache"
|
||||
|
||||
BOTTLE_ID="38412122-AAE1-4831-9F2E-6A0EB364B3F9"
|
||||
BOTTLE_NAME="HoMM-OldenEra"
|
||||
# franke Whisky 3 (Whisky.app) — канон; isaacmarovitz legacy после migrate-bottle-to-franke-whisky.sh
|
||||
_FRANKE_BOTTLE="${HOME}/Library/Containers/com.franke.Whisky/Bottles/${BOTTLE_ID}"
|
||||
_ISAAC_BOTTLE="${HOME}/Library/Containers/com.isaacmarovitz.Whisky/Bottles/${BOTTLE_ID}"
|
||||
if [[ -d "${_FRANKE_BOTTLE}/drive_c" ]]; then
|
||||
BOTTLE_DIR="${_FRANKE_BOTTLE}"
|
||||
WHISKY_LIBS="${HOME}/Library/Application Support/com.franke.Whisky"
|
||||
else
|
||||
BOTTLE_DIR="${_ISAAC_BOTTLE}"
|
||||
WHISKY_LIBS="${HOME}/Library/Application Support/com.isaacmarovitz.Whisky"
|
||||
fi
|
||||
unset _FRANKE_BOTTLE _ISAAC_BOTTLE
|
||||
WINE64="${WHISKY_LIBS}/Libraries/Wine/bin/wine64"
|
||||
WINE_LIB="${WHISKY_LIBS}/Libraries/Wine/lib"
|
||||
STEAM_DIR="${BOTTLE_DIR}/drive_c/Program Files (x86)/Steam"
|
||||
STEAM_EXE="${STEAM_DIR}/Steam.exe"
|
||||
STEAM_SETUP="${GAMES_CACHE_DIR}/SteamSetup.exe"
|
||||
LIBRARIES_TAR="${GAMES_CACHE_DIR}/Libraries.tar.gz"
|
||||
|
||||
# GOG Galaxy 2.0 (полный offline setup ~290 MB; web stub GOG_Galaxy_2.0.exe — только загрузчик)
|
||||
GOG_SETUP="${GAMES_CACHE_DIR}/setup_galaxy.exe"
|
||||
GOG_SETUP_URL="${GOG_SETUP_URL:-}"
|
||||
GOG_DIR_X86="${BOTTLE_DIR}/drive_c/Program Files (x86)/GOG Galaxy"
|
||||
GOG_DIR_X64="${BOTTLE_DIR}/drive_c/Program Files/GOG Galaxy"
|
||||
GOG_EXE="${GOG_DIR_X86}/GalaxyClient.exe"
|
||||
# CEF (как Steam/Whisky #1220). Без -cef-in-process-gpu — меньше gpu-process CPU spin.
|
||||
GOG_GALAXY_BASE_ARGS="${GOG_GALAXY_BASE_ARGS:-/runWithoutUpdating /deelevated}"
|
||||
GOG_GALAXY_CEF_ARGS="${GOG_GALAXY_CEF_ARGS:--allosarches -cef-force-32bit -no-cef-sandbox -cef-disable-gpu}"
|
||||
|
||||
homm_gog_cef_args_line() {
|
||||
printf '%s %s' "${GOG_GALAXY_BASE_ARGS}" "${GOG_GALAXY_CEF_ARGS}"
|
||||
}
|
||||
|
||||
homm_gog_cef_args_array() {
|
||||
local _IFS=$IFS
|
||||
IFS=' '
|
||||
# shellcheck disable=SC2206
|
||||
local -a _a=(${GOG_GALAXY_BASE_ARGS} ${GOG_GALAXY_CEF_ARGS})
|
||||
IFS=$_IFS
|
||||
printf '%s\n' "${_a[@]}"
|
||||
}
|
||||
|
||||
homm_gog_exe() {
|
||||
if [[ -f "${GOG_EXE}" ]]; then
|
||||
echo "${GOG_EXE}"
|
||||
return 0
|
||||
fi
|
||||
local found
|
||||
found="$(find "${BOTTLE_DIR}/drive_c/Program Files"* -maxdepth 3 -name 'GalaxyClient.exe' 2>/dev/null | head -1)"
|
||||
if [[ -n "${found}" ]]; then
|
||||
echo "${found}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
homm_wine_gog_running() {
|
||||
pgrep -f "${BOTTLE_ID}.*[Gg]alaxy[Cc]lient" >/dev/null 2>&1 \
|
||||
|| pgrep -f 'GOG Galaxy.*GalaxyClient' >/dev/null 2>&1 \
|
||||
|| pgrep -f 'GalaxyClient\.exe' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
export_wine_env_gog() {
|
||||
export_wine_env
|
||||
unset WINEDLLOVERRIDES 2>/dev/null || true
|
||||
export WINEDEBUG="${WINEDEBUG:--all}"
|
||||
}
|
||||
|
||||
# Heroes of Might and Magic: Olden Era (Steam)
|
||||
HOMM_APP_ID="3105440"
|
||||
HOMM_DIR="${STEAM_DIR}/steamapps/common/Heroes of Might and Magic Olden Era"
|
||||
HOMM_EXE="${HOMM_DIR}/HeroesOldenEra.exe"
|
||||
WINEPATH_BIN="${WHISKY_LIBS}/Libraries/Wine/bin/winepath"
|
||||
|
||||
# Запуск игры: по умолчанию виртуальный стол Wine (часто лечит «двигается, ЛКМ нет»).
|
||||
# HOMM_USE_WINEVDESKTOP=0 — запуск напрямую без explorer /desktop (быстрый A/B тест).
|
||||
# HOMM_LAUNCH_DIRECT=1 — legacy-переключатель прямого запуска (оставлен для совместимости).
|
||||
# HOMM_VDESKTOP=WxH — размер виртуального стола.
|
||||
#
|
||||
# По умолчанию Unity принудительно в оконном режиме (-screen-fullscreen 0 …):
|
||||
# это часто восстанавливает ЛКМ на macOS+Wine при багах полноэкранного режима.
|
||||
# Выключить: HOMM_UNITY_WINDOWED=0
|
||||
HOMM_VDESKTOP="${HOMM_VDESKTOP:-1920x1080}"
|
||||
HOMM_USE_WINEVDESKTOP="${HOMM_USE_WINEVDESKTOP:-1}"
|
||||
HOMM_LAUNCH_DIRECT="${HOMM_LAUNCH_DIRECT:-0}"
|
||||
HOMM_UNITY_WINDOWED="${HOMM_UNITY_WINDOWED:-1}"
|
||||
# Дополнительные аргументы Unity через пробел, напр.: "-popupwindow"
|
||||
HOMM_UNITY_EXTRA="${HOMM_UNITY_EXTRA:-}"
|
||||
|
||||
# Steam CEF (steamwebhelper): без этих флагов в Whisky/Wine часто нет окна UI
|
||||
# (cef_log: message_window.cc Class does not exist, webhelper в цикле перезапуска).
|
||||
# Источник: Whisky #1220 — -allosarches -cef-force-32bit -cef-in-process-gpu -no-cef-sandbox
|
||||
HOMM_STEAM_CEF_ARGS="${HOMM_STEAM_CEF_ARGS:--allosarches -cef-force-32bit -cef-in-process-gpu -no-cef-sandbox -cef-disable-gpu}"
|
||||
# HOMM_STEAM_VDESKTOP=1 — запуск через explorer /desktop (если окно «теряется»)
|
||||
HOMM_STEAM_VDESKTOP="${HOMM_STEAM_VDESKTOP:-0}"
|
||||
|
||||
# Диск: APFS Data volume. Guard: block < HOMM_DISK_MIN_GB (3), warn < HOMM_DISK_WARN_PCT (10).
|
||||
homm_data_volume_path() {
|
||||
if [[ -d /System/Volumes/Data ]]; then
|
||||
echo /System/Volumes/Data
|
||||
elif [[ -n "${HOME:-}" && -d "${HOME}" ]]; then
|
||||
echo "${HOME}"
|
||||
else
|
||||
echo /
|
||||
fi
|
||||
}
|
||||
|
||||
homm_disk_free_kb() {
|
||||
df -k "$(homm_data_volume_path)" | awk 'NR==2 {print $4}'
|
||||
}
|
||||
|
||||
homm_disk_total_kb() {
|
||||
df -k "$(homm_data_volume_path)" | awk 'NR==2 {print $2}'
|
||||
}
|
||||
|
||||
homm_disk_used_pct() {
|
||||
df -k "$(homm_data_volume_path)" | awk 'NR==2 {gsub(/%/,"",$5); print $5}'
|
||||
}
|
||||
|
||||
homm_disk_free_pct() {
|
||||
local free_kb total_kb
|
||||
free_kb="$(homm_disk_free_kb)"
|
||||
total_kb="$(homm_disk_total_kb)"
|
||||
if [[ "${total_kb}" -gt 0 ]]; then
|
||||
echo $(( free_kb * 100 / total_kb ))
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
export_wine_env() {
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export WINEMSYNC=1
|
||||
export WINEESYNC=1
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
}
|
||||
|
||||
# Steam UI: без DXVK; dwrite=n,b — классический фикс пустого текста VGUI (см. fix-homm-steam-ui.sh).
|
||||
export_wine_env_steam() {
|
||||
export_wine_env
|
||||
unset DXVK_ASYNC DXVK_HUD 2>/dev/null || true
|
||||
export WINEDLLOVERRIDES="dwrite=n,b"
|
||||
}
|
||||
|
||||
homm_kill_mac_steam() {
|
||||
pkill -9 -f 'Steam.AppBundle/Steam/Contents/MacOS/steam_osx' 2>/dev/null || true
|
||||
pkill -9 -f 'Steam.AppBundle/Steam/Contents/MacOS/ipcserver' 2>/dev/null || true
|
||||
pkill -9 -f 'Steam Helper.app/Contents/MacOS/Steam Helper' 2>/dev/null || true
|
||||
}
|
||||
|
||||
homm_steam_cef_args() {
|
||||
# shellcheck disable=SC2206
|
||||
echo ${HOMM_STEAM_CEF_ARGS}
|
||||
}
|
||||
|
||||
homm_steam_connection_log() {
|
||||
echo "${STEAM_DIR}/logs/connection_log.txt"
|
||||
}
|
||||
|
||||
# Последняя сессия в connection_log — Logged On?
|
||||
homm_steam_logged_on() {
|
||||
local log
|
||||
log="$(homm_steam_connection_log)"
|
||||
[[ -f "${log}" ]] || return 1
|
||||
tail -25 "${log}" | grep -qE '\[Logged On'
|
||||
}
|
||||
|
||||
# Steam.exe в bottle (Terminal start.sh или Whisky GUI — argv часто C:\…\Steam\steam.exe).
|
||||
# В regex избегать \S в "\Steam" — иначе ложные совпадения; используйте [/\\] или .
|
||||
homm_wine_steam_running() {
|
||||
pgrep -f "${BOTTLE_ID}.*[Ss]team\\.exe" >/dev/null 2>&1 \
|
||||
|| pgrep -f "Whisky/Bottles/${BOTTLE_ID}.*[Ss]team\\.exe" >/dev/null 2>&1 \
|
||||
|| pgrep -f 'Steam[/\\]steam\.exe' >/dev/null 2>&1 \
|
||||
|| pgrep -f 'steamwebhelper\.exe' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Logged On + живой клиент (для play/game и сейвов).
|
||||
homm_steam_ready_for_game() {
|
||||
homm_steam_logged_on || return 1
|
||||
homm_wine_steam_running
|
||||
}
|
||||
|
||||
# Нативный Steam.app на Mac мешает bottle (login / CM / ipc).
|
||||
homm_mac_steam_running() {
|
||||
pgrep -f 'Steam\.AppBundle/Steam/Contents/MacOS/steam_osx' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
homm_warn_mac_steam() {
|
||||
if homm_mac_steam_running; then
|
||||
echo "⚠️ Запущен нативный Steam на Mac (часто остаётся после удаления Steam.app из /Applications)."
|
||||
echo " Папка: ~/Library/Application Support/Steam/Steam.AppBundle/"
|
||||
echo " Остановить:"
|
||||
echo " pkill -f 'Steam.AppBundle/Steam/Contents/MacOS/steam_osx'"
|
||||
echo " pkill -f 'Steam.AppBundle/Steam/Contents/MacOS/ipcserver'"
|
||||
echo " Затем: bash ${GAMES_DIR}/start.sh reset && bash ${GAMES_DIR}/start.sh steam-ui"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Olden Era (Unity DX11): Gcenx DXVK-macOS в system32 — см. install-dxvk-macos.sh
|
||||
export_wine_env_game() {
|
||||
export_wine_env
|
||||
export WINEDLLOVERRIDES="d3d11,d3d10core=n,b"
|
||||
export DXVK_ASYNC=1
|
||||
export DXVK_HUD=0
|
||||
# Меньше конфликтов fullscreen с macOS Stage Manager / края экрана
|
||||
export SDL_VIDEO_MAC_FULLSCREEN_SPACES="${SDL_VIDEO_MAC_FULLSCREEN_SPACES:-0}"
|
||||
}
|
||||
46
scripts/games/diagnose-gog-auth.sh
Executable file
46
scripts/games/diagnose-gog-auth.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# Снимок логов GOG login + краткий разбор (auth.gog.com/token, refreshToken).
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
LOGDIR="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs"
|
||||
OUT="${GAMES_CACHE_DIR}/gog-auth-diagnose-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "${OUT}"
|
||||
|
||||
for f in GalaxyClient.log cef.log ClientHelper-Authentication.log GalaxyClientService.log; do
|
||||
[[ -f "${LOGDIR}/${f}" ]] && cp -f "${LOGDIR}/${f}" "${OUT}/"
|
||||
done
|
||||
|
||||
{
|
||||
echo "=== $(date) ==="
|
||||
echo "Bottle: ${BOTTLE_NAME} (${BOTTLE_ID})"
|
||||
df -h "$(homm_data_volume_path)" | awk 'NR==2'
|
||||
echo ""
|
||||
echo "=== proxy (macOS) ==="
|
||||
scutil --proxy 2>/dev/null | head -12
|
||||
echo ""
|
||||
echo "=== GOG processes ==="
|
||||
pgrep -fl 'GalaxyClient' 2>/dev/null || echo "(none)"
|
||||
echo ""
|
||||
echo "=== refreshToken в реестре ==="
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
"${WINE64}" reg query 'HKCU\Software\GOG.com\Galaxy' /v refreshToken 2>&1 || echo "нет refreshToken"
|
||||
echo ""
|
||||
echo "=== последние auth-события (GalaxyClient.log) ==="
|
||||
grep -iE 'Authentication failed|auth\.gog\.com/token|refreshToken|on_login_success|timeout.*gog|API status' \
|
||||
"${LOGDIR}/GalaxyClient.log" 2>/dev/null | tail -25
|
||||
echo ""
|
||||
echo "=== CEF login (cef.log) ==="
|
||||
grep -iE 'ERROR|message from source|recaptcha|frame-ancestors' "${LOGDIR}/cef.log" 2>/dev/null | tail -15
|
||||
echo ""
|
||||
echo "=== Mac → auth.gog.com/token ==="
|
||||
curl -sS -o /dev/null -w "http:%{http_code} total:%{time_total}s\n" --max-time 15 \
|
||||
-X POST "https://auth.gog.com/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=authorization_code&client_id=x&code=x" 2>&1 || echo "curl FAIL"
|
||||
} > "${OUT}/summary.txt"
|
||||
|
||||
echo "Архив: ${OUT}"
|
||||
cat "${OUT}/summary.txt"
|
||||
4
scripts/games/diagnose-gog-ja3-session.sh
Executable file
4
scripts/games/diagnose-gog-ja3-session.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)/gog"
|
||||
exec bash "${GOG_DIR}/diagnose-session.sh" "$@"
|
||||
41
scripts/games/diagnose-homm-mouse.sh
Executable file
41
scripts/games/diagnose-homm-mouse.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Диагностика «курсор двигается, клики не работают» для HoMM Olden Era в Whisky.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
LOG="${BOTTLE_DIR}/drive_c/users/${USER}/AppData/LocalLow/Unfrozen/HeroesOldenEra/Player.log"
|
||||
LOG_PREV="${BOTTLE_DIR}/drive_c/users/${USER}/AppData/LocalLow/Unfrozen/HeroesOldenEra/Player-prev.log"
|
||||
|
||||
echo "Bottle: ${BOTTLE_NAME}"
|
||||
echo "Player.log: ${LOG}"
|
||||
echo ""
|
||||
|
||||
if [[ ! -f "${LOG}" ]]; then
|
||||
echo "Нет Player.log — хотя бы один раз запустите игру."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -q 'EnableMouseInPointer failed' "${LOG}" 2>/dev/null; then
|
||||
echo "ДИАГНОЗ: Unity + Wine — заглушка EnableMouseInPointer (WM_POINTER)."
|
||||
echo "Строка из лога:"
|
||||
grep -n 'EnableMouseInPointer' "${LOG}" | head -5 || true
|
||||
echo ""
|
||||
echo "WineBus / виртуальный desktop / SDL это НЕ лечат."
|
||||
echo ""
|
||||
echo "Рабочие пути:"
|
||||
echo " 1) CrossOver (macOS) + при необходимости патч:"
|
||||
echo " https://github.com/dabielf/crossover-unity-mouse-fix"
|
||||
echo " 2) Ждать WhiskyWine/Wine с upstream-фиксом (см. Wine bug #53847)."
|
||||
echo ""
|
||||
DOCS="$(cd "${GAMES_DIR}/../.." && pwd)/docs/games/UNITY_POINTER_MOUSE_FIX.md"
|
||||
echo "Подробности: ${DOCS}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Строка EnableMouseInPointer в текущем Player.log не найдена."
|
||||
if [[ -f "${LOG_PREV}" ]] && grep -q 'EnableMouseInPointer failed' "${LOG_PREV}" 2>/dev/null; then
|
||||
echo "(В Player-prev.log она была — перезапустите игру и снова этот скрипт.)"
|
||||
fi
|
||||
echo "Если клики всё равно мертвы — пришлите первые 80 строк Player.log в поддержку."
|
||||
88
scripts/games/diagnose-homm-saves.sh
Executable file
88
scripts/games/diagnose-homm-saves.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# Сейвы 1/2/3: файлы на диске vs видимость в игре (Steam API / login).
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
CONN_LOG="${STEAM_DIR}/logs/connection_log.txt"
|
||||
PLAYER_LOG="${BOTTLE_DIR}/drive_c/users/${USER}/AppData/LocalLow/Unfrozen/HeroesOldenEra/Player.log"
|
||||
USERS_ROOT="${BOTTLE_DIR}/drive_c/users/${USER}/AppData/LocalLow/Unfrozen/HeroesOldenEra/users"
|
||||
|
||||
echo "=== HoMM Olden Era — диагностика сейвов ==="
|
||||
echo "Bottle: ${BOTTLE_NAME}"
|
||||
echo ""
|
||||
|
||||
# --- файлы на диске ---
|
||||
if [[ -d "${USERS_ROOT}" ]]; then
|
||||
echo "Профили users/:"
|
||||
ls -1 "${USERS_ROOT}" 2>/dev/null || true
|
||||
sp="$(find "${USERS_ROOT}" -type d -path '*/saves/singleplayer' 2>/dev/null | head -1)"
|
||||
if [[ -n "${sp}" ]]; then
|
||||
echo ""
|
||||
echo "singleplayer на диске: ${sp}/"
|
||||
ls -lh "${sp}"/*.saveskirmish 2>/dev/null || echo " (нет *.saveskirmish)"
|
||||
else
|
||||
echo "singleplayer: не найден"
|
||||
fi
|
||||
else
|
||||
echo "Нет ${USERS_ROOT} — игра ещё не создавала профиль."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# --- Steam login ---
|
||||
if [[ -f "${CONN_LOG}" ]]; then
|
||||
echo "Steam connection_log (хвост):"
|
||||
tail -4 "${CONN_LOG}" | sed 's/^/ /'
|
||||
if tail -20 "${CONN_LOG}" | grep -qE '\[Logged On'; then
|
||||
echo " → Steam в bottle: Logged On (OK для сейвов)"
|
||||
STEAM_OK=1
|
||||
else
|
||||
echo " → Steam в bottle: Logged Off (сейвы в меню не подтянутся)"
|
||||
STEAM_OK=0
|
||||
fi
|
||||
else
|
||||
echo "Нет connection_log.txt"
|
||||
STEAM_OK=0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# --- Player.log ---
|
||||
if [[ -f "${PLAYER_LOG}" ]]; then
|
||||
if grep -q 'SteamApi_Init failed\|ConnectToGlobalUser failed' "${PLAYER_LOG}" 2>/dev/null; then
|
||||
echo "Player.log: SteamApi_Init FAILED — игра без живого Steam-клиента."
|
||||
echo " (файлы на диске могут быть, слоты в UI пустые)"
|
||||
API_OK=0
|
||||
elif grep -q 'Steam is probably not running' "${PLAYER_LOG}" 2>/dev/null; then
|
||||
echo "Player.log: Steam is probably not running"
|
||||
API_OK=0
|
||||
else
|
||||
echo "Player.log: явных ошибок Steam API в последнем запуске нет."
|
||||
API_OK=1
|
||||
fi
|
||||
if grep -q 'FileManager.*saves' "${PLAYER_LOG}" 2>/dev/null; then
|
||||
echo " FileManager видит папку saves (путь профиля на диске совпадает)."
|
||||
fi
|
||||
else
|
||||
echo "Нет Player.log — сначала запустите игру."
|
||||
API_OK=0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ "${STEAM_OK:-0}" == "1" && "${API_OK:-0}" == "1" ]]; then
|
||||
echo "ВЕРДИКТ: контур сейвов выглядит здоровым. В игре: Загрузить → skirmish → слоты 1/2/3."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "ВЕРДИКТ: игра могла открыться, но сейвы не подтянутся, пока Steam не Logged On."
|
||||
echo ""
|
||||
echo "Цикл:"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh reset"
|
||||
echo " bash ${SCRIPT_DIR}/fix-homm-steam-ui.sh # один раз, если пустой UI Steam"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh steam-ui # login в аккаунт"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh play # или Play в библиотеке"
|
||||
echo ""
|
||||
echo "Не использовать для сейвов: start.sh game без Logged On Steam."
|
||||
exit 1
|
||||
74
scripts/games/diagnose-homm-steam-ui.sh
Executable file
74
scripts/games/diagnose-homm-steam-ui.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env bash
|
||||
# Почему нет окна Wine-Steam после start.sh steam-ui.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
CEF_LOG="${STEAM_DIR}/logs/cef_log.txt"
|
||||
CONSOLE_LOG="${STEAM_DIR}/logs/console_log.txt"
|
||||
|
||||
echo "=== Wine-Steam UI — диагностика ==="
|
||||
echo "Bottle: ${BOTTLE_NAME} (${BOTTLE_ID})"
|
||||
echo ""
|
||||
|
||||
echo "1) Mac Steam (мешает login / CEF):"
|
||||
if homm_mac_steam_running; then
|
||||
echo " ❌ Запущен steam_osx — killall steam_osx ipcserver"
|
||||
else
|
||||
echo " ✅ Mac Steam не мешает"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "2) Процессы bottle:"
|
||||
if pgrep -f "${BOTTLE_ID}.*Steam\\.exe" >/dev/null 2>&1; then
|
||||
echo " ✅ Steam.exe"
|
||||
pgrep -lf "${BOTTLE_ID}.*Steam\\.exe" 2>/dev/null | head -2 | sed 's/^/ /'
|
||||
else
|
||||
echo " ❌ Steam.exe не запущен — bash ${SCRIPT_DIR}/start.sh steam-ui"
|
||||
fi
|
||||
if pgrep -f "${BOTTLE_ID}.*steamwebhelper" >/dev/null 2>&1 || pgrep -f "Whisky/Bottles/${BOTTLE_ID}.*steamwebhelper" >/dev/null 2>&1; then
|
||||
echo " ✅ steamwebhelper (CEF UI)"
|
||||
pgrep -lf 'steamwebhelper' 2>/dev/null | grep -E "${BOTTLE_ID}|Program Files \(x86\)\\\\Steam" | head -2 | sed 's/^/ /' || pgrep -lf steamwebhelper | head -1 | sed 's/^/ /'
|
||||
else
|
||||
echo " ❌ steamwebhelper нет — окно Steam (login) не появится"
|
||||
echo " Типично: cef_log → message_window.cc Class does not exist"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "3) CEF-лог (последние ошибки):"
|
||||
if [[ -f "${CEF_LOG}" ]]; then
|
||||
if tail -40 "${CEF_LOG}" | grep -q 'message_window.cc'; then
|
||||
echo " ❌ message_window.cc — steamwebhelper падает / перезапускается"
|
||||
tail -40 "${CEF_LOG}" | grep 'message_window\|chrome_elf\|LdrInitialize' | tail -3 | sed 's/^/ /'
|
||||
echo " Фикс: bash ${SCRIPT_DIR}/start.sh reset && bash ${SCRIPT_DIR}/start.sh steam-ui"
|
||||
echo " (в start.sh уже: ${HOMM_STEAM_CEF_ARGS})"
|
||||
else
|
||||
echo " ✅ Нет свежих message_window в хвосте cef_log"
|
||||
fi
|
||||
else
|
||||
echo " (cef_log.txt ещё нет — Steam не стартовал)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "4) VGUI / шрифты (console_log):"
|
||||
if [[ -f "${CONSOLE_LOG}" ]] && tail -30 "${CONSOLE_LOG}" | grep -q winFont; then
|
||||
echo " ⚠️ winFont — пустой текст на кнопках VGUI"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh fix-steam-ui && bash ${SCRIPT_DIR}/start.sh reset && bash ${SCRIPT_DIR}/start.sh steam-ui"
|
||||
else
|
||||
echo " ✅ winFont в хвосте нет (или лог пуст)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "5) Login:"
|
||||
if homm_steam_logged_on; then
|
||||
echo " ✅ Logged On"
|
||||
else
|
||||
echo " ❌ Logged Off — Retry / Enter Offline Mode в окне Steam"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "Где искать окно:"
|
||||
echo " • Whisky.app → bottle HoMM-OldenEra (после migrate-bottle-to-franke-whisky.sh)"
|
||||
echo " • Cmd+Tab (wine64), Mission Control"
|
||||
echo " • Не HOMM_STEAM_VDESKTOP=1 — explorer: Failed to set primary display settings"
|
||||
75
scripts/games/fix-gog-auth.sh
Executable file
75
scripts/games/fix-gog-auth.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# GOG Galaxy в Wine: логин в CEF ок, но auth.gog.com/token таймаут → сброс на пароль.
|
||||
# Фиксы: IPv6 в Wine, сеть Poco/OpenSSL, CEF-флаги GOG.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
cmd_wine_check() {
|
||||
if [[ ! -x "${WINE64}" ]]; then
|
||||
echo "Нет WhiskyWine: bash ${SCRIPT_DIR}/install-whiskywine-manual.sh"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_wine_network_registry() {
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
echo "Wine: отключаю IPv6 DNS (частая причина зависания WinHTTP/Poco)…"
|
||||
"${WINE64}" reg add 'HKCU\Software\Wine\Network' /v DnsIPv6Enabled /t REG_DWORD /d 0 /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg add 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings' \
|
||||
/v DisableIPv6 /t REG_DWORD /d 1 /f >/dev/null 2>&1 || true
|
||||
echo "Wine: ProxyEnable=0 (без системного прокси в bottle)…"
|
||||
"${WINE64}" reg add 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings' \
|
||||
/v ProxyEnable /t REG_DWORD /d 0 /f >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
cmd_dll_overrides() {
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
local key='HKCU\Software\Wine\DllOverrides'
|
||||
echo "Wine: DllOverrides для OpenSSL/Poco (native)…"
|
||||
for dll in libssl-1_1 libcrypto-1_1 libssl-3 libcrypto-3; do
|
||||
"${WINE64}" reg add "${key}" /v "${dll}" /t REG_SZ /d native,builtin /f >/dev/null 2>&1 || true
|
||||
done
|
||||
}
|
||||
|
||||
cmd_clear_gog_webcache() {
|
||||
local cache="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/webcache"
|
||||
if [[ -d "${cache}" ]]; then
|
||||
rm -rf "${cache:?}"/*
|
||||
echo "Очищен webcache: ${cache}"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_update_plist() {
|
||||
local ps="${BOTTLE_DIR}/Program Settings/GalaxyClient.exe.plist"
|
||||
local args
|
||||
args="$(homm_gog_cef_args_line)"
|
||||
if [[ -f "${ps}" ]]; then
|
||||
/usr/bin/plutil -replace arguments -string "${args}" "${ps}" 2>/dev/null \
|
||||
|| echo " (plist вручную: ${args})"
|
||||
echo "Обновлён ${ps}"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
cmd_wine_check
|
||||
if ! homm_gog_exe >/dev/null; then
|
||||
echo "GOG не установлен: bash ${SCRIPT_DIR}/start.sh gog-install"
|
||||
exit 1
|
||||
fi
|
||||
cmd_wine_network_registry
|
||||
cmd_dll_overrides
|
||||
cmd_clear_gog_webcache
|
||||
cmd_update_plist
|
||||
echo ""
|
||||
echo "OK. Перезапуск GOG:"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh gog-restart"
|
||||
echo ""
|
||||
echo "Если снова сброс на пароль — соберите логи:"
|
||||
echo " bash ${SCRIPT_DIR}/diagnose-gog-auth.sh"
|
||||
echo ""
|
||||
echo "Проверка в логе после входа: НЕ должно быть «timeout … auth.gog.com/token»."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
45
scripts/games/fix-homm-steam-ui.sh
Executable file
45
scripts/games/fix-homm-steam-ui.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Steam в Wine/Whisky: пустой VGUI (winFont), кнопки без текста → DWriteEnable + шрифты.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
cmd_wine_check() {
|
||||
if [[ ! -x "${WINE64}" ]]; then
|
||||
echo "Нет WhiskyWine: bash ${SCRIPT_DIR}/install-whiskywine-manual.sh"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_install_fonts() {
|
||||
local fonts="${BOTTLE_DIR}/drive_c/windows/Fonts"
|
||||
local steam="${STEAM_DIR}"
|
||||
mkdir -p "${fonts}"
|
||||
for f in /System/Library/Fonts/Supplemental/{Arial,Tahoma,Verdana,"Times New Roman","Courier New"}.ttf; do
|
||||
[[ -f "${f}" ]] && cp -n "${f}" "${fonts}/" 2>/dev/null || true
|
||||
done
|
||||
cp -n "${steam}/resource/fonts/marlett.ttf" "${fonts}/" 2>/dev/null || true
|
||||
cp -n "${steam}/clientui/fonts/"*.ttf "${fonts}/" 2>/dev/null || true
|
||||
echo "Шрифты в ${fonts}/ ($(ls "${fonts}" | wc -l | tr -d ' ') файлов)"
|
||||
}
|
||||
|
||||
cmd_dwrite_registry() {
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
"${WINE64}" reg add 'HKCU\Software\Valve\Steam' /v DWriteEnable /t REG_DWORD /d 0 /f >/dev/null
|
||||
echo "Registry: HKCU\\Software\\Valve\\Steam\\DWriteEnable = 0"
|
||||
}
|
||||
|
||||
main() {
|
||||
cmd_wine_check
|
||||
cmd_install_fonts
|
||||
cmd_dwrite_registry
|
||||
echo ""
|
||||
echo "OK. Перезапустите Steam:"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh reset"
|
||||
echo " bash ${SCRIPT_DIR}/start.sh steam-ui"
|
||||
echo ""
|
||||
echo "Если текст всё ещё пустой — пройдите диалог вслепую: правая кнопка внизу или Tab → Enter."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
28
scripts/games/fix-homm-wine-input.sh
Executable file
28
scripts/games/fix-homm-wine-input.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${SCRIPT_DIR}/config.sh"
|
||||
|
||||
MODE="${1:-sdl-on}"
|
||||
export_wine_env
|
||||
|
||||
case "$MODE" in
|
||||
sdl-on)
|
||||
"${WINE64}" reg add 'HKLM\System\CurrentControlSet\Services\winebus' /v DisableHidraw /t REG_DWORD /d 1 /f >/dev/null
|
||||
"${WINE64}" reg add 'HKLM\System\CurrentControlSet\Services\winebus' /v 'Enable SDL' /t REG_DWORD /d 1 /f >/dev/null
|
||||
echo "WineBus SDL ON"
|
||||
;;
|
||||
sdl-off)
|
||||
"${WINE64}" reg add 'HKLM\System\CurrentControlSet\Services\winebus' /v DisableHidraw /t REG_DWORD /d 0 /f >/dev/null
|
||||
"${WINE64}" reg add 'HKLM\System\CurrentControlSet\Services\winebus' /v 'Enable SDL' /t REG_DWORD /d 0 /f >/dev/null
|
||||
echo "WineBus SDL OFF"
|
||||
;;
|
||||
show)
|
||||
"${WINE64}" reg query 'HKLM\System\CurrentControlSet\Services\winebus' /v DisableHidraw || true
|
||||
"${WINE64}" reg query 'HKLM\System\CurrentControlSet\Services\winebus' /v 'Enable SDL' || true
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $(basename "$0") [sdl-on|sdl-off|show]"; exit 1
|
||||
;;
|
||||
esac
|
||||
1
scripts/games/gog/.gitignore
vendored
Normal file
1
scripts/games/gog/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bottle.env
|
||||
64
scripts/games/gog/README.md
Normal file
64
scripts/games/gog/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# GOG web-installer (без Steam)
|
||||
|
||||
Отдельный Whisky-bottle. Скрипты **не** трогают HoMM-OldenEra, Steam, `start.sh play`, дампы Steam.
|
||||
|
||||
Антипаттерны (swap, winedbg, «20 GB»): [docs/games/TROUBLESHOOTING.md](../../../docs/games/TROUBLESHOOTING.md#антипаттерны-не-делать).
|
||||
|
||||
## Один раз
|
||||
|
||||
```bash
|
||||
# 1) Whisky → New Bottle → Windows 10 64-bit → имя «GOG-Install»
|
||||
bash ~/code/local_machine/scripts/games/gog/list-bottles.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/use-bottle.sh <UUID> GOG-Install
|
||||
|
||||
# 2) Подготовка bottle (или одной командой: bash gog/install-all.sh /path/stub.exe)
|
||||
bash ~/code/local_machine/scripts/games/gog/create-bottle.sh GOG-Install # если bottle ещё нет
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-bottle.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/install-gdiplus.sh # обязательно (~900MB скачивание)
|
||||
brew install cabextract
|
||||
bash ~/code/local_machine/scripts/games/gog/install-corefonts.sh
|
||||
```
|
||||
|
||||
## Запуск web-stub
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/run-web-installer.sh \
|
||||
~/path/to/GOG_Galaxy_Jagged_Alliance_3.exe
|
||||
```
|
||||
|
||||
Лог: `/tmp/gog-web-installer-GOG-Install.log`
|
||||
|
||||
## Jagged Alliance 3 **без GOG Galaxy** (канон)
|
||||
|
||||
**Runbook (установка + графика + RCA + антипаттерны):** [JA3_RUNBOOK.md](../../../docs/games/JA3_RUNBOOK.md)
|
||||
|
||||
```bash
|
||||
# Установка (Whisky GOG-Install): 5 частей offline backup в ~/Games/
|
||||
bash ~/code/local_machine/scripts/games/gog/install-ja3-innoextract.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/finish-ja3-layout.sh
|
||||
|
||||
# Запуск (CrossOver D3DMetal — один раз setup, затем каждый раз run):
|
||||
brew install --cask crossover
|
||||
bash ~/code/local_machine/scripts/games/gog/setup-ja3-crossover.sh
|
||||
bash ~/code/local_machine/scripts/games/gog/run-ja3-crossover.sh
|
||||
```
|
||||
|
||||
| Скрипт | Назначение |
|
||||
|--------|------------|
|
||||
| `install-ja3-innoextract.sh` | **Канон установки** (Whisky) |
|
||||
| `finish-ja3-layout.sh` | CommonLua, реестр GOG |
|
||||
| `setup-ja3-crossover.sh` | CrossOver bottle JA3-GOG + clone игры |
|
||||
| **`run-ja3-crossover.sh`** | **Канон запуска** (D3D12 / D3DMetal) |
|
||||
| `run-ja3.sh` | Whisky — **графика D3D12 не работает** (см. runbook) |
|
||||
|
||||
Galaxy / web-stub — **не использовать** для JA3 ([GOG_GALAXY_7H_RCA.md](../../../docs/games/GOG_GALAXY_7H_RCA.md)).
|
||||
|
||||
## Сброс только GOG
|
||||
|
||||
```bash
|
||||
bash ~/code/local_machine/scripts/games/gog/reset.sh
|
||||
```
|
||||
|
||||
## Устаревшее (не использовать для GOG)
|
||||
|
||||
- `setup-wine-bottle.sh` / bottle **HoMM-OldenEra** — там Steam и общий префикс.
|
||||
6
scripts/games/gog/bottle.env.example
Normal file
6
scripts/games/gog/bottle.env.example
Normal file
@@ -0,0 +1,6 @@
|
||||
# Скопируйте в bottle.env и укажите отдельный Whisky-bottle (без Steam/HoMM).
|
||||
# Создайте в Whisky: New Bottle → Windows 10 64-bit → имя «GOG-Install»
|
||||
# UUID: Whisky → bottle → правый клик / About, или: bash list-bottles.sh
|
||||
|
||||
BOTTLE_ID=00000000-0000-0000-0000-000000000000
|
||||
BOTTLE_NAME=GOG-Install
|
||||
70
scripts/games/gog/collect-gog-logs-7h.sh
Executable file
70
scripts/games/gog/collect-gog-logs-7h.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Собрать GOG/Whisky логи за N часов (по умолчанию 7) + timeline событий.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
HOURS="${1:-7}"
|
||||
OUT="${2:-${HOME}/code/local_machine/temp/gog-logs-${HOURS}h-$(date +%Y%m%d-%H%M%S)}"
|
||||
GALOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs"
|
||||
mkdir -p "${OUT}"
|
||||
|
||||
cp -R "${GALOG}" "${OUT}/galaxy-logs" 2>/dev/null || true
|
||||
mkdir -p "${OUT}/whisky-logs"
|
||||
cp "${HOME}/Library/Logs/com.franke.Whisky"/*.log "${OUT}/whisky-logs/" 2>/dev/null || true
|
||||
[[ -f "/tmp/gog-galaxy-client-${BOTTLE_NAME}.log" ]] && cp "/tmp/gog-galaxy-client-${BOTTLE_NAME}.log" "${OUT}/" || true
|
||||
|
||||
df -h "$(gog_data_volume_path)" >"${OUT}/disk.txt"
|
||||
pgrep -fl 'Galaxy|GOG|explorer.*GOG' >"${OUT}/processes-now.txt" 2>/dev/null || echo "(none)" >"${OUT}/processes-now.txt"
|
||||
plutil -p "${BOTTLE_DIR}/Metadata.plist" >"${OUT}/metadata.plist.txt" 2>/dev/null || true
|
||||
[[ -f "${BOTTLE_DIR}/Program Settings/GalaxyClient.exe.plist" ]] && \
|
||||
plutil -p "${BOTTLE_DIR}/Program Settings/GalaxyClient.exe.plist" >"${OUT}/GalaxyClient.exe.plist.txt"
|
||||
|
||||
python3 <<PY
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
hours = int("${HOURS}")
|
||||
out = Path("${OUT}")
|
||||
cutoff = datetime.now() - timedelta(hours=hours)
|
||||
pat = re.compile(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})")
|
||||
keys = (
|
||||
"will continue", "will exit", "Second client", "Showing login", "foreground",
|
||||
"Deelevate", "Fatal", "deelevated", "Reloaded without", "Command-line parameters",
|
||||
"parent window", "eglInitialize",
|
||||
)
|
||||
logdir = Path("${GALOG}")
|
||||
lines = [f"# GOG Galaxy timeline (last {hours}h)\n", f"cutoff_local={cutoff.isoformat()}\n", f"bottle={Path('${BOTTLE_DIR}').name}\n\n"]
|
||||
for fn in sorted(logdir.glob("*.log")):
|
||||
lines.append(f"## {fn.name}\n")
|
||||
for line in fn.read_text(errors="replace").splitlines():
|
||||
m = pat.match(line)
|
||||
if not m:
|
||||
continue
|
||||
try:
|
||||
ts = datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S.%f")
|
||||
except ValueError:
|
||||
continue
|
||||
if ts < cutoff:
|
||||
continue
|
||||
if any(k.lower() in line.lower() for k in keys):
|
||||
lines.append(line[:600] + "\n")
|
||||
lines.append("\n")
|
||||
(out / "timeline.md").write_text("".join(lines), encoding="utf-8")
|
||||
PY
|
||||
|
||||
cat >"${OUT}/README.txt" <<EOF
|
||||
GOG log bundle (${HOURS}h)
|
||||
Generated: $(date)
|
||||
Bottle: ${BOTTLE_NAME} (${BOTTLE_ID})
|
||||
|
||||
Read first: timeline.md
|
||||
Galaxy logs: galaxy-logs/
|
||||
Whisky: whisky-logs/
|
||||
EOF
|
||||
|
||||
echo "OK: ${OUT}"
|
||||
echo " timeline.md — события за ${HOURS}ч"
|
||||
ls -la "${OUT}"
|
||||
38
scripts/games/gog/collect-setup-rca.sh
Executable file
38
scripts/games/gog/collect-setup-rca.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
OUT="${1:-${HOME}/code/local_machine/temp/gog-setup-rca-$(date +%Y%m%d-%H%M%S)}"
|
||||
mkdir -p "${OUT}"
|
||||
|
||||
{
|
||||
echo "=== $(date) ==="
|
||||
echo "Bottle: ${BOTTLE_NAME} ${BOTTLE_ID}"
|
||||
echo ""
|
||||
echo "=== disk ==="
|
||||
df -h /System/Volumes/Data | tail -1
|
||||
echo ""
|
||||
echo "=== processes ==="
|
||||
pgrep -fl 'Galaxy|GOG_Galaxy' 2>/dev/null || echo "(none)"
|
||||
echo ""
|
||||
echo "=== GalaxyClient ==="
|
||||
find "${BOTTLE_DIR}/drive_c" -name 'GalaxyClient.exe' 2>/dev/null || true
|
||||
} >"${OUT}/summary.txt"
|
||||
|
||||
for f in \
|
||||
"/tmp/gog-web-installer-${BOTTLE_NAME}.log" \
|
||||
"/tmp/gog-galaxy-setup-${BOTTLE_NAME}.log"; do
|
||||
[[ -f "${f}" ]] && cp "${f}" "${OUT}/"
|
||||
done
|
||||
|
||||
find "${BOTTLE_DIR}/drive_c" -name 'Setup Log*.txt' 2>/dev/null | while read -r f; do
|
||||
cp "${f}" "${OUT}/$(basename "${f}" | tr ' #' '_')"
|
||||
done
|
||||
|
||||
find "${BOTTLE_DIR}/drive_c/ProgramData/GOG.com" -name '*.log' 2>/dev/null | while read -r f; do
|
||||
cp "${f}" "${OUT}/$(basename "${f}")"
|
||||
done
|
||||
|
||||
echo "${OUT}"
|
||||
238
scripts/games/gog/config.sh
Executable file
238
scripts/games/gog/config.sh
Executable file
@@ -0,0 +1,238 @@
|
||||
# GOG-only Wine/Whisky — без Steam, HoMM, export_wine_env_steam.
|
||||
GOG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
GAMES_CACHE_DIR="${GAMES_DIR}/cache"
|
||||
|
||||
_env_file="${GOG_DIR}/bottle.env"
|
||||
if [[ -f "${_env_file}" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${_env_file}"
|
||||
elif [[ -n "${GOG_BOTTLE_ID:-}" ]]; then
|
||||
BOTTLE_ID="${GOG_BOTTLE_ID}"
|
||||
BOTTLE_NAME="${GOG_BOTTLE_NAME:-GOG-Install}"
|
||||
else
|
||||
echo "❌ Нет bottle для GOG."
|
||||
echo " 1) Whisky → New Bottle (Windows 10 64-bit), имя GOG-Install"
|
||||
echo " 2) bash ${GOG_DIR}/list-bottles.sh"
|
||||
echo " 3) bash ${GOG_DIR}/use-bottle.sh <UUID>"
|
||||
echo ""
|
||||
echo " Не используйте bottle HoMM-OldenEra — там Steam, дампы и winedbg."
|
||||
exit 1
|
||||
fi
|
||||
unset _env_file
|
||||
|
||||
_FRANKE="${HOME}/Library/Containers/com.franke.Whisky/Bottles/${BOTTLE_ID}"
|
||||
_ISAAC="${HOME}/Library/Containers/com.isaacmarovitz.Whisky/Bottles/${BOTTLE_ID}"
|
||||
if [[ -d "${_FRANKE}/drive_c" ]]; then
|
||||
BOTTLE_DIR="${_FRANKE}"
|
||||
WHISKY_LIBS="${HOME}/Library/Application Support/com.franke.Whisky"
|
||||
elif [[ -d "${_ISAAC}/drive_c" ]]; then
|
||||
BOTTLE_DIR="${_ISAAC}"
|
||||
WHISKY_LIBS="${HOME}/Library/Application Support/com.isaacmarovitz.Whisky"
|
||||
else
|
||||
echo "❌ Bottle не найден: ${BOTTLE_NAME} (${BOTTLE_ID})"
|
||||
echo " Проверьте UUID: bash ${GOG_DIR}/list-bottles.sh"
|
||||
exit 1
|
||||
fi
|
||||
unset _FRANKE _ISAAC
|
||||
|
||||
WINE64="${WHISKY_LIBS}/Libraries/Wine/bin/wine64"
|
||||
WINE_LIB="${WHISKY_LIBS}/Libraries/Wine/lib"
|
||||
WINEPATH_BIN="${WHISKY_LIBS}/Libraries/Wine/bin/winepath"
|
||||
|
||||
gog_data_volume_path() {
|
||||
if [[ -d /System/Volumes/Data ]]; then
|
||||
echo /System/Volumes/Data
|
||||
else
|
||||
echo "${HOME}"
|
||||
fi
|
||||
}
|
||||
|
||||
gog_disk_free_kb() {
|
||||
df -k "$(gog_data_volume_path)" | awk 'NR==2 {print $4}'
|
||||
}
|
||||
|
||||
gog_disk_guard() {
|
||||
local min_gb="${1:-5}"
|
||||
local min_kb free_kb
|
||||
min_kb=$((min_gb * 1024 * 1024))
|
||||
free_kb="$(gog_disk_free_kb)"
|
||||
if [[ "${free_kb}" -lt "${min_kb}" ]]; then
|
||||
echo "❌ Мало места (<${min_gb} GB на Data)."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
gog_swap_used_mb() {
|
||||
local used
|
||||
used="$(sysctl vm.swapusage 2>/dev/null | sed -n 's/.*used = \([0-9.]*\)M.*/\1/p')"
|
||||
[[ -n "${used}" ]] || return 1
|
||||
printf '%.0f' "${used}"
|
||||
}
|
||||
|
||||
gog_swap_guard() {
|
||||
local max_mb="${1:-400}"
|
||||
local used
|
||||
used="$(gog_swap_used_mb)" || return 0
|
||||
if awk -v u="${used}" -v m="${max_mb}" 'BEGIN{exit !(u>=m)}'; then
|
||||
echo "❌ Swap уже ~${used}MB (лимит ${max_mb}MB) — запуск GOG/Wine запрещён."
|
||||
echo " bash ${GOG_DIR}/emergency-stop.sh"
|
||||
echo " затем перезагрузка Mac (swap macOS отдаёт медленно)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# JA3/CrossOver: жёсткий стоп только если swap высокий И Wine/JA3 ещё живы.
|
||||
# «Залипший» swap после закрытия (macOS не отдал) — предупреждение, запуск разрешён.
|
||||
gog_wine_or_ja3_active() {
|
||||
pgrep -f 'JA3\.exe|wineserver|wine64-preloader|winedbg|wine64\.exe|CrossOver.*wine' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
gog_swap_guard_ja3_launch() {
|
||||
local soft_mb="${1:-1200}"
|
||||
local hard_mb="${2:-4096}"
|
||||
local used
|
||||
used="$(gog_swap_used_mb)" || return 0
|
||||
if [[ "${JA3_IGNORE_SWAP:-0}" == "1" ]]; then
|
||||
echo "⚠️ JA3_IGNORE_SWAP=1 — swap ~${used}MB"
|
||||
return 0
|
||||
fi
|
||||
if gog_wine_or_ja3_active; then
|
||||
if awk -v u="${used}" -v m="${soft_mb}" 'BEGIN{exit !(u>=m)}'; then
|
||||
echo "❌ Swap ~${used}MB и Wine/JA3 ещё работают (лимит ${soft_mb}MB)."
|
||||
echo " bash ${GOG_DIR}/reset-ja3-crossover.sh или bash ${GOG_DIR}/emergency-stop.sh"
|
||||
echo " затем reboot Mac. Или: JA3_IGNORE_SWAP=1 bash …/run-ja3-crossover.sh"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
if awk -v u="${used}" -v m="${hard_mb}" 'BEGIN{exit !(u>=m)}'; then
|
||||
echo "❌ Swap ~${used}MB (лимит ${hard_mb}MB) — reboot Mac."
|
||||
echo " Или: JA3_IGNORE_SWAP=1 bash …/run-ja3-crossover.sh"
|
||||
return 1
|
||||
fi
|
||||
if awk -v u="${used}" -v m="${soft_mb}" 'BEGIN{exit !(u>=m)}'; then
|
||||
echo "⚠️ Swap ~${used}MB — залип после прошлых сессий (Wine не запущен). Запуск JA3…"
|
||||
echo " (reboot снизит swap; JA3_IGNORE_SWAP=1 — принудительный игнор)"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Виртуальный стол Wine — окно CEF часто «есть в логах», но не видно на macOS без /desktop.
|
||||
GOG_VDESKTOP="${GOG_VDESKTOP:-1280x800}"
|
||||
# 0 = прямой GalaxyClient (рекомендуется; vdesktop — в Whisky Metadata или launch-galaxy-ui.sh).
|
||||
GOG_USE_WINEVDESKTOP="${GOG_USE_WINEVDESKTOP:-0}"
|
||||
# GOG_LAUNCH_FG=1 — exec в текущем терминале (лучше для видимости окна, блокирует shell).
|
||||
GOG_LAUNCH_FG="${GOG_LAUNCH_FG:-0}"
|
||||
# GOG_CEF_PROFILE=steam — добавить -cef-in-process-gpu (A/B если login пустой).
|
||||
# /deelevated — обязательно в Wine (иначе DeelevateStrategy + exit -31, см. WineHQ #41584).
|
||||
GOG_GALAXY_BASE_ARGS="${GOG_GALAXY_BASE_ARGS:-/runWithoutUpdating /deelevated}"
|
||||
GOG_GALAXY_CEF_ARGS="${GOG_GALAXY_CEF_ARGS:--allosarches -cef-force-32bit -no-cef-sandbox -cef-disable-gpu}"
|
||||
|
||||
gog_galaxy_cef_args_array() {
|
||||
local -a a=(${GOG_GALAXY_BASE_ARGS} ${GOG_GALAXY_CEF_ARGS})
|
||||
if [[ "${GOG_CEF_PROFILE:-}" == "steam" ]]; then
|
||||
a+=(-cef-in-process-gpu)
|
||||
fi
|
||||
printf '%s\n' "${a[@]}"
|
||||
}
|
||||
|
||||
export_wine_env_gog() {
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export WINEMSYNC=1
|
||||
unset WINEESYNC 2>/dev/null || true
|
||||
local _wine_ext="${WHISKY_LIBS}/Libraries/Wine/lib/external"
|
||||
if [[ -d "${_wine_ext}/D3DMetal.framework" ]]; then
|
||||
export DYLD_FRAMEWORK_PATH="${_wine_ext}:${DYLD_FRAMEWORK_PATH:-}"
|
||||
export DYLD_LIBRARY_PATH="${_wine_ext}:${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
else
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
fi
|
||||
unset _wine_ext
|
||||
export WINEDLLOVERRIDES="gdiplus=n,b"
|
||||
unset DXVK_ASYNC DXVK_HUD 2>/dev/null || true
|
||||
export WINEDEBUG=-all
|
||||
export MVK_CONFIG_LOG_LEVEL=0
|
||||
}
|
||||
|
||||
ja3_gptk_available() {
|
||||
[[ -d "${WHISKY_LIBS}/Libraries/Wine/lib/external/D3DMetal.framework" ]] && return 0
|
||||
[[ -f "${WHISKY_LIBS}/Libraries/.gptk_installed" ]] && return 0
|
||||
[[ -d "${WHISKY_LIBS}/D3DMetal" ]] && return 0
|
||||
[[ -d "${WHISKY_LIBS}/GPTK" ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
ja3_bottle_uses_d3dmetal() {
|
||||
local meta="${BOTTLE_DIR}/Metadata.plist"
|
||||
[[ -f "${meta}" ]] || return 1
|
||||
plutil -extract graphicsConfig.backend raw -o - "${meta}" 2>/dev/null | grep -q d3dMetal
|
||||
}
|
||||
|
||||
ja3_dxmt_installed() {
|
||||
[[ -f "${BOTTLE_DIR}/.ja3_dxmt_installed" ]]
|
||||
}
|
||||
|
||||
# JA3 = D3D12 (Porting Kit / GPTK). Без GPTK — best-effort D3D11+DXVK (d3d12=d).
|
||||
export_wine_env_ja3() {
|
||||
export_wine_env_gog
|
||||
if ja3_gptk_available || ja3_bottle_uses_d3dmetal; then
|
||||
export WINED3DMETAL=1
|
||||
unset DXVK_ASYNC DXVK_HUD DXVK_CONFIG_FILE 2>/dev/null || true
|
||||
# builtin d3d12/dxgi + libd3dshared.so; без n,b на GPTK PE
|
||||
export WINEDLLOVERRIDES="libxess,xess,dxcompiler,dxil,nvngx_dlss,XeFX,XeFX_Loader=d;gdiplus=n,b"
|
||||
return 0
|
||||
fi
|
||||
if ja3_dxmt_installed; then
|
||||
# DXMT builtin: не ставить dxgi/d3d11=n,b (ломает загрузку, c000007b).
|
||||
export WINEDLLOVERRIDES="d3d12=d;libxess,xess,dxcompiler,dxil,nvngx_dlss,XeFX,XeFX_Loader=d;gdiplus=n,b"
|
||||
else
|
||||
export WINEDLLOVERRIDES="d3d11,d3d10core=n,b;d3d12=d;libxess,xess,dxcompiler,dxil,nvngx_dlss,XeFX,XeFX_Loader=d;gdiplus=n,b"
|
||||
fi
|
||||
unset DXVK_ASYNC DXVK_HUD DXVK_CONFIG_FILE 2>/dev/null || true
|
||||
export SDL_VIDEO_MAC_FULLSCREEN_SPACES="${SDL_VIDEO_MAC_FULLSCREEN_SPACES:-0}"
|
||||
export MVK_CONFIG_RESUME_LOST_DEVICE=1
|
||||
}
|
||||
|
||||
export_wine_env_ja3_d3dmetal() {
|
||||
export_wine_env_gog
|
||||
export WINED3DMETAL=1
|
||||
export WINEDLLOVERRIDES="libxess,xess,dxcompiler,dxil,nvngx_dlss,XeFX,XeFX_Loader=d;gdiplus=n,b"
|
||||
unset DXVK_ASYNC DXVK_HUD 2>/dev/null || true
|
||||
}
|
||||
|
||||
gog_steam_client_in_bottle() {
|
||||
local f
|
||||
[[ -d "${BOTTLE_DIR}/drive_c/Program Files (x86)/Steam" ]] && return 0
|
||||
[[ -f "${BOTTLE_DIR}/drive_c/Program Files (x86)/Steam/steam.exe" ]] && return 0
|
||||
f="$(find "${BOTTLE_DIR}/drive_c" -maxdepth 4 -iname 'steam.exe' -print -quit 2>/dev/null || true)"
|
||||
[[ -n "${f}" ]]
|
||||
}
|
||||
|
||||
gog_assert_no_steam_client() {
|
||||
if gog_steam_client_in_bottle; then
|
||||
echo "❌ В bottle ${BOTTLE_NAME} найден Steam client — контур GOG/JA3 без Steam."
|
||||
echo " bash ${GOG_DIR}/purge-steam-from-bottle.sh"
|
||||
return 1
|
||||
fi
|
||||
if [[ "${BOTTLE_ID}" == "38412122-AAE1-4831-9F2E-6A0EB364B3F9" ]]; then
|
||||
echo "❌ Это bottle HoMM-OldenEra (Steam). JA3 только в GOG-Install:"
|
||||
echo " bash ${GOG_DIR}/use-bottle.sh <UUID-GOG-Install>"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
gog_wine_check() {
|
||||
if [[ ! -x "${WINE64}" ]]; then
|
||||
echo "Нет Wine: bash ${GAMES_DIR}/install-whiskywine-manual.sh"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "${BOTTLE_DIR}/drive_c" ]]; then
|
||||
echo "❌ Нет drive_c в ${BOTTLE_DIR}"
|
||||
echo " Откройте bottle «${BOTTLE_NAME}» в Whisky один раз."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
37
scripts/games/gog/configure-ja3-d3dmetal.sh
Executable file
37
scripts/games/gog/configure-ja3-d3dmetal.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 = DirectX 12. На Apple Silicon канон: Whisky + Game Porting Toolkit (D3DMetal), не DXVK.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
META="${BOTTLE_DIR}/Metadata.plist"
|
||||
[[ -f "${META}" ]] || {
|
||||
echo "❌ Нет ${META} — откройте bottle ${BOTTLE_NAME} в Whisky."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ! ja3_gptk_available; then
|
||||
if [[ -d "/Applications/Game Porting Toolkit.app" ]]; then
|
||||
bash "${GOG_DIR}/install-gptk-whisky.sh"
|
||||
else
|
||||
echo "❌ Game Porting Toolkit не установлен."
|
||||
echo " brew install --cask gcenx/wine/game-porting-toolkit"
|
||||
echo " bash ${GOG_DIR}/install-gptk-whisky.sh"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
/usr/libexec/PlistBuddy -c 'Set :graphicsConfig:backend d3dMetal' "${META}" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c 'Add :graphicsConfig:backend string d3dMetal' "${META}"
|
||||
/usr/libexec/PlistBuddy -c 'Set :dxvkConfig:dxvk false' "${META}" 2>/dev/null || true
|
||||
|
||||
bash "${GOG_DIR}/force-ja3-d3d11.sh" >/dev/null 2>&1 || true
|
||||
# С GPTK можно d3d12 — сбросить принудительный d3d11 в LocalStorage при желании:
|
||||
# perl -i -pe 's/GraphicsApi = "d3d11"/GraphicsApi = "d3d12"/' ...
|
||||
|
||||
bash "${GOG_DIR}/prepare-ja3-d3dmetal.sh"
|
||||
|
||||
echo "OK: bottle ${BOTTLE_NAME} → Graphics Backend D3DMetal"
|
||||
echo " Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
echo " Whisky Run: C:\\GOG Games\\Jagged Alliance 3\\run-ja3.cmd (не JA3.exe — иначе graphics/CommonLua)"
|
||||
102
scripts/games/gog/create-bottle.sh
Executable file
102
scripts/games/gog/create-bottle.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# Создать пустой Whisky-bottle «GOG-Install» (wineboot --init), без Steam.
|
||||
set -euo pipefail
|
||||
GOG_SCRIPTS="$(cd "$(dirname "$0")" && pwd)"
|
||||
FRANKE="${HOME}/Library/Containers/com.franke.Whisky"
|
||||
WHISKY_LIBS="${HOME}/Library/Application Support/com.franke.Whisky/Libraries"
|
||||
WINE64="${WHISKY_LIBS}/Wine/bin/wine64"
|
||||
WINE_LIB="${WHISKY_LIBS}/Wine/lib"
|
||||
NAME="${1:-GOG-Install}"
|
||||
|
||||
if [[ -f "${GOG_SCRIPTS}/bottle.env" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "${GOG_SCRIPTS}/bottle.env"
|
||||
if [[ -d "${FRANKE}/Bottles/${BOTTLE_ID}/drive_c" ]]; then
|
||||
echo "Bottle уже есть: ${BOTTLE_NAME} (${BOTTLE_ID})"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -x "${WINE64}" ]]; then
|
||||
echo "Нет Wine: bash $(dirname "$GOG_SCRIPTS")/install-whiskywine-manual.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BOTTLE_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
|
||||
BOTTLE_PATH="${FRANKE}/Bottles/${BOTTLE_ID}"
|
||||
mkdir -p "${BOTTLE_PATH}"
|
||||
|
||||
echo "Создаю префикс Wine: ${BOTTLE_PATH} (2–5 мин)…"
|
||||
export WINEPREFIX="${BOTTLE_PATH}"
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
export WINEDEBUG=-all
|
||||
"${WINE64}" wineboot --init
|
||||
|
||||
python3 <<PY
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
|
||||
meta = Path("${BOTTLE_PATH}") / "Metadata.plist"
|
||||
data = {
|
||||
"info": {"name": "${NAME}", "pins": [], "blocklist": []},
|
||||
"displayConfig": {
|
||||
"customWidth": 1920,
|
||||
"customHeight": 1080,
|
||||
"resolutionPreset": "r1920x1080",
|
||||
"virtualDesktopEnabled": False,
|
||||
},
|
||||
"dxvkConfig": {"dxvk": False, "dxvkAsync": True, "dxvkHud": {"off": {}}},
|
||||
"graphicsConfig": {"backend": "recommended"},
|
||||
"audioConfig": {
|
||||
"audioDriver": "auto",
|
||||
"latencyPreset": "defaultPreset",
|
||||
"outputDeviceMode": "followSystem",
|
||||
},
|
||||
"cleanupConfig": {
|
||||
"clipboardPolicy": "auto",
|
||||
"clipboardThreshold": 10240,
|
||||
"closeWithProcessesPolicy": "ask",
|
||||
"killOnQuit": "inherit",
|
||||
},
|
||||
"customDLLOverrides": [],
|
||||
"fileVersion": {"major": 1, "minor": 0, "patch": 0, "build": "", "preRelease": ""},
|
||||
}
|
||||
with meta.open("wb") as f:
|
||||
plistlib.dump(data, f)
|
||||
print("Metadata.plist OK")
|
||||
PY
|
||||
|
||||
BOTTLE_VM="${FRANKE}/BottleVM.plist"
|
||||
python3 <<PY
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
|
||||
vm = Path("${BOTTLE_VM}")
|
||||
url = "file://${BOTTLE_PATH}"
|
||||
if vm.exists():
|
||||
with vm.open("rb") as f:
|
||||
data = plistlib.load(f)
|
||||
else:
|
||||
data = {"fileVersion": {"major": 1, "minor": 0, "patch": 0, "build": "", "preRelease": ""}, "paths": []}
|
||||
paths = data.setdefault("paths", [])
|
||||
if not any(p.get("relative", "").endswith("${BOTTLE_ID}") for p in paths):
|
||||
paths.append({"relative": url})
|
||||
with vm.open("wb") as f:
|
||||
plistlib.dump(data, f)
|
||||
print("BottleVM.plist OK")
|
||||
PY
|
||||
|
||||
if command -v whisky >/dev/null 2>&1; then
|
||||
whisky add "${BOTTLE_PATH}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
cat >"${GOG_SCRIPTS}/bottle.env" <<EOF
|
||||
BOTTLE_ID=${BOTTLE_ID}
|
||||
BOTTLE_NAME=${NAME}
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "OK: ${NAME}"
|
||||
echo " UUID: ${BOTTLE_ID}"
|
||||
echo " Path: ${BOTTLE_PATH}"
|
||||
echo "Дальше: bash ${GOG_SCRIPTS}/setup-bottle.sh"
|
||||
44
scripts/games/gog/diagnose-galaxy-window.sh
Executable file
44
scripts/games/gog/diagnose-galaxy-window.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# RCA: процесс жив, окна нет — логи + процессы (без Apple Events).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
OUT="${1:-${HOME}/code/local_machine/temp/gog-window-rca-$(date +%Y%m%d-%H%M%S)}"
|
||||
mkdir -p "${OUT}"
|
||||
|
||||
GALAXY_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/GalaxyClient.log"
|
||||
INIT_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/GalaxyInitialization.log"
|
||||
CEF_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/cef.log"
|
||||
LAUNCH_LOG="/tmp/gog-galaxy-client-${BOTTLE_NAME}.log"
|
||||
|
||||
{
|
||||
echo "=== GOG Galaxy window RCA ==="
|
||||
date
|
||||
echo "bottle: ${BOTTLE_NAME} ${BOTTLE_ID}"
|
||||
echo "GOG_USE_WINEVDESKTOP=${GOG_USE_WINEVDESKTOP:-?} GOG_VDESKTOP=${GOG_VDESKTOP:-?}"
|
||||
echo ""
|
||||
echo "--- pgrep Galaxy ---"
|
||||
pgrep -fl 'GalaxyClient|explorer.exe /desktop=GOG' 2>/dev/null || echo "(нет процессов)"
|
||||
echo ""
|
||||
echo "--- disk ---"
|
||||
df -h "$(gog_data_volume_path)" 2>/dev/null | tail -1
|
||||
} >"${OUT}/summary.txt"
|
||||
|
||||
for f in "${GALAXY_LOG}" "${INIT_LOG}" "${CEF_LOG}" "${LAUNCH_LOG}"; do
|
||||
[[ -f "${f}" ]] || continue
|
||||
base="$(basename "${f}")"
|
||||
tail -80 "${f}" >"${OUT}/${base}.tail" 2>/dev/null || true
|
||||
done
|
||||
|
||||
grep -E 'Second client|will continue|Showing login|foreground|Failed to locate|parent window' \
|
||||
"${GALAXY_LOG}" "${CEF_LOG}" 2>/dev/null >"${OUT}/signals.txt" || true
|
||||
|
||||
echo "OK: ${OUT}"
|
||||
echo " summary.txt, signals.txt, *.tail"
|
||||
echo ""
|
||||
echo "Если в signals.txt есть login/foreground, а окна нет:"
|
||||
echo " bash ${GOG_DIR}/reset.sh"
|
||||
echo " bash ${GOG_DIR}/launch-galaxy-ui.sh # vdesktop + foreground"
|
||||
echo " Cmd+Tab → wine64 / GOGGalaxy / GalaxyClient"
|
||||
28
scripts/games/gog/diagnose-session.sh
Executable file
28
scripts/games/gog/diagnose-session.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
RCA="${GAMES_DIR}/../temp/gog-rca-$(date +%Y%m%d-%H%M%S)"
|
||||
LOG="/tmp/gog-web-installer-${BOTTLE_NAME}.log"
|
||||
mkdir -p "${RCA}"
|
||||
|
||||
{
|
||||
echo "GOG bottle: ${BOTTLE_NAME} (${BOTTLE_ID})"
|
||||
df -h "$(gog_data_volume_path)"
|
||||
sysctl vm.swapusage 2>/dev/null || true
|
||||
echo ""
|
||||
echo "markers:"; ls -la "${BOTTLE_DIR}"/.gog_* 2>/dev/null || true
|
||||
echo ""
|
||||
du -sh "${BOTTLE_DIR}" "${BOTTLE_DIR}/drive_c/windows/Fonts" 2>/dev/null || true
|
||||
echo ""
|
||||
echo "log ${LOG}:"
|
||||
tail -30 "${LOG}" 2>/dev/null || echo "(нет)"
|
||||
echo ""
|
||||
echo "winedbg: $(pgrep winedbg 2>/dev/null | wc -l | tr -d ' ')"
|
||||
pgrep -lf 'GOG_Galaxy|GalaxyWeb' 2>/dev/null || echo "(нет GOG процессов)"
|
||||
} | tee "${RCA}/report.txt"
|
||||
|
||||
cp "${LOG}" "${RCA}/" 2>/dev/null || true
|
||||
echo "RCA: ${RCA}"
|
||||
26
scripts/games/gog/emergency-stop.sh
Executable file
26
scripts/games/gog/emergency-stop.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# Снять нагрузку GOG/Wine + winedbg (swap). Безопасно для GOG-bottle.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=gog-process.sh
|
||||
source "${GOG_DIR}/gog-process.sh"
|
||||
|
||||
_winedbg_count() {
|
||||
pgrep winedbg 2>/dev/null | wc -l | tr -d ' '
|
||||
}
|
||||
|
||||
echo "=== До ==="
|
||||
sysctl vm.swapusage 2>/dev/null || true
|
||||
echo "winedbg: $(_winedbg_count)"
|
||||
|
||||
gog_kill_all
|
||||
pkill -9 winedbg 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "=== После kill ==="
|
||||
pgrep -fl 'Galaxy|GOG Galaxy' 2>/dev/null || echo "Galaxy: (нет)"
|
||||
echo "winedbg: $(_winedbg_count)"
|
||||
sysctl vm.swapusage 2>/dev/null || true
|
||||
echo ""
|
||||
echo "Если Mac всё ещё тормозит — перезагрузка (swap отдаётся медленно)."
|
||||
echo "Galaxy UI в Wine на этом Mac не подтверждён — JA3: docs/games/GOG_GALAXY_7H_RCA.md § Offline"
|
||||
42
scripts/games/gog/fetch-ja3-setup-exe.sh
Executable file
42
scripts/games/gog/fetch-ja3-setup-exe.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Скачать только лаунчер (~1 MB) для multipart offline backup JA3.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
JA3_GAMES_DIR="${JA3_GAMES_DIR:-${HOME}/Games}"
|
||||
OUT="${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730).exe"
|
||||
|
||||
mkdir -p "${JA3_GAMES_DIR}"
|
||||
|
||||
if [[ -f "${OUT}" ]]; then
|
||||
echo "Уже есть: ${OUT}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v lgogdownloader >/dev/null 2>&1; then
|
||||
echo "❌ Нужен lgogdownloader: brew install lgogdownloader"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Скачивание en1installer0 (~1 MB) → ${OUT}"
|
||||
echo "Если ошибка login — выполните: lgogdownloader --login"
|
||||
echo ""
|
||||
|
||||
lgogdownloader \
|
||||
--download-file "jagged_alliance_3/en1installer0" \
|
||||
--output-file "$(basename "${OUT}")" \
|
||||
--directory "${JA3_GAMES_DIR}"
|
||||
|
||||
if [[ -f "${OUT}" ]]; then
|
||||
ls -lh "${OUT}"
|
||||
echo "OK. Дальше: bash ${GOG_DIR}/install-ja3-multipart.sh"
|
||||
else
|
||||
# lgogdownloader может сохранить под другим именем
|
||||
f="$(find "${JA3_GAMES_DIR}" -maxdepth 1 -iname 'setup*jagged*alliance*3*.exe' -print -quit 2>/dev/null || true)"
|
||||
if [[ -n "${f}" ]]; then
|
||||
echo "Скачан как: ${f}"
|
||||
echo "Запуск: JA3_SETUP_EXE='${f}' bash ${GOG_DIR}/install-ja3-multipart.sh"
|
||||
else
|
||||
echo "❌ .exe не появился в ${JA3_GAMES_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
7
scripts/games/gog/finish-ja3-install.sh
Executable file
7
scripts/games/gog/finish-ja3-install.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Устарело: Galaxy UI. Канон — install-ja3.sh (offline backup без Galaxy).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
echo "Канон JA3 без Galaxy: bash ${GOG_DIR}/install-ja3.sh"
|
||||
echo "Док: docs/games/JA3_MAC_WITHOUT_GALAXY.md"
|
||||
exec bash "${GOG_DIR}/install-ja3.sh"
|
||||
48
scripts/games/gog/finish-ja3-layout.sh
Executable file
48
scripts/games/gog/finish-ja3-layout.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Доп. шаги после innoextract: layout GOG, реестр, DXVK, CommonLua.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
JA3="${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3"
|
||||
|
||||
[[ -f "${JA3}/JA3.exe" ]] || {
|
||||
echo "❌ Нет JA3 — сначала install-ja3-innoextract.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Inno Setup копирует CommonLua в корень; innoextract — только ModTools/ (Wine не видит symlink)
|
||||
if [[ ! -f "${JA3}/CommonLua/Core/autorun.lua" ]]; then
|
||||
rm -rf "${JA3}/CommonLua"
|
||||
cp -R "${JA3}/ModTools/Src/CommonLua" "${JA3}/CommonLua"
|
||||
echo " CommonLua скопирован из ModTools/Src (~9 MB)"
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/register-ja3-registry.sh"
|
||||
bash "${GOG_DIR}/install-ja3-redist.sh" 2>/dev/null || true
|
||||
if ja3_gptk_available || ja3_bottle_uses_d3dmetal; then
|
||||
bash "${GOG_DIR}/prepare-ja3-d3dmetal.sh" 2>/dev/null || bash "${GOG_DIR}/install-gptk-whisky.sh" || true
|
||||
else
|
||||
if [[ -d "/Applications/Game Porting Toolkit.app" ]]; then
|
||||
bash "${GOG_DIR}/install-gptk-whisky.sh"
|
||||
else
|
||||
bash "${GOG_DIR}/install-ja3-dxmt.sh" 2>/dev/null || bash "${GOG_DIR}/install-dxvk.sh"
|
||||
fi
|
||||
fi
|
||||
cp -f "${GOG_DIR}/ja3-dxvk.conf" "${JA3}/dxvk.conf" 2>/dev/null || true
|
||||
if ja3_gptk_available || ja3_bottle_uses_d3dmetal; then
|
||||
perl -i -pe 's/^\t\tGraphicsApi = "d3d11"/\t\tGraphicsApi = "d3d12"/' \
|
||||
"${BOTTLE_DIR}/drive_c/users/${WINE_USER:-eternal}/AppData/Roaming/Jagged Alliance 3/LocalStorage.lua" 2>/dev/null || true
|
||||
else
|
||||
bash "${GOG_DIR}/force-ja3-d3d11.sh"
|
||||
fi
|
||||
# Intel XeSS / DLSS DLL в папке игры ломают D3D11 на MoltenVK — убрать с пути загрузки
|
||||
mkdir -p "${JA3}/.disabled-upscale"
|
||||
for dll in libxess.dll nvngx_dlss.dll XeFX.dll XeFX_Loader.dll dxcompiler.dll dxil.dll; do
|
||||
[[ -f "${JA3}/${dll}" ]] && mv -f "${JA3}/${dll}" "${JA3}/.disabled-upscale/${dll}"
|
||||
done
|
||||
bash "${GOG_DIR}/purge-steam-from-bottle.sh" >/dev/null 2>&1 || true
|
||||
|
||||
echo "OK: finish-ja3-layout"
|
||||
77
scripts/games/gog/fix-ja3-crossover-mouse.sh
Executable file
77
scripts/games/gog/fix-ja3-crossover-mouse.sh
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
# Курсор JA3 в CrossOver (Retina / HiDPI).
|
||||
# Профили (JA3_MOUSE_PROFILE):
|
||||
# legacy — RetinaMode=n (полный ход по Y; возможен сдвиг клика) — default
|
||||
# retina-lite — RetinaMode=y без LogPixels (если legacy смещает, но нет клипа)
|
||||
# retina — RetinaMode=y + LogPixels=192 (клип на середине экрана — не default)
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
CX="/Applications/CrossOver.app/Contents/SharedSupport/CrossOver"
|
||||
CX_BOTTLE="JA3-GOG"
|
||||
CX_DIR="${HOME}/Library/Application Support/CrossOver/Bottles/${CX_BOTTLE}"
|
||||
|
||||
[[ -d "${CX}" ]] || {
|
||||
echo "❌ CrossOver не установлен"
|
||||
exit 1
|
||||
}
|
||||
|
||||
PROFILE="${JA3_MOUSE_PROFILE:-legacy}"
|
||||
WREG() { "${CX}/bin/wine" --bottle "${CX_BOTTLE}" "$@"; }
|
||||
|
||||
case "${PROFILE}" in
|
||||
retina)
|
||||
RETINA="y"
|
||||
SET_DPI=1
|
||||
DPI="${JA3_LOG_PIXELS:-192}"
|
||||
SET_WIN8=1
|
||||
;;
|
||||
retina-lite)
|
||||
RETINA="y"
|
||||
SET_DPI=0
|
||||
SET_WIN8=0
|
||||
;;
|
||||
legacy|*)
|
||||
RETINA="n"
|
||||
SET_DPI=0
|
||||
SET_WIN8=0
|
||||
PROFILE="legacy"
|
||||
;;
|
||||
esac
|
||||
|
||||
WREG reg add 'HKCU\Software\Wine\Mac Driver' /v RetinaMode /t REG_SZ /d "${RETINA}" /f >/dev/null
|
||||
|
||||
if [[ "${SET_DPI}" == "1" ]]; then
|
||||
WREG reg add 'HKCU\Control Panel\Desktop' /v LogPixels /t REG_DWORD /d "${DPI}" /f >/dev/null
|
||||
else
|
||||
WREG reg delete 'HKCU\Control Panel\Desktop' /v LogPixels /f >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [[ "${SET_WIN8}" == "1" ]]; then
|
||||
WREG reg add 'HKCU\Control Panel\Desktop' /v Win8DpiScaling /t REG_DWORD /d 1 /f >/dev/null
|
||||
else
|
||||
WREG reg delete 'HKCU\Control Panel\Desktop' /v Win8DpiScaling /f >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
CONF="${CX_DIR}/cxbottle.conf"
|
||||
if [[ -f "${CONF}" ]] && ! grep -q 'SDL_VIDEO_MAC_FULLSCREEN_SPACES' "${CONF}"; then
|
||||
sed -i '' '/^\[EnvironmentVariables\]/a\
|
||||
"SDL_VIDEO_MAC_FULLSCREEN_SPACES" = "0"
|
||||
' "${CONF}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "OK: profile=${PROFILE} RetinaMode=${RETINA} → ${CX_BOTTLE}"
|
||||
echo " Перезапуск: bash ${GOG_DIR}/reset-ja3-crossover.sh && bash ${GOG_DIR}/run-ja3-crossover.sh"
|
||||
case "${PROFILE}" in
|
||||
legacy)
|
||||
echo " Если клик смещён: JA3_MOUSE_PROFILE=retina-lite bash ${GOG_DIR}/fix-ja3-crossover-mouse.sh"
|
||||
echo " Без перезапуска: верхний левый угол окна = угол экрана."
|
||||
;;
|
||||
retina-lite)
|
||||
echo " Если смещение осталось: JA3_MOUSE_PROFILE=legacy …"
|
||||
echo " Если клип на середине: JA3_MOUSE_PROFILE=legacy …"
|
||||
;;
|
||||
retina)
|
||||
echo " ⚠️ retina+LogPixels=192 часто режет курсор на середине экрана."
|
||||
echo " Если так: JA3_MOUSE_PROFILE=legacy bash ${GOG_DIR}/fix-ja3-crossover-mouse.sh"
|
||||
;;
|
||||
esac
|
||||
35
scripts/games/gog/force-ja3-d3d11.sh
Executable file
35
scripts/games/gog/force-ja3-d3d11.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 на Mac (Whisky без GPTK): рендер через D3D11 + DXVK, не D3D12 (wine d3d12 → «Failed creating render device»).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
WINE_USER="${WINE_USER:-eternal}"
|
||||
LS="${BOTTLE_DIR}/drive_c/users/${WINE_USER}/AppData/Roaming/Jagged Alliance 3/LocalStorage.lua"
|
||||
|
||||
if [[ ! -f "${LS}" ]]; then
|
||||
mkdir -p "$(dirname "${LS}")"
|
||||
cat >"${LS}" <<'EOF'
|
||||
return {
|
||||
Options = {
|
||||
GraphicsApi = "d3d11",
|
||||
VideoPreset = "Low",
|
||||
},
|
||||
}
|
||||
EOF
|
||||
echo "OK: создан ${LS} (GraphicsApi=d3d11)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if grep -q 'GraphicsApi = "d3d11"' "${LS}"; then
|
||||
echo "OK: GraphicsApi уже d3d11"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
perl -i -pe '
|
||||
if (/^\t\tGraphicsApi = /) {
|
||||
$_ = "\t\tGraphicsApi = \"d3d11\",\n";
|
||||
}
|
||||
' "${LS}"
|
||||
echo "OK: ${LS} → GraphicsApi=d3d11 (было d3d12 или пусто)"
|
||||
72
scripts/games/gog/gog-process.sh
Executable file
72
scripts/games/gog/gog-process.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# Общие kill/lock для GOG Galaxy в GOG-bottle.
|
||||
GOG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
WINE_SERV="${WHISKY_LIBS}/Libraries/Wine/bin/wineserver"
|
||||
|
||||
gog_lock_files_dir() {
|
||||
echo "${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/lock-files"
|
||||
}
|
||||
|
||||
gog_remove_lock_files() {
|
||||
local d
|
||||
d="$(gog_lock_files_dir)"
|
||||
[[ -d "${d}" ]] || return 0
|
||||
rm -f "${d}"/*.lock 2>/dev/null || true
|
||||
}
|
||||
|
||||
gog_kill_all() {
|
||||
pkill -9 -f 'explorer.exe /desktop=GOGGalaxy' 2>/dev/null || true
|
||||
pkill -9 -f 'explorer /desktop=GOGGalaxy' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxyClient Helper' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxyClient' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxyUpdater' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxyInstaller' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxySetup' 2>/dev/null || true
|
||||
pkill -9 -f 'GalaxyWebInstaller' 2>/dev/null || true
|
||||
pkill -9 -f 'GOG_Galaxy_' 2>/dev/null || true
|
||||
pkill -9 -f 'GOG Galaxy' 2>/dev/null || true
|
||||
pkill -9 -f "${BOTTLE_ID}.*[Ss]team" 2>/dev/null || true
|
||||
pkill -9 -f "${BOTTLE_DIR}.*steam\.exe" 2>/dev/null || true
|
||||
pkill -9 -f "${BOTTLE_ID}.*wine" 2>/dev/null || true
|
||||
pkill -9 -f 'winedevice\.exe' 2>/dev/null || true
|
||||
pkill -9 -f 'wine64-preloader' 2>/dev/null || true
|
||||
pkill -9 -f 'wine64\.exe' 2>/dev/null || true
|
||||
sleep 1
|
||||
if [[ -d "${BOTTLE_DIR}" ]]; then
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
[[ -x "${WINE_SERV}" ]] && "${WINE_SERV}" -k 2>/dev/null || true
|
||||
sleep 1
|
||||
gog_remove_lock_files
|
||||
fi
|
||||
sleep 1
|
||||
}
|
||||
|
||||
gog_galaxy_running() {
|
||||
pgrep -f 'GalaxyClient\.exe' >/dev/null 2>&1 \
|
||||
|| pgrep -f 'GalaxyClient Helper' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
gog_wait_log_line() {
|
||||
local log="$1" pattern="$2" timeout="${3:-30}"
|
||||
local i
|
||||
for i in $(seq 1 "${timeout}"); do
|
||||
[[ -f "${log}" ]] && grep -q "${pattern}" "${log}" 2>/dev/null && return 0
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
gog_mac_activate_wine() {
|
||||
osascript -e '
|
||||
tell application "System Events"
|
||||
repeat with proc in (every process whose name contains "wine" or name contains "Wine")
|
||||
try
|
||||
set frontmost of proc to true
|
||||
exit repeat
|
||||
end try
|
||||
end repeat
|
||||
end tell' 2>/dev/null || true
|
||||
}
|
||||
37
scripts/games/gog/install-all.sh
Executable file
37
scripts/games/gog/install-all.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# Полный сетап GOG-bottle + web-stub. Аргумент: путь к GOG_Galaxy_*.exe
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
STUB="${1:-}"
|
||||
|
||||
if [[ ! -f "${GOG_DIR}/bottle.env" ]]; then
|
||||
bash "${GOG_DIR}/create-bottle.sh" GOG-Install
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/setup-bottle.sh" --force
|
||||
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
if [[ ! -f "${BOTTLE_DIR}/.gog_gdiplus_installed" ]]; then
|
||||
bash "${GOG_DIR}/install-gdiplus.sh"
|
||||
fi
|
||||
|
||||
command -v cabextract >/dev/null || brew install cabextract
|
||||
bash "${GOG_DIR}/install-corefonts.sh"
|
||||
bash "${GOG_DIR}/install-galaxy-extract.sh"
|
||||
|
||||
if [[ -z "${STUB}" ]]; then
|
||||
echo "Готово. Установка JA3:"
|
||||
echo " bash ${GOG_DIR}/launch-galaxy.sh # вход → Install в библиотеке"
|
||||
echo " bash ${GOG_DIR}/finish-ja3-install.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${GOG_USE_WEB_STUB:-0}" == "1" ]]; then
|
||||
echo "Web-stub (эксперимент; GalaxySetup часто падает на gdi32 в Wine)…"
|
||||
bash "${GOG_DIR}/run-web-installer.sh" "${STUB}"
|
||||
else
|
||||
echo "Galaxy уже распакован. Для игры — launch-galaxy.sh (не web-stub)."
|
||||
echo "Принудительно stub: GOG_USE_WEB_STUB=1 bash $0 ${STUB}"
|
||||
bash "${GOG_DIR}/launch-galaxy.sh"
|
||||
fi
|
||||
48
scripts/games/gog/install-corefonts.sh
Executable file
48
scripts/games/gog/install-corefonts.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
# shellcheck source=wine-fonts.sh
|
||||
source "${GOG_DIR}/wine-fonts.sh"
|
||||
|
||||
CACHE="${GAMES_CACHE_DIR}/corefonts"
|
||||
FONTS="${BOTTLE_DIR}/drive_c/windows/Fonts"
|
||||
|
||||
cmd_fetch() {
|
||||
local name="$1" url="$2"
|
||||
mkdir -p "${CACHE}"
|
||||
if [[ -f "${CACHE}/${name}" ]] && [[ "$(wc -c <"${CACHE}/${name}")" -gt 10000 ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo "Скачиваю ${name}…"
|
||||
curl -fsSL --retry 3 -o "${CACHE}/${name}" "${url}" || return 1
|
||||
}
|
||||
|
||||
cmd_extract_cab() {
|
||||
local cab="$1" out="${CACHE}/extract-${2:-x}"
|
||||
rm -rf "${out}"
|
||||
mkdir -p "${out}"
|
||||
cabextract -d "${out}" "${cab}"
|
||||
find "${out}" -iname '*.ttf' -exec cp -n {} "${FONTS}/" \;
|
||||
}
|
||||
|
||||
gog_wine_check
|
||||
command -v cabextract >/dev/null || { echo "brew install cabextract"; exit 1; }
|
||||
mkdir -p "${FONTS}" "${CACHE}"
|
||||
|
||||
cmd_fetch andale32.exe "https://downloads.sourceforge.net/corefonts/andale32.exe" \
|
||||
|| cmd_fetch andale32.exe "https://ftp.debian.org/debian/pool/contrib/m/msttcorefonts/andale32.exe" || true
|
||||
cmd_fetch arial32.exe "https://downloads.sourceforge.net/corefonts/arial32.exe" \
|
||||
|| cmd_fetch arial32.exe "https://ftp.debian.org/debian/pool/contrib/m/msttcorefonts/arial32.exe" || true
|
||||
cmd_fetch times32.exe "https://downloads.sourceforge.net/corefonts/times32.exe" \
|
||||
|| cmd_fetch times32.exe "https://ftp.debian.org/debian/pool/contrib/m/msttcorefonts/times32.exe" || true
|
||||
|
||||
for cab in andale32.exe arial32.exe times32.exe; do
|
||||
[[ -f "${CACHE}/${cab}" ]] && [[ "$(wc -c <"${CACHE}/${cab}")" -gt 10000 ]] \
|
||||
&& cmd_extract_cab "${CACHE}/${cab}" "${cab%.exe}" || true
|
||||
done
|
||||
|
||||
gog_wine_ensure_fonts 1
|
||||
touch "${BOTTLE_DIR}/.gog_corefonts_installed"
|
||||
echo "OK: corefonts в GOG bottle ($(ls "${FONTS}" | wc -l | tr -d ' ') файлов)"
|
||||
35
scripts/games/gog/install-dxvk.sh
Executable file
35
scripts/games/gog/install-dxvk.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# DXVK-macOS в GOG-bottle (для JA3).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
# install-dxvk-macos.sh привязан к HoMM config — дублируем install для GOG-bottle
|
||||
DXVK_CACHE="${GAMES_DIR}/cache/dxvk-macos"
|
||||
DXVK_TAR="${DXVK_CACHE}/dxvk-macOS-async.tar.gz"
|
||||
DXVK_URL="https://github.com/Gcenx/DXVK-macOS/releases/download/v1.10.3-20230507-repack/dxvk-macOS-async-v1.10.3-20230507-repack.tar.gz"
|
||||
DXVK_DIR="${DXVK_CACHE}/dxvk-macOS-async-v1.10.3-20230507-repack"
|
||||
DXVK_X64="${DXVK_DIR}/x64"
|
||||
WINE_LIB64="${WHISKY_LIBS}/Libraries/Wine/lib/wine/x86_64-windows"
|
||||
SYS32="${BOTTLE_DIR}/drive_c/windows/system32"
|
||||
|
||||
gog_wine_check
|
||||
mkdir -p "${DXVK_CACHE}"
|
||||
[[ -f "${DXVK_TAR}" ]] || curl -L --fail -o "${DXVK_TAR}" "${DXVK_URL}"
|
||||
[[ -f "${DXVK_X64}/d3d11.dll" ]] || tar -xzf "${DXVK_TAR}" -C "${DXVK_CACHE}"
|
||||
cp -f "${WINE_LIB64}/dxgi.dll" "${SYS32}/dxgi.dll"
|
||||
cp -f "${DXVK_X64}/d3d11.dll" "${SYS32}/d3d11.dll"
|
||||
cp -f "${DXVK_X64}/d3d10core.dll" "${SYS32}/d3d10core.dll"
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
"${WINE64}" reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d11 /d native,b /f >/dev/null
|
||||
"${WINE64}" reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d10core /d native,b /f >/dev/null
|
||||
"${WINE64}" reg add 'HKEY_CURRENT_USER\Software\Wine\Direct3D' /v renderer /t REG_SZ /d vulkan /f >/dev/null 2>&1 || true
|
||||
# Whisky UI: DXVK для HoMM; JA3 предпочитает D3DMetal (configure-ja3-d3dmetal.sh)
|
||||
if [[ -f "${BOTTLE_DIR}/Metadata.plist" ]]; then
|
||||
/usr/libexec/PlistBuddy -c 'Set :dxvkConfig:dxvk true' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c 'Add :dxvkConfig:dxvk bool true' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
fi
|
||||
echo "OK: DXVK-macOS в ${BOTTLE_NAME}"
|
||||
100
scripts/games/gog/install-galaxy-extract.sh
Executable file
100
scripts/games/gog/install-galaxy-extract.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
# GOG Galaxy в GOG-bottle без Wine-Inno (gdi32/botva2). Как install-gog-galaxy.sh для HoMM.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
GOG_SETUP="${GAMES_CACHE_DIR:-${GAMES_DIR}/cache}/setup_galaxy.exe"
|
||||
EXTRACT_DIR="${GAMES_CACHE_DIR:-${GAMES_DIR}/cache}/gog-galaxy-extract-gog-bottle"
|
||||
GOG_PF="${BOTTLE_DIR}/drive_c/Program Files (x86)/GOG Galaxy"
|
||||
GOG_PD="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy"
|
||||
MARKER="${BOTTLE_DIR}/.gog_galaxy_extract_installed"
|
||||
|
||||
gog_galaxy_exe() {
|
||||
if [[ -f "${GOG_PF}/GalaxyClient.exe" ]]; then
|
||||
echo "${GOG_PF}/GalaxyClient.exe"
|
||||
return 0
|
||||
fi
|
||||
local found
|
||||
found="$(find "${BOTTLE_DIR}/drive_c/Program Files"* -maxdepth 3 -name 'GalaxyClient.exe' 2>/dev/null | head -1)"
|
||||
[[ -n "${found}" ]] && echo "${found}" && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
homm_gog_setup_url() {
|
||||
if [[ -n "${GOG_SETUP_URL:-}" ]]; then
|
||||
echo "${GOG_SETUP_URL}"
|
||||
return 0
|
||||
fi
|
||||
local json url
|
||||
json="$(curl -fsSL "https://remote-config.gog.com/components/webinstaller?component_version=2.0.0")"
|
||||
url="$(python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["content"]["windows"]["downloadLink"])' <<<"${json}" 2>/dev/null)" || true
|
||||
if [[ -z "${url}" ]]; then
|
||||
url="https://content-system.gog.com/open_link/download?path=/open/galaxy/client/setup_galaxy_2.0.100.1.exe"
|
||||
fi
|
||||
echo "${url}"
|
||||
}
|
||||
|
||||
main() {
|
||||
gog_wine_check
|
||||
gog_disk_guard 3 || exit 1
|
||||
|
||||
if gog_galaxy_exe >/dev/null 2>&1; then
|
||||
echo "GOG Galaxy уже в bottle: $(gog_galaxy_exe)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -f "${GOG_SETUP}" ]]; then
|
||||
mkdir -p "$(dirname "${GOG_SETUP}")"
|
||||
dl_url="$(homm_gog_setup_url)"
|
||||
echo "Скачиваю setup_galaxy.exe (~290 MB)…"
|
||||
curl -L --fail --retry 3 -C - -o "${GOG_SETUP}" "${dl_url}"
|
||||
fi
|
||||
|
||||
if ! command -v innoextract >/dev/null 2>&1; then
|
||||
echo "Нужен innoextract: brew install innoextract"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Распаковка GOG Galaxy (innoextract) → ${BOTTLE_NAME}…"
|
||||
rm -rf "${EXTRACT_DIR}"
|
||||
mkdir -p "${EXTRACT_DIR}"
|
||||
innoextract -e -d "${EXTRACT_DIR}" "${GOG_SETUP}"
|
||||
|
||||
mkdir -p "${GOG_PF}" "${GOG_PD}"
|
||||
cp -R "${EXTRACT_DIR}/app/." "${GOG_PF}/"
|
||||
local pd_src
|
||||
pd_src="$(find "${EXTRACT_DIR}" -type d -path '*/GOG.com/Galaxy' 2>/dev/null | head -1)"
|
||||
if [[ -n "${pd_src}" ]]; then
|
||||
cp -R "${pd_src}/." "${GOG_PD}/"
|
||||
fi
|
||||
|
||||
if ! gog_galaxy_exe >/dev/null 2>&1; then
|
||||
echo "❌ GalaxyClient.exe не найден после распаковки"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VC++ redist (из setup_galaxy)…"
|
||||
local vc_x86="${EXTRACT_DIR}/tmp/VC_redist.x86.exe"
|
||||
local vc_x64="${EXTRACT_DIR}/tmp/VC_redist.x64.exe"
|
||||
if [[ -f "${vc_x86}" ]]; then
|
||||
export_wine_env_gog
|
||||
cp -f "${vc_x86}" "${BOTTLE_DIR}/drive_c/VC_redist.x86.exe"
|
||||
cp -f "${vc_x64}" "${BOTTLE_DIR}/drive_c/VC_redist.x64.exe"
|
||||
"${WINE64}" C:\\VC_redist.x86.exe /install /quiet /norestart
|
||||
"${WINE64}" C:\\VC_redist.x64.exe /install /quiet /norestart
|
||||
fi
|
||||
|
||||
local fonts_dst="${BOTTLE_DIR}/drive_c/windows/Fonts"
|
||||
mkdir -p "${fonts_dst}"
|
||||
cp -f "${EXTRACT_DIR}"/fonts/*.ttf "${fonts_dst}/" 2>/dev/null || true
|
||||
|
||||
bash "${GOG_DIR}/register-galaxy-registry.sh"
|
||||
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ >"${MARKER}"
|
||||
echo "OK: $(gog_galaxy_exe)"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
20
scripts/games/gog/install-gdiplus.sh
Executable file
20
scripts/games/gog/install-gdiplus.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# MS gdiplus.dll — без этого .NET падает на GenericSansSerif (GdipGetGenericFontFamilySansSerif).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
gog_wine_check
|
||||
command -v winetricks >/dev/null || { echo "brew install winetricks"; exit 1; }
|
||||
|
||||
export WINE="${WINE64}"
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
export WINEDEBUG=-all
|
||||
|
||||
echo "winetricks gdiplus (~3–5 мин, скачает win7sp1)…"
|
||||
winetricks -q gdiplus
|
||||
|
||||
touch "${BOTTLE_DIR}/.gog_gdiplus_installed"
|
||||
echo "OK: gdiplus в ${BOTTLE_NAME}"
|
||||
51
scripts/games/gog/install-gptk-whisky.sh
Executable file
51
scripts/games/gog/install-gptk-whisky.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# Apple D3DMetal (GPTK) в Whisky Libraries — только framework + unix-мост.
|
||||
# НЕ ditto всего GPTK lib (перезапишет wine64 на 7.7 и сломает bottle).
|
||||
# Канон: https://github.com/Whisky-App/Whisky/issues/124
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
GPTK_APP="${GPTK_APP:-/Applications/Game Porting Toolkit.app}"
|
||||
GPTK_WINE="${GPTK_APP}/Contents/Resources/wine"
|
||||
WHISKY_WINE="${WHISKY_LIBS}/Libraries/Wine"
|
||||
EXT="${WHISKY_WINE}/lib/external"
|
||||
WINE_UNIX="${WHISKY_WINE}/lib/wine/x86_64-unix"
|
||||
|
||||
[[ -d "${GPTK_WINE}/lib/external/D3DMetal.framework" ]] || {
|
||||
echo "❌ Нет Game Porting Toolkit.app"
|
||||
echo " brew install --cask gcenx/wine/game-porting-toolkit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir -p "${EXT}"
|
||||
ditto "${GPTK_WINE}/lib/external/D3DMetal.framework" "${EXT}/D3DMetal.framework"
|
||||
cp -f "${GPTK_WINE}/lib/external/libd3dshared.dylib" "${EXT}/libd3dshared.dylib"
|
||||
|
||||
# @rpath @loader_path — копия в корень lib/ (fixmywhisky)
|
||||
cp -Rf "${EXT}/D3DMetal.framework" "${WHISKY_WINE}/lib/D3DMetal.framework"
|
||||
cp -f "${EXT}/libd3dshared.dylib" "${WHISKY_WINE}/lib/libd3dshared.dylib"
|
||||
|
||||
fix_d3dmetal_fw() {
|
||||
local fw="$1"
|
||||
[[ -d "${fw}/Versions" ]] || return 0
|
||||
rm -f "${fw}/D3DMetal" "${fw}/Resources" 2>/dev/null || true
|
||||
ln -sf Versions/Current/D3DMetal "${fw}/D3DMetal"
|
||||
ln -sf Versions/Current/Resources "${fw}/Resources"
|
||||
chmod +x "${fw}/Versions/Current/D3DMetal" 2>/dev/null || true
|
||||
xattr -d com.apple.quarantine "${fw}" 2>/dev/null || true
|
||||
}
|
||||
fix_d3dmetal_fw "${EXT}/D3DMetal.framework"
|
||||
fix_d3dmetal_fw "${WHISKY_WINE}/lib/D3DMetal.framework"
|
||||
chmod +x "${EXT}/libd3dshared.dylib" "${WHISKY_WINE}/lib/libd3dshared.dylib" 2>/dev/null || true
|
||||
xattr -d com.apple.quarantine "${EXT}/libd3dshared.dylib" 2>/dev/null || true
|
||||
|
||||
# Мост D3DMetal: unix .so → libd3dshared. PE d3d12/dxgi — от Gcenx Wine 11.x (не GPTK 7.7).
|
||||
mkdir -p "${WINE_UNIX}"
|
||||
ln -sf ../../external/libd3dshared.dylib "${WINE_UNIX}/d3d12.so"
|
||||
ln -sf ../../external/libd3dshared.dylib "${WINE_UNIX}/dxgi.so"
|
||||
|
||||
touch "${WHISKY_LIBS}/Libraries/.gptk_installed"
|
||||
echo "OK: GPTK D3DMetal + d3d12/dxgi bridge → ${WHISKY_WINE}"
|
||||
echo " Запуск: bash ${GOG_DIR}/prepare-ja3-d3dmetal.sh && bash ${GOG_DIR}/run-ja3.sh"
|
||||
58
scripts/games/gog/install-ja3-dxmt.sh
Executable file
58
scripts/games/gog/install-ja3-dxmt.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
# DXMT (D3D10/11 → Metal) для JA3 без GPTK. JA3 на D3D12 всё равно нужен GPTK; скрипт + -graphicsapi d3d11.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
DXMT_CACHE="${GAMES_DIR}/cache/dxmt"
|
||||
DXMT_TAR="${DXMT_CACHE}/dxmt-v0.80-builtin.tar.gz"
|
||||
DXMT_URL="https://github.com/3Shain/dxmt/releases/download/v0.80/dxmt-v0.80-builtin.tar.gz"
|
||||
WINE_LIB="${WHISKY_LIBS}/Libraries/Wine/lib"
|
||||
WINE_LIB64="${WINE_LIB}/wine/x86_64-windows"
|
||||
WINE_UNIX="${WINE_LIB}/wine/x86_64-unix"
|
||||
SYS32="${BOTTLE_DIR}/drive_c/windows/system32"
|
||||
|
||||
gog_wine_check
|
||||
mkdir -p "${DXMT_CACHE}" "${SYS32}"
|
||||
[[ -f "${DXMT_TAR}" ]] || curl -L --fail -o "${DXMT_TAR}" "${DXMT_URL}"
|
||||
tar -xzf "${DXMT_TAR}" -C "${DXMT_CACHE}"
|
||||
|
||||
# builtin layout: bin/ or root with dlls — find files
|
||||
DXMT_ROOT="${DXMT_CACHE}"
|
||||
for sub in dxmt-v0.80-builtin bin .; do
|
||||
[[ -f "${DXMT_CACHE}/${sub}/d3d11.dll" ]] && DXMT_ROOT="${DXMT_CACHE}/${sub}" && break
|
||||
done
|
||||
[[ -f "${DXMT_ROOT}/d3d11.dll" ]] || DXMT_ROOT="$(find "${DXMT_CACHE}" -name d3d11.dll -print -quit | xargs dirname)"
|
||||
|
||||
install_one() {
|
||||
local src="$1" dest="$2"
|
||||
[[ -f "${src}" ]] || return 0
|
||||
cp -f "${src}" "${dest}/"
|
||||
}
|
||||
|
||||
install_one "${DXMT_ROOT}/winemetal.so" "${WINE_UNIX}"
|
||||
install_one "${DXMT_ROOT}/winemetal.dll" "${WINE_LIB64}"
|
||||
install_one "${DXMT_ROOT}/winemetal.dll" "${SYS32}"
|
||||
install_one "${DXMT_ROOT}/d3d11.dll" "${WINE_LIB64}"
|
||||
install_one "${DXMT_ROOT}/dxgi.dll" "${WINE_LIB64}"
|
||||
install_one "${DXMT_ROOT}/d3d10core.dll" "${WINE_LIB64}"
|
||||
# Только wine lib + system32 (не в папку игры — иначе конфликт с overrides).
|
||||
install_one "${DXMT_ROOT}/d3d11.dll" "${SYS32}"
|
||||
install_one "${DXMT_ROOT}/dxgi.dll" "${SYS32}"
|
||||
install_one "${DXMT_ROOT}/d3d10core.dll" "${SYS32}"
|
||||
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
"${WINE64}" reg add 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d12 /d disabled /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d11 /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v d3d10core /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKEY_CURRENT_USER\Software\Wine\DllOverrides' /v dxgi /f >/dev/null 2>&1 || true
|
||||
|
||||
touch "${BOTTLE_DIR}/.ja3_dxmt_installed"
|
||||
if [[ -f "${BOTTLE_DIR}/Metadata.plist" ]]; then
|
||||
/usr/libexec/PlistBuddy -c 'Set :dxvkConfig:dxvk false' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c 'Set :performanceConfig:forceD3D11 true' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
fi
|
||||
echo "OK: DXMT v0.80 в ${BOTTLE_NAME} (+ game dir)"
|
||||
203
scripts/games/gog/install-ja3-innoextract.sh
Executable file
203
scripts/games/gog/install-ja3-innoextract.sh
Executable file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3: multipart GOG → innoextract в bottle (без Wine-Inno). Экономия диска: .bin удаляются по ходу.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
JA3_GAMES_DIR="${JA3_GAMES_DIR:-${HOME}/Games}"
|
||||
JA3_PREFIX='setup_jagged_alliance_3_1.5.2_(80730)'
|
||||
JA3_EXE="${JA3_GAMES_DIR}/${JA3_PREFIX}.exe"
|
||||
JA3_TARGET="${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3"
|
||||
MARKER="${BOTTLE_DIR}/.ja3_installed"
|
||||
LOG="/tmp/ja3-innoextract-${BOTTLE_NAME}.log"
|
||||
JA3_MIN_FREE_MB="${JA3_MIN_FREE_MB:-800}"
|
||||
JA3_KEEP_BINS="${JA3_KEEP_BINS:-0}"
|
||||
|
||||
gog_disk_free_mb() {
|
||||
local kb
|
||||
kb="$(gog_disk_free_kb)"
|
||||
echo $((kb / 1024))
|
||||
}
|
||||
|
||||
gog_disk_watch() {
|
||||
local min_mb="$1"
|
||||
local free
|
||||
free="$(gog_disk_free_mb)"
|
||||
if [[ "${free}" -lt "${min_mb}" ]]; then
|
||||
echo "❌ Мало места на диске: ${free} MB свободно (нужно ≥${min_mb} MB)" >&2
|
||||
df -h "$(gog_data_volume_path)" | awk 'NR==2' >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
gog_ja3_safe_cleanup() {
|
||||
local freed=0
|
||||
local d f
|
||||
|
||||
for d in \
|
||||
"${GAMES_CACHE_DIR}/gog-galaxy-extract-gog-bottle" \
|
||||
"${GAMES_CACHE_DIR}/ja3-extract-test" \
|
||||
"${GAMES_CACHE_DIR}/ja3-innoextract-staging"; do
|
||||
if [[ -d "${d}" ]]; then
|
||||
local sz
|
||||
sz="$(du -sk "${d}" | awk '{print $1}')"
|
||||
rm -rf "${d}"
|
||||
freed=$((freed + sz))
|
||||
echo " удалён cache: ${d}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -f "${GAMES_CACHE_DIR}/setup_galaxy.exe" ]] \
|
||||
&& [[ -f "${BOTTLE_DIR}/drive_c/Program Files (x86)/GOG Galaxy/GalaxyClient.exe" ]]; then
|
||||
f="${GAMES_CACHE_DIR}/setup_galaxy.exe"
|
||||
sz="$(du -sk "${f}" | awk '{print $1}')"
|
||||
rm -f "${f}"
|
||||
freed=$((freed + sz))
|
||||
echo " удалён setup_galaxy.exe (Galaxy уже в bottle)"
|
||||
fi
|
||||
|
||||
if [[ "${freed}" -gt 0 ]]; then
|
||||
echo "Освобождено ~$((freed / 1024)) MB (cache)"
|
||||
fi
|
||||
}
|
||||
|
||||
ja3_bin_path() {
|
||||
echo "${JA3_GAMES_DIR}/${JA3_PREFIX}-$1.bin"
|
||||
}
|
||||
|
||||
ja3_delete_bin() {
|
||||
local n="$1"
|
||||
local p
|
||||
p="$(ja3_bin_path "${n}")"
|
||||
if [[ -f "${p}" ]]; then
|
||||
rm -f "${p}"
|
||||
echo " 🗑 удалён $(basename "${p}") (~$(du -sh "${p}" 2>/dev/null | cut -f1 || echo '?'))"
|
||||
gog_disk_watch "${JA3_MIN_FREE_MB}" || true
|
||||
fi
|
||||
}
|
||||
|
||||
ja3_delete_all_bins() {
|
||||
local n
|
||||
for n in 1 2 3 4; do
|
||||
ja3_delete_bin "${n}" || true
|
||||
done
|
||||
if [[ -f "${JA3_EXE}" && "${JA3_KEEP_BINS}" != "1" ]]; then
|
||||
rm -f "${JA3_EXE}"
|
||||
echo " 🗑 удалён $(basename "${JA3_EXE}")"
|
||||
fi
|
||||
}
|
||||
|
||||
ja3_watch_bins() {
|
||||
local line opened=0
|
||||
while IFS= read -r line; do
|
||||
printf '%s\n' "${line}"
|
||||
case "${line}" in
|
||||
*"Opening \"${JA3_PREFIX}-2.bin\""*)
|
||||
ja3_delete_bin 1
|
||||
opened=2
|
||||
;;
|
||||
*"Opening \"${JA3_PREFIX}-3.bin\""*)
|
||||
ja3_delete_bin 2
|
||||
opened=3
|
||||
;;
|
||||
*"Opening \"${JA3_PREFIX}-4.bin\""*)
|
||||
ja3_delete_bin 3
|
||||
opened=4
|
||||
;;
|
||||
esac
|
||||
gog_disk_watch "${JA3_MIN_FREE_MB}" || return 1
|
||||
done
|
||||
if [[ "${opened}" -ge 4 ]]; then
|
||||
ja3_delete_bin 4 || true
|
||||
fi
|
||||
}
|
||||
|
||||
verify() {
|
||||
local n
|
||||
[[ -f "${JA3_EXE}" ]] || {
|
||||
echo "❌ Нет: ${JA3_EXE}"
|
||||
return 1
|
||||
}
|
||||
for n in 1 2 3 4; do
|
||||
[[ -f "$(ja3_bin_path "${n}")" ]] || {
|
||||
echo "❌ Нет: $(ja3_bin_path "${n}")"
|
||||
return 1
|
||||
}
|
||||
done
|
||||
command -v innoextract >/dev/null 2>&1 || {
|
||||
echo "❌ brew install innoextract"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
verify || exit 1
|
||||
gog_wine_check
|
||||
|
||||
if [[ -f "${JA3_TARGET}/JA3.exe" ]]; then
|
||||
echo "JA3 уже установлен: ${JA3_TARGET}/JA3.exe"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/emergency-stop.sh" || true
|
||||
|
||||
echo "=== Очистка cache (без трогания архивов игры) ==="
|
||||
gog_ja3_safe_cleanup
|
||||
|
||||
gog_disk_watch "${JA3_MIN_FREE_MB}" || {
|
||||
echo "❌ Перед установкой нужно ≥$((JA3_MIN_FREE_MB / 1024)) GB свободно."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Свободно на Data: $(gog_disk_free_mb) MB"
|
||||
echo "Распаковка JA3 → ${JA3_TARGET}"
|
||||
echo "Лог: ${LOG}"
|
||||
echo "(архивы .bin удаляются по мере чтения innoextract)"
|
||||
echo ""
|
||||
|
||||
rm -rf "${JA3_TARGET}"
|
||||
mkdir -p "${JA3_TARGET}"
|
||||
: >"${LOG}"
|
||||
|
||||
cd "${JA3_GAMES_DIR}"
|
||||
set +e
|
||||
innoextract -e -m -d "${JA3_TARGET}" "$(basename "${JA3_EXE}")" 2>&1 \
|
||||
| tee "${LOG}" | ja3_watch_bins
|
||||
local rc=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
if [[ "${rc}" -ne 0 ]]; then
|
||||
echo ""
|
||||
echo "❌ innoextract exit ${rc}"
|
||||
tail -15 "${LOG}" 2>/dev/null || true
|
||||
df -h "$(gog_data_volume_path)" | awk 'NR==2'
|
||||
if [[ "${JA3_CLEANUP_PARTIAL:-1}" == "1" ]]; then
|
||||
echo "Удаляю незавершённую распаковку в bottle…"
|
||||
rm -rf "${JA3_TARGET}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "${JA3_TARGET}/JA3.exe" ]]; then
|
||||
bash "${GOG_DIR}/finish-ja3-layout.sh"
|
||||
date -u +"%Y-%m-%dT%H:%M:%SZ" >"${MARKER}"
|
||||
if [[ "${JA3_KEEP_BINS}" != "1" ]]; then
|
||||
echo ""
|
||||
echo "Удаляю GOG-архивы (~15 GB)…"
|
||||
ja3_delete_all_bins
|
||||
fi
|
||||
echo ""
|
||||
echo "OK: JA3 установлен."
|
||||
echo " Свободно: $(gog_disk_free_mb) MB"
|
||||
echo " Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
else
|
||||
echo "❌ JA3.exe не найден"
|
||||
find "${JA3_TARGET}" -maxdepth 2 -iname 'JA3.exe' 2>/dev/null | head -5
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
152
scripts/games/gog/install-ja3-multipart.sh
Executable file
152
scripts/games/gog/install-ja3-multipart.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 из GOG offline backup: setup_*.exe + setup_*-1.bin … -4.bin в одной папке.
|
||||
# Galaxy не нужен. Установщик читает .bin рядом с .exe (не копируем 15 GB в bottle).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
JA3_GAMES_DIR="${JA3_GAMES_DIR:-${HOME}/Games}"
|
||||
JA3_SETUP_EXE="${JA3_SETUP_EXE:-}"
|
||||
LOG="/tmp/ja3-multipart-install-${BOTTLE_NAME}.log"
|
||||
|
||||
# Имена GOG v1.5.2 (80730); при другой версии задайте JA3_SETUP_EXE.
|
||||
JA3_EXE_DEFAULT="${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730).exe"
|
||||
JA3_BINS=(
|
||||
"${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730)-1.bin"
|
||||
"${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730)-2.bin"
|
||||
"${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730)-3.bin"
|
||||
"${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730)-4.bin"
|
||||
)
|
||||
|
||||
gog_unix_to_win() {
|
||||
local u="$1"
|
||||
if [[ -x "${WINEPATH_BIN}" ]]; then
|
||||
"${WINEPATH_BIN}" -w "${u}"
|
||||
else
|
||||
# fallback: Z: = /
|
||||
local rest="${u#/}"
|
||||
echo "Z:\\\\${rest//\//\\\\}"
|
||||
fi
|
||||
}
|
||||
|
||||
find_setup_exe() {
|
||||
if [[ -n "${JA3_SETUP_EXE}" && -f "${JA3_SETUP_EXE}" ]]; then
|
||||
echo "${JA3_SETUP_EXE}"
|
||||
return 0
|
||||
fi
|
||||
if [[ -f "${JA3_EXE_DEFAULT}" ]]; then
|
||||
echo "${JA3_EXE_DEFAULT}"
|
||||
return 0
|
||||
fi
|
||||
local f
|
||||
f="$(find "${JA3_GAMES_DIR}" -maxdepth 1 -iname 'setup*jagged*alliance*3*.exe' -print -quit 2>/dev/null || true)"
|
||||
[[ -n "${f}" && -f "${f}" ]] && echo "${f}" && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Использование: bash $0
|
||||
|
||||
Папка с архивами (по умолчанию): ${JA3_GAMES_DIR}
|
||||
|
||||
Нужны 5 файлов в одной папке:
|
||||
setup_jagged_alliance_3_1.5.2_(80730).exe (~1 MB, лаунчер Inno)
|
||||
setup_jagged_alliance_3_1.5.2_(80730)-1.bin … -4.bin
|
||||
|
||||
Скачать недостающий .exe: https://www.gog.com/account → JA3 → offline backup (первая, ~1 MB часть).
|
||||
|
||||
Или:
|
||||
lgogdownloader --login
|
||||
bash ${GOG_DIR}/fetch-ja3-setup-exe.sh
|
||||
|
||||
Переменные: JA3_GAMES_DIR, JA3_SETUP_EXE
|
||||
EOF
|
||||
}
|
||||
|
||||
verify_parts() {
|
||||
local exe="$1"
|
||||
local missing=0
|
||||
local b
|
||||
|
||||
if [[ ! -d "${JA3_GAMES_DIR}" ]]; then
|
||||
echo "❌ Нет каталога: ${JA3_GAMES_DIR}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
for b in "${JA3_BINS[@]}"; do
|
||||
if [[ ! -f "${b}" ]]; then
|
||||
echo "❌ Нет: ${b}"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -f "${exe}" ]]; then
|
||||
echo "❌ Нет лаунчера .exe (~1 MB) в ${JA3_GAMES_DIR}"
|
||||
echo " У вас есть 4× .bin — без .exe установка GOG не стартует."
|
||||
echo " Скачайте с account.gog.com первую часть offline backup (installer0, ~1 MB)."
|
||||
echo " Или: bash ${GOG_DIR}/fetch-ja3-setup-exe.sh"
|
||||
missing=1
|
||||
fi
|
||||
|
||||
[[ "${missing}" -eq 0 ]]
|
||||
}
|
||||
|
||||
main() {
|
||||
local exe win_exe
|
||||
exe="$(find_setup_exe)" || exe=""
|
||||
|
||||
if ! verify_parts "${exe}"; then
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/emergency-stop.sh" || true
|
||||
gog_swap_guard "${GOG_SWAP_MAX_MB:-400}" || exit 1
|
||||
# bins (~15 GB) + распаковка в bottle (~16 GB) — пик ~25–30 GB на Data
|
||||
gog_disk_guard "${JA3_MIN_DISK_GB:-25}" || {
|
||||
echo "❌ Мало места: нужно ≥25 GB свободно на Data (архивы + игра в bottle)."
|
||||
echo " Сейчас: $(df -h "$(gog_data_volume_path)" | awk 'NR==2 {print $4 " свободно, " $5 " занято"}')"
|
||||
exit 1
|
||||
}
|
||||
|
||||
export_wine_env_gog
|
||||
gog_wine_check
|
||||
|
||||
win_exe="$(gog_unix_to_win "${exe}")"
|
||||
SILENT=(/VERYSILENT /SUPPRESSMSGBOXES /NORESTART)
|
||||
|
||||
echo "Установка JA3 из multipart (без Galaxy)"
|
||||
echo " Папка: ${JA3_GAMES_DIR}"
|
||||
echo " Wine: ${win_exe}"
|
||||
echo " Лог: ${LOG}"
|
||||
: >"${LOG}"
|
||||
|
||||
# Inno ищет .bin в каталоге установщика — запуск из JA3_GAMES_DIR
|
||||
cd "${JA3_GAMES_DIR}"
|
||||
if ! "${WINE64}" "${win_exe}" "${SILENT[@]}" >>"${LOG}" 2>&1; then
|
||||
echo "⚠️ Wine-Inno не прошёл — пробуем innoextract (без botva2/gdi32)…"
|
||||
if command -v innoextract >/dev/null 2>&1; then
|
||||
exec bash "${GOG_DIR}/install-ja3-innoextract.sh"
|
||||
fi
|
||||
echo "❌ Установщик завершился с ошибкой:"
|
||||
strings "${LOG}" 2>/dev/null | tail -20 || tail -20 "${LOG}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3/JA3.exe" ]]; then
|
||||
date -u +"%Y-%m-%dT%H:%M:%SZ" >"${BOTTLE_DIR}/.ja3_installed"
|
||||
echo ""
|
||||
echo "OK: JA3 установлен."
|
||||
echo " Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
echo " После проверки игры можно удалить .bin в ${JA3_GAMES_DIR} (~15 GB)."
|
||||
else
|
||||
echo "⚠️ JA3.exe не найден в bottle — tail лога:"
|
||||
tail -40 "${LOG}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
64
scripts/games/gog/install-ja3-offline.sh
Executable file
64
scripts/games/gog/install-ja3-offline.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 из GOG offline backup installer — без Galaxy. Inno /VERYSILENT.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
GAMES_DIR="$(cd "${GOG_DIR}/.." && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
INSTALLER="${1:-}"
|
||||
LOG="/tmp/ja3-offline-install-${BOTTLE_NAME}.log"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Использование: bash $0 /path/to/setup_jagged_alliance_3_*.exe
|
||||
|
||||
Скачать (Windows offline backup):
|
||||
https://www.gog.com/account → Jagged Alliance 3 → Download offline backup game installers
|
||||
|
||||
Документация: docs/games/JA3_MAC_WITHOUT_GALAXY.md
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ -z "${INSTALLER}" || ! -f "${INSTALLER}" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/emergency-stop.sh"
|
||||
gog_swap_guard "${GOG_SWAP_MAX_MB:-400}" || exit 1
|
||||
gog_disk_guard 40 || {
|
||||
echo "❌ Для JA3 нужно ~40+ GB свободно на Data (игра + установка)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
export_wine_env_gog
|
||||
gog_wine_check
|
||||
|
||||
# Inno silent (GOG offline installers)
|
||||
SILENT=(/VERYSILENT /SUPPRESSMSGBOXES /NORESTART)
|
||||
local_base="$(basename "${INSTALLER}")"
|
||||
cp -f "${INSTALLER}" "${BOTTLE_DIR}/drive_c/${local_base}"
|
||||
|
||||
win="C:\\${local_base}"
|
||||
echo "Установка JA3 (без Galaxy): ${INSTALLER}"
|
||||
echo "Лог: ${LOG}"
|
||||
: >"${LOG}"
|
||||
|
||||
cd "${BOTTLE_DIR}/drive_c"
|
||||
if ! "${WINE64}" "${win}" "${SILENT[@]}" >>"${LOG}" 2>&1; then
|
||||
echo "❌ Установщик завершился с ошибкой. tail:"
|
||||
tail -40 "${LOG}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3/JA3.exe" ]]; then
|
||||
date -u +"%Y-%m-%dT%H:%M:%SZ" >"${BOTTLE_DIR}/.ja3_installed"
|
||||
echo ""
|
||||
echo "OK: JA3 установлен."
|
||||
echo " Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
else
|
||||
echo "⚠️ JA3.exe пока не найден — проверьте лог, возможно нужны ещё offline .exe (DLC/base):"
|
||||
tail -30 "${LOG}"
|
||||
exit 1
|
||||
fi
|
||||
35
scripts/games/gog/install-ja3-redist.sh
Executable file
35
scripts/games/gog/install-ja3-redist.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# VC++ 2015 из __redist JA3 (innoextract не запускает установщики).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
JA3="${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3"
|
||||
VC_X86="${JA3}/__redist/MSVC2015/VC_redist.x86.exe"
|
||||
VC_X64="${JA3}/__redist/MSVC2015_x64/VC_redist.x64.exe"
|
||||
MARKER="${BOTTLE_DIR}/.ja3_redist_installed"
|
||||
|
||||
[[ -f "${JA3}/JA3.exe" ]] || {
|
||||
echo "❌ Сначала install-ja3-innoextract.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ -f "${MARKER}" ]]; then
|
||||
echo "JA3 redist уже ставился: ${MARKER}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export_wine_env_gog
|
||||
gog_wine_check
|
||||
|
||||
for vc in "${VC_X86}" "${VC_X64}"; do
|
||||
[[ -f "${vc}" ]] || continue
|
||||
base="$(basename "${vc}")"
|
||||
cp -f "${vc}" "${BOTTLE_DIR}/drive_c/${base}"
|
||||
echo "VC++ ${base}…"
|
||||
"${WINE64}" "C:\\${base}" /install /quiet /norestart || true
|
||||
done
|
||||
|
||||
date -u +"%Y-%m-%dT%H:%M:%SZ" >"${MARKER}"
|
||||
echo "OK: VC++ redist для JA3"
|
||||
53
scripts/games/gog/install-ja3-wine-finish.sh
Executable file
53
scripts/games/gog/install-ja3-wine-finish.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# Довести установку JA3 через Wine-Inno (пост-скрипты GOG), если есть архивы в JA3_GAMES_DIR.
|
||||
# innoextract одного не хватает для Lua/VFS — Wine-установщик создаёт правильный layout.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
JA3_GAMES_DIR="${JA3_GAMES_DIR:-${HOME}/Games}"
|
||||
PREFIX='setup_jagged_alliance_3_1.5.2_(80730)'
|
||||
EXE="${JA3_GAMES_DIR}/${PREFIX}.exe"
|
||||
TARGET='C:\GOG Games\Jagged Alliance 3'
|
||||
|
||||
verify_archives() {
|
||||
[[ -f "${EXE}" ]] || {
|
||||
echo "❌ Нет ${EXE}"
|
||||
echo " Положите 5 частей offline backup в ${JA3_GAMES_DIR}"
|
||||
exit 1
|
||||
}
|
||||
local n
|
||||
for n in 1 2 3 4; do
|
||||
[[ -f "${JA3_GAMES_DIR}/${PREFIX}-${n}.bin" ]] || {
|
||||
echo "❌ Нет ${JA3_GAMES_DIR}/${PREFIX}-${n}.bin"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
verify_archives
|
||||
bash "${GOG_DIR}/emergency-stop.sh" || true
|
||||
gog_swap_guard "${GOG_SWAP_MAX_MB:-800}" || exit 1
|
||||
gog_disk_guard 20 || exit 1
|
||||
|
||||
export_wine_env_gog
|
||||
gog_wine_check
|
||||
|
||||
local win
|
||||
win="$("${WINEPATH_BIN}" -w "${EXE}")"
|
||||
echo "Wine-установщик GOG (пост-скрипты, Lua.hpk)…"
|
||||
echo " ${win}"
|
||||
cd "${JA3_GAMES_DIR}"
|
||||
if ! "${WINE64}" "${win}" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /DIR="${TARGET}"; then
|
||||
echo "❌ Wine-Inno упал — см. docs/games/TROUBLESHOOTING.md § Setup gdi32"
|
||||
echo " Запасной путь: innoextract + -unpacked (run-ja3.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/finish-ja3-layout.sh"
|
||||
echo "OK. Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
66
scripts/games/gog/install-ja3.sh
Executable file
66
scripts/games/gog/install-ja3.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 без GOG Galaxy: подготовка bottle + подсказка по offline installer.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
echo "=== Jagged Alliance 3 без GOG Galaxy ==="
|
||||
echo "Док: docs/games/JA3_MAC_WITHOUT_GALAXY.md"
|
||||
echo ""
|
||||
|
||||
bash "${GOG_DIR}/emergency-stop.sh" || true
|
||||
|
||||
USED="$(bash -c 'source '"${GOG_DIR}"'/config.sh 2>/dev/null; gog_swap_used_mb' 2>/dev/null || echo 0)"
|
||||
if [[ "${USED:-0}" -gt 400 ]] 2>/dev/null; then
|
||||
echo "❌ Swap ~${USED}MB — сначала перезагрузите Mac, затем снова этот скрипт."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash "${GOG_DIR}/setup-ja3-bottle.sh"
|
||||
|
||||
echo ""
|
||||
echo "=== Скачайте offline installer (Windows) ==="
|
||||
echo " https://www.gog.com/account → Jagged Alliance 3"
|
||||
echo " → Download offline backup game installers (все части)"
|
||||
echo ""
|
||||
open "https://www.gog.com/account" 2>/dev/null || true
|
||||
|
||||
JA3_GAMES_DIR="${HOME}/Games"
|
||||
ja3_bins_ok=1
|
||||
for n in 1 2 3 4; do
|
||||
[[ -f "${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730)-${n}.bin" ]] || ja3_bins_ok=0
|
||||
done
|
||||
if [[ "${ja3_bins_ok}" -eq 1 ]]; then
|
||||
echo "Найдены 4× .bin в ${JA3_GAMES_DIR} — multipart offline backup."
|
||||
if [[ -f "${JA3_GAMES_DIR}/setup_jagged_alliance_3_1.5.2_(80730).exe" ]] \
|
||||
|| find "${JA3_GAMES_DIR}" -maxdepth 1 -iname 'setup*jagged*alliance*3*.exe' -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "Запуск install-ja3-innoextract.sh (innoextract, без Wine-Inno) …"
|
||||
exec bash "${GOG_DIR}/install-ja3-innoextract.sh"
|
||||
fi
|
||||
echo "❌ Не хватает лаунчера .exe (~1 MB) рядом с .bin."
|
||||
echo " Скачайте с https://www.gog.com/account или:"
|
||||
echo " bash ${GOG_DIR}/fetch-ja3-setup-exe.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Автоустановка, если installer уже в Downloads
|
||||
found=()
|
||||
while IFS= read -r -d '' f; do
|
||||
found+=("$f")
|
||||
done < <(find "${HOME}/Downloads" -maxdepth 1 -iname 'setup*jagged*alliance*3*.exe' -print0 2>/dev/null)
|
||||
|
||||
if [[ ${#found[@]} -eq 0 ]]; then
|
||||
echo "Когда .exe скачан (или все 5 частей в ~/Games):"
|
||||
echo " bash ${GOG_DIR}/install-ja3-offline.sh ~/Downloads/setup_jagged_alliance_3_*.exe"
|
||||
echo " bash ${GOG_DIR}/install-ja3-multipart.sh"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Найдены installers в Downloads:"
|
||||
printf ' %s\n' "${found[@]}"
|
||||
echo ""
|
||||
for f in "${found[@]}"; do
|
||||
echo "Установка: ${f}"
|
||||
bash "${GOG_DIR}/install-ja3-offline.sh" "${f}" || exit 1
|
||||
done
|
||||
echo ""
|
||||
echo "Запуск: bash ${GOG_DIR}/run-ja3.sh"
|
||||
6
scripts/games/gog/ja3-dxvk.conf
Normal file
6
scripts/games/gog/ja3-dxvk.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
# JA3 на Apple Silicon + MoltenVK (Whisky / DXVK-macOS)
|
||||
dxgi.hideIntelGpu = True
|
||||
d3d11.enableFeatureLevel11_1 = True
|
||||
d3d11.maxFeatureLevel = 12_1
|
||||
d3d11.dxgiBindBackBufferAsRT = True
|
||||
dxgi.deferSurfaceCreation = True
|
||||
9
scripts/games/gog/launch-galaxy-ui.sh
Executable file
9
scripts/games/gog/launch-galaxy-ui.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Запуск GOG Galaxy с максимальным шансом увидеть окно на macOS:
|
||||
# виртуальный desktop Wine + foreground в этом терминале.
|
||||
set -euo pipefail
|
||||
# Suppress verbose MoltenVK capability dump in foreground mode.
|
||||
export MVK_CONFIG_LOG_LEVEL=0
|
||||
export GOG_USE_WINEVDESKTOP=1
|
||||
export GOG_LAUNCH_FG=1
|
||||
exec bash "$(cd "$(dirname "$0")" && pwd)/launch-galaxy.sh" "$@"
|
||||
22
scripts/games/gog/launch-galaxy-whisky.sh
Executable file
22
scripts/games/gog/launch-galaxy-whisky.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# Запуск GOG Galaxy через Whisky GUI (pin gog-galaxy) — лучший шанс увидеть окно на macOS.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
bash "${GOG_DIR}/pin-galaxy-whisky.sh" >/dev/null 2>&1 || bash "${GOG_DIR}/pin-galaxy-whisky.sh"
|
||||
bash "${GOG_DIR}/reset.sh"
|
||||
|
||||
echo "Открываю Whisky → bottle «${BOTTLE_NAME}»."
|
||||
echo ""
|
||||
echo "В GUI Whisky:"
|
||||
echo " 1) Выберите bottle «${BOTTLE_NAME}»"
|
||||
echo " 2) Pin «gog-galaxy» → Run (не GalaxySetup_full / не web-stub)"
|
||||
echo " 3) Cmd+Tab → wine64 / GOG Galaxy"
|
||||
echo ""
|
||||
echo "Если pin нет: bash ${GOG_DIR}/pin-galaxy-whisky.sh"
|
||||
echo "Логи: bash ${GOG_DIR}/collect-gog-logs-7h.sh"
|
||||
echo ""
|
||||
|
||||
open -a Whisky 2>/dev/null || open -a "Whisky.app"
|
||||
133
scripts/games/gog/launch-galaxy.sh
Executable file
133
scripts/games/gog/launch-galaxy.sh
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env bash
|
||||
# GOG Galaxy в GOG-bottle (CEF). По умолчанию — виртуальный desktop Wine (окно видно на Mac).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
# shellcheck source=gog-process.sh
|
||||
source "${GOG_DIR}/gog-process.sh"
|
||||
|
||||
LOG="/tmp/gog-galaxy-client-${BOTTLE_NAME}.log"
|
||||
GALAXY_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/GalaxyClient.log"
|
||||
INIT_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/GalaxyInitialization.log"
|
||||
CEF_LOG="${BOTTLE_DIR}/drive_c/ProgramData/GOG.com/Galaxy/logs/cef.log"
|
||||
|
||||
gog_launch_wine() {
|
||||
local win='C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe'
|
||||
local -a args=()
|
||||
mapfile -t args < <(gog_galaxy_cef_args_array)
|
||||
|
||||
cd "${BOTTLE_DIR}/drive_c"
|
||||
if [[ "${GOG_USE_WINEVDESKTOP:-1}" == "1" ]]; then
|
||||
local w h
|
||||
IFS='x' read -r w h <<< "${GOG_VDESKTOP:-1280x800}"
|
||||
echo "Режим: explorer /desktop=GOGGalaxy,${w}x${h}"
|
||||
if [[ "${GOG_LAUNCH_FG:-0}" == "1" ]]; then
|
||||
exec "${WINE64}" explorer "/desktop=GOGGalaxy,${w}x${h}" "${win}" "${args[@]}"
|
||||
fi
|
||||
nohup "${WINE64}" explorer "/desktop=GOGGalaxy,${w}x${h}" "${win}" "${args[@]}" >>"${LOG}" 2>&1 &
|
||||
else
|
||||
echo "Режим: прямой GalaxyClient (GOG_USE_WINEVDESKTOP=0)"
|
||||
if [[ "${GOG_LAUNCH_FG:-0}" == "1" ]]; then
|
||||
exec "${WINE64}" "${win}" "${args[@]}"
|
||||
fi
|
||||
nohup "${WINE64}" "${win}" "${args[@]}" >>"${LOG}" 2>&1 &
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
gog_wine_check
|
||||
gog_swap_guard "${GOG_SWAP_MAX_MB:-400}" || exit 1
|
||||
gog_disk_guard 8 || exit 1
|
||||
if [[ ! -f "${BOTTLE_DIR}/.gog_galaxy_extract_installed" ]]; then
|
||||
echo "Сначала: bash ${GOG_DIR}/install-galaxy-extract.sh"
|
||||
exit 1
|
||||
fi
|
||||
if ! find "${BOTTLE_DIR}/drive_c/Program Files"* -name 'GalaxyClient.exe' -print -quit 2>/dev/null | grep -q .; then
|
||||
bash "${GOG_DIR}/install-galaxy-extract.sh"
|
||||
fi
|
||||
|
||||
echo "Останавливаем старые процессы GOG Galaxy…"
|
||||
gog_kill_all
|
||||
pkill -f 'explorer.exe /desktop=GOGGalaxy' 2>/dev/null || true
|
||||
|
||||
if gog_galaxy_running; then
|
||||
echo "❌ Galaxy всё ещё в процессах — повторите: bash ${GOG_DIR}/reset.sh"
|
||||
pgrep -fl 'Galaxy' 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export_wine_env_gog
|
||||
echo "Bottle: ${BOTTLE_NAME}"
|
||||
echo "Лог: ${LOG}"
|
||||
: >"${LOG}"
|
||||
|
||||
if [[ "${GOG_LAUNCH_FG:-0}" == "1" ]]; then
|
||||
echo ""
|
||||
echo "Foreground: окно Wine привязано к этому терминалу. Закройте Galaxy — Ctrl+C."
|
||||
echo "Ищите окно «GOGGalaxy» или полосу заголовка Wine desktop."
|
||||
echo ""
|
||||
gog_launch_wine
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gog_launch_wine
|
||||
echo "Запуск wine64…"
|
||||
|
||||
local ok=0 i
|
||||
for i in $(seq 1 30); do
|
||||
sleep 1
|
||||
if grep -q 'Second client instance detected' "${INIT_LOG}" 2>/dev/null; then
|
||||
if tail -5 "${INIT_LOG}" 2>/dev/null | grep -q 'The client will exit'; then
|
||||
echo "❌ Второй экземпляр: bash ${GOG_DIR}/reset.sh && bash $0"
|
||||
tail -8 "${INIT_LOG}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ -f "${GALAXY_LOG}" ]] && grep -qE 'Showing login form|No refresh token found|will continue' "${GALAXY_LOG}" 2>/dev/null; then
|
||||
ok=1
|
||||
break
|
||||
fi
|
||||
if [[ -f "${INIT_LOG}" ]] && grep -q 'will continue' "${INIT_LOG}" 2>/dev/null; then
|
||||
ok=1
|
||||
break
|
||||
fi
|
||||
if ! gog_galaxy_running; then
|
||||
sleep 2
|
||||
if ! gog_galaxy_running; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if ! gog_galaxy_running; then
|
||||
echo "❌ GalaxyClient не остался в процессах:"
|
||||
tail -25 "${LOG}"
|
||||
[[ -f "${INIT_LOG}" ]] && tail -15 "${INIT_LOG}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
gog_mac_activate_wine || true
|
||||
|
||||
echo ""
|
||||
echo "OK: GOG Galaxy запущен (процесс + login в логе — окно на Mac не гарантировано)."
|
||||
echo " • Окно: bash ${GOG_DIR}/launch-galaxy-whisky.sh → Whisky → pin gog-galaxy → Run"
|
||||
echo " • RCA/логи 7ч: docs/games/GOG_GALAXY_7H_RCA.md | collect-gog-logs-7h.sh"
|
||||
if [[ "${GOG_USE_WINEVDESKTOP:-1}" == "1" ]]; then
|
||||
echo " • Cmd+Tab → «wine64» / «GOGGalaxy» — отдельное окно виртуального стола Wine"
|
||||
else
|
||||
echo " • Cmd+Tab → «GalaxyClient» или «wine64»"
|
||||
fi
|
||||
echo " • Не видно окна? bash ${GOG_DIR}/launch-galaxy-ui.sh (vdesktop + foreground)"
|
||||
echo " • Whisky → «${BOTTLE_NAME}» → pin gog-galaxy → Run"
|
||||
echo " • Лог: ${GALAXY_LOG}"
|
||||
if [[ "${ok}" -eq 1 ]]; then
|
||||
echo " • В логе login UI — войдите → Jagged Alliance 3 → Install"
|
||||
if [[ -f "${CEF_LOG}" ]] && grep -q 'parent window' "${CEF_LOG}" 2>/dev/null; then
|
||||
echo " • cef.log: «parent window» — типично для Wine; vdesktop/Whisky Run часто лечит"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
11
scripts/games/gog/list-bottles.sh
Executable file
11
scripts/games/gog/list-bottles.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
echo "Whisky bottles (franke):"
|
||||
for meta in "${HOME}/Library/Containers/com.franke.Whisky/Bottles/"*/Metadata.plist; do
|
||||
[[ -f "${meta}" ]] || continue
|
||||
id="$(basename "$(dirname "${meta}")")"
|
||||
name="$(plutil -extract info.name raw "${meta}" 2>/dev/null || echo "?")"
|
||||
du -sh "$(dirname "${meta}")" 2>/dev/null | awk -v id="${id}" -v n="${name}" '{printf " %s %-20s %s\n", id, n, $1}'
|
||||
done
|
||||
echo ""
|
||||
echo "Привязать GOG: bash $(dirname "$0")/use-bottle.sh <UUID> [имя]"
|
||||
45
scripts/games/gog/pin-galaxy-whisky.sh
Executable file
45
scripts/games/gog/pin-galaxy-whisky.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pin GOG Galaxy в Whisky для bottle GOG-Install (как start.sh gog в HoMM).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
GOG_EXE="${BOTTLE_DIR}/drive_c/Program Files (x86)/GOG Galaxy/GalaxyClient.exe"
|
||||
[[ -f "${GOG_EXE}" ]] || { echo "Нет GalaxyClient — install-galaxy-extract.sh"; exit 1; }
|
||||
|
||||
METADATA="${BOTTLE_DIR}/Metadata.plist"
|
||||
PROG_SETTINGS="${BOTTLE_DIR}/Program Settings/GalaxyClient.exe.plist"
|
||||
GOG_URL="file://${BOTTLE_DIR}/drive_c/Program%20Files%20(x86)/GOG%20Galaxy/GalaxyClient.exe"
|
||||
CEF="$(gog_galaxy_cef_args_array | tr '\n' ' ')"
|
||||
|
||||
python3 <<PY
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
|
||||
meta = Path("${METADATA}")
|
||||
with meta.open("rb") as f:
|
||||
data = plistlib.load(f)
|
||||
pins = data.setdefault("info", {}).setdefault("pins", [])
|
||||
if not any(p.get("name") == "gog-galaxy" for p in pins):
|
||||
pins.append({
|
||||
"name": "gog-galaxy",
|
||||
"removable": False,
|
||||
"url": {"relative": "${GOG_URL}"},
|
||||
})
|
||||
with meta.open("wb") as f:
|
||||
plistlib.dump(data, f)
|
||||
print("pin gog-galaxy added")
|
||||
else:
|
||||
print("pin exists")
|
||||
|
||||
ps = Path("${PROG_SETTINGS}")
|
||||
ps.parent.mkdir(parents=True, exist_ok=True)
|
||||
with ps.open("wb") as f:
|
||||
plistlib.dump({"arguments": "${CEF}", "environment": {}, "locale": ""}, f)
|
||||
print("OK:", ps)
|
||||
PY
|
||||
|
||||
echo ""
|
||||
echo "Whisky → ${BOTTLE_NAME} → pin «gog-galaxy» → Run"
|
||||
echo "Или: bash ${GOG_DIR}/launch-galaxy.sh"
|
||||
84
scripts/games/gog/prepare-ja3-d3dmetal.sh
Executable file
84
scripts/games/gog/prepare-ja3-d3dmetal.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# JA3 + D3DMetal: убрать DXVK/GPTK PE из system32, оставить builtin Gcenx + d3d12/dxgi.so → libd3dshared.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
SYS32="${BOTTLE_DIR}/drive_c/windows/system32"
|
||||
WINE_PE="${WHISKY_LIBS}/Libraries/Wine/lib/wine/x86_64-windows"
|
||||
WINE_UNIX="${WHISKY_LIBS}/Libraries/Wine/lib/wine/x86_64-unix"
|
||||
BAK="${SYS32}/.ja3-d3dmetal-bak"
|
||||
LS="${BOTTLE_DIR}/drive_c/users/${WINE_USER:-eternal}/AppData/Roaming/Jagged Alliance 3/LocalStorage.lua"
|
||||
|
||||
ja3_gptk_available || ja3_bottle_uses_d3dmetal || {
|
||||
echo "❌ Нет GPTK/D3DMetal — configure-ja3-d3dmetal.sh"
|
||||
exit 1
|
||||
}
|
||||
|
||||
bash "${GOG_DIR}/install-gptk-whisky.sh"
|
||||
|
||||
mkdir -p "${BAK}"
|
||||
# DXVK / GPTK PE в system32 ломают D3DMetal (114688/86016 → «graphics subsystem»).
|
||||
for dll in d3d12.dll dxgi.dll d3d11.dll d3d10core.dll d3d12core.dll winemetal.dll; do
|
||||
if [[ -f "${SYS32}/${dll}" ]]; then
|
||||
mv -f "${SYS32}/${dll}" "${BAK}/${dll}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Мост Metal + Gcenx PE (94208/200704). НЕ GPTK PE — __wine_unix_call на Wine 11.
|
||||
# НЕ копировать в system32 DXVK/GPTK DLL.
|
||||
mkdir -p "${WINE_UNIX}"
|
||||
ln -sf ../../external/libd3dshared.dylib "${WINE_UNIX}/d3d12.so"
|
||||
ln -sf ../../external/libd3dshared.dylib "${WINE_UNIX}/dxgi.so"
|
||||
# system32: убрать overrides — builtin из Wine lib
|
||||
for dll in d3d12.dll dxgi.dll d3d11.dll d3d10core.dll d3d12core.dll winemetal.dll; do
|
||||
if [[ -f "${SYS32}/${dll}" ]]; then
|
||||
mv -f "${SYS32}/${dll}" "${BAK}/${dll}" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export DYLD_LIBRARY_PATH="${WHISKY_LIBS}/Libraries/Wine/lib/external:${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
export WINED3DMETAL=1
|
||||
export WINEDEBUG=-all
|
||||
|
||||
"${WINE64}" reg delete 'HKCU\Software\Wine\DllOverrides' /v d3d11 /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKCU\Software\Wine\DllOverrides' /v d3d10core /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKCU\Software\Wine\DllOverrides' /v dxgi /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKCU\Software\Wine\DllOverrides' /v d3d12 /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKCU\Software\Wine\Direct3D' /v renderer /f >/dev/null 2>&1 || true
|
||||
|
||||
if [[ -f "${LS}" ]]; then
|
||||
perl -i -pe 's/^\t\tGraphicsApi = ".*"/\t\tGraphicsApi = "d3d12"/' "${LS}"
|
||||
else
|
||||
mkdir -p "$(dirname "${LS}")"
|
||||
cat >"${LS}" <<'EOF'
|
||||
return {
|
||||
Options = {
|
||||
GraphicsApi = "d3d12",
|
||||
VideoPreset = "Low",
|
||||
},
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -f "${BOTTLE_DIR}/Metadata.plist" ]]; then
|
||||
/usr/libexec/PlistBuddy -c 'Set :dxvkConfig:dxvk false' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c 'Set :performanceConfig:forceD3D11 false' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || \
|
||||
/usr/libexec/PlistBuddy -c 'Add :performanceConfig:forceD3D11 bool false' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c 'Set :graphicsConfig:backend d3dMetal' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c 'Set :metalConfig:sequoiaCompatMode true' "${BOTTLE_DIR}/Metadata.plist" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
touch "${BOTTLE_DIR}/.ja3_d3dmetal_prepared"
|
||||
# Ярлык для Whisky Run (тот же CWD + D3DMetal env из Metadata)
|
||||
cat >"${BOTTLE_DIR}/drive_c/GOG Games/Jagged Alliance 3/run-ja3.cmd" <<'EOF'
|
||||
@echo off
|
||||
cd /d "C:\GOG Games\Jagged Alliance 3"
|
||||
start "" "JA3.exe" -unpacked %*
|
||||
EOF
|
||||
|
||||
echo "OK: D3DMetal graphics для ${BOTTLE_NAME}"
|
||||
echo " Скрипт: bash ${GOG_DIR}/run-ja3.sh"
|
||||
echo " Whisky Run: C:\\GOG Games\\Jagged Alliance 3\\run-ja3.cmd (не JA3.exe напрямую)"
|
||||
52
scripts/games/gog/purge-steam-from-bottle.sh
Executable file
52
scripts/games/gog/purge-steam-from-bottle.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Удалить клиент Steam из GOG-bottle (если попал). JA3/GOG — без Steam.app.
|
||||
# Не трогает steam_api64.dll в папке игры (GOG Galaxy wrapper, не Steam client).
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
STEAM_DIRS=(
|
||||
"${BOTTLE_DIR}/drive_c/Program Files (x86)/Steam"
|
||||
"${BOTTLE_DIR}/drive_c/Program Files/Steam"
|
||||
"${BOTTLE_DIR}/drive_c/Steam"
|
||||
)
|
||||
|
||||
main() {
|
||||
echo "=== Purge Steam из bottle ${BOTTLE_NAME} ==="
|
||||
|
||||
pkill -9 -f "${BOTTLE_ID}.*[Ss]team" 2>/dev/null || true
|
||||
pkill -9 -f "${BOTTLE_DIR}.*steam\.exe" 2>/dev/null || true
|
||||
|
||||
local d removed=0
|
||||
for d in "${STEAM_DIRS[@]}"; do
|
||||
if [[ -d "${d}" ]]; then
|
||||
echo " удаляю: ${d}"
|
||||
rm -rf "${d}"
|
||||
removed=1
|
||||
fi
|
||||
done
|
||||
|
||||
find "${BOTTLE_DIR}/drive_c" -maxdepth 4 \( -iname 'steam.exe' -o -iname 'steamservice.exe' \) 2>/dev/null | while read -r f; do
|
||||
echo " удаляю: ${f}"
|
||||
rm -f "${f}"
|
||||
removed=1
|
||||
done
|
||||
|
||||
export WINEPREFIX="${BOTTLE_DIR}"
|
||||
export DYLD_LIBRARY_PATH="${WINE_LIB}:${DYLD_LIBRARY_PATH:-}"
|
||||
if [[ -x "${WINE64}" ]]; then
|
||||
"${WINE64}" reg delete 'HKCU\Software\Valve' /f >/dev/null 2>&1 || true
|
||||
"${WINE64}" reg delete 'HKLM\Software\Valve' /f >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [[ "${removed}" -eq 0 ]]; then
|
||||
echo "OK: каталога Steam в bottle не было."
|
||||
else
|
||||
echo "OK: Steam client удалён из bottle."
|
||||
fi
|
||||
echo ""
|
||||
echo "Примечание: JA3.exe использует steam_api64.dll + Galaxy.dll (GOG), это не Steam.app."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
20
scripts/games/gog/register-galaxy-registry.sh
Executable file
20
scripts/games/gog/register-galaxy-registry.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Реестр после innoextract — web-installer видит установленный Galaxy.
|
||||
set -euo pipefail
|
||||
GOG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
# shellcheck source=config.sh
|
||||
source "${GOG_DIR}/config.sh"
|
||||
|
||||
GOG_PF='C:\Program Files (x86)\GOG Galaxy'
|
||||
CLIENT="${GOG_PF}\\GalaxyClient.exe"
|
||||
SERVICE="${GOG_PF}\\GalaxyClientService.exe"
|
||||
|
||||
gog_wine_check
|
||||
export_wine_env_gog
|
||||
|
||||
"${WINE64}" reg add 'HKLM\Software\WOW6432Node\GOG.com\GalaxyClient' /v version /t REG_SZ /d '2.0.100.1' /f
|
||||
"${WINE64}" reg add 'HKLM\Software\WOW6432Node\GOG.com\GalaxyClient\paths' /v GalaxyClientExePath /t REG_SZ /d "${CLIENT}" /f
|
||||
"${WINE64}" reg add 'HKLM\Software\WOW6432Node\GOG.com\GalaxyClient\paths' /v GalaxyClientServiceExePath /t REG_SZ /d "${SERVICE}" /f
|
||||
"${WINE64}" reg add 'HKCU\Software\GOG.com\Galaxy' /f 2>/dev/null || true
|
||||
|
||||
echo "OK: GalaxyClient paths → ${CLIENT}"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user