<!-- BEGIN CHANGE: artefato de contexto ? updater manual dos clients White Antidote (sem GitHub Actions) -->

# Client updater ? contexto White Antidote

**Status:** plano aprovado (manual, sem GitHub Actions / sem repo de release)  
**Origem da decisão:** chat OTC (`P:\OT\OTC`) ? 2026-07-27  
**Workspace de implementação (portal + host):** `P:\OT\OTSERVER\pedro\otx2-html.code-workspace`  
**Workspace dos clients:** `P:\OT\OTC`

## Objetivo

Player baixa **1x**, instala, e nas aberturas seguintes recebe patch quando houver update ? **sem** pipeline GitHub.

Publicacao = copiar artefatos para o servidor (`html/Downloads`, `html/files-*`, APIs PHP).

**Manual operacional (installer + patch + APK):** [`docs/otc-custom-release-manual.md`](otc-custom-release-manual.md)  
**Skill Cursor:** `.cursor/skills/otc-custom-release/`

## Os 3 clients (escopo)

| # | Produto no portal | Artefato atual | Pasta fonte (OTC) | Estratégia de update |
|---|---|---|---|---|
| 1 | Windows (Elfbot NG) | `Downloads/WhiteAntidote.exe` (installer) | `P:\OT\OTC\WhiteAntidoteElfbotNG\` + `installer-whiteantidote.iit` | **Updater separado** (launcher) ? Tibia clássico **não** tem módulo OTC |
| 2 | Custom OTC | `Downloads/WhiteAntidoteCustom.exe` | `P:\OT\OTC\WhiteAntidoteCustomOTC\` / `otclient-meu\` + `installer-whiteantidotecustomotc20.iit` | **Updater nativo OTCv8** (`modules/updater`) |
| 3 | Android APK | `Downloads/whiteantidote-android.apk` | `otclient-meu` + `create_android_assets.ps1` ? `assets/data.zip` | **Updater nativo** para data/módulos; **APK binário** = republicar no `/Downloads` (ou Play Store) |

Download page hoje: `html/portal/src/app/download/page.tsx`  
Base: `https://ot.whiteantidote.com/Downloads`

---

## Decisão: sem GitHub Actions

- **Não** criar repo de release nem workflow para o player.
- Release = processo manual (ou script local) ? FTP/cópia para `html/`.
- GitHub Actions continua irrelevante para este fluxo (pode existir para outras coisas do portal).

---

## Arquitetura proposta no host (`html/`)

```
html/
  Downloads/                          # instaladores / APK full (1ª vez)
    WhiteAntidote.exe                # installer ElfbotNG (+ launcher updater)
    WhiteAntidoteCustom.exe          # installer OTC
    whiteantidote-android.apk

  files-otc/                          # árvore de patch do OTC (data.zip source)
    init.lua
    data/
    modules/
    layouts/
    mods/
    WhiteAntidoteCustom.exe         # binário opcional (GL/DX conforme platform)

  files-classic/                      # árvore / zips de patch do client ElfbotNG
    manifest.json                     # versão + lista de arquivos + hashes
    patches/
      1.0.1.zip                       # zip incremental ou full
    full/
      WhiteAntidote-classic-full.zip

  portal/api/
    updater.php                       # NOVO ? API OTCv8 (Custom + APK assets)
    classic-updater.php               # NOVO ? API do launcher ElfbotNG
```

URLs públicas alvo:

| Uso | URL |
|---|---|
| OTC / APK assets | `https://ot.whiteantidote.com/api/updater.php` |
| Files OTC | `https://ot.whiteantidote.com/files-otc/` |
| Classic launcher | `https://ot.whiteantidote.com/api/classic-updater.php` |
| Files classic | `https://ot.whiteantidote.com/files-classic/` |
| Full downloads | `https://ot.whiteantidote.com/Downloads/...` |

> Nota: portal API atual vive em `html/portal/api/` e em prod costuma ser exposta como `/api/`. Seguir o mesmo padrão dos outros PHP (`lib.php`, CORS, etc.).

---

## A) Custom OTC + APK (updater nativo OTCv8)

### Como o client funciona (já existe)

- Módulo: `modules/updater/updater.lua`
- Só roda se `Services.updater` estiver setado **e** `g_resources.isLoadedFromArchive()` (`data.zip`).
- POST JSON: `{ version, build, os, platform, args }`
- Resposta esperada:

```json
{
  "url": "https://ot.whiteantidote.com/files-otc",
  "files": {
    "/init.lua": "<crc32b>",
    "/data/...": "<crc32b>",
    "/modules/...": "<crc32b>"
  },
  "binary": {
    "file": "/otclient_dx.exe",
    "checksum": "<crc32b>"
  },
  "keepFiles": false
}
```

> Checksum = **CRC32** (`hash_file('crc32b')` no PHP), nao MD5. O client OTCv8 usa zlib CRC32 em `g_resources.filesChecksums` e no download HTTP.

- Client baixa **so** arquivos com CRC diferente, reconstrói `data.zip`, opcionalmente troca o exe, reinicia.

### Config atual (ainda aponta pro OTCv8 público)

Arquivos: `otclient-meu/init.lua` e `WhiteAntidoteCustomOTC/init.lua`

```lua
APP_VERSION = 1341
Services = {
  updater = "http://otclient.ovh/api/updater.php",  -- TROCAR
  ...
}
```

**Mudança necessária:**

```lua
updater = "https://ot.whiteantidote.com/api/updater.php",
```

(Bump `APP_VERSION` quando quiser sinalizar client muito velho.)

### `updater.php` (implementado - Fase 1)

Referência OTCv8 padrão (Kondra / OTLand) + White Antidote:

- Código: `portal/api/updater.php`
- Diretório local = `WA_HTML_ROOT/files-otc` (prod: `/home/ubuntu/portal/assets/files-otc`) ou `html/files-otc` no repo
- URL pública = `https://ot.whiteantidote.com/files-otc` (Alias Apache nos confs de deploy)
- Cache de checksums ~45s em `portal/cache/updater_otc_checksums.json`
- Platform -> binario: `WIN32-WGL` -> `otclient_dx.exe`, `WIN32-EGL` -> `otclient_gl.exe`; Android: sem `binary`
- Inclui no mapa: `init.lua`, `data/`, `modules/`, `layouts/`, `mods/`
- `data/things`: omitido por default (client preserva); com `args.version` inclui só aquela versão + `keepFiles=true`
- README de publicação: `files-otc/README.md`

**Status Fase 1 (portal):** API + pasta + Alias confs.
**Status Fase 1 (OTC):** `Services.updater` aponta para WA; script `P:\OT\OTC\tools\publish-otc-release.ps1`.
Ainda falta: 1o espelho em `files-otc/` no host + teste com `data.zip` antigo.

### Packaging OTC (obrigatório para o updater ligar)

1. Build do exe (GL/DX).
2. Gerar **`data.zip`** contendo `init.lua`, `data`, `modules`, `layouts`, `mods` (mesmo espírito do `create_android_assets.ps1`).
3. Distribuição release = pasta com `WhiteAntidoteCustom.exe` + `data.zip` (não pasta `data/` solta).
4. Espelhar a **mesma árvore descompactada** em `html/files-otc/` no servidor.
5. Installer InstallAware só na 1ª instalação; patches = updater.

### Packaging APK

1. `create_android_assets.ps1` ? `assets/data.zip`.
2. Build APK ? publicar em `Downloads/whiteantidote-android.apk` quando o **binário** mudar.
3. Com updater apontando para a API WA: mudanças só em Lua/data atualizam via patch **sem** novo APK (desde que o client rode em archive mode).
4. Troca de engine/NDK/permissions = novo APK full.

### Rotina manual de release OTC/APK (sem GitHub)

1. Alterar fontes em `P:\OT\OTC\otclient-meu` (ou CustomOTC).
2. Regenerar `data.zip` / espelhar em `html/files-otc/`.
3. Se mudou exe: copiar binário para `files-otc/` e, se for 1ª instalação, atualizar installer em `Downloads/`.
4. Se mudou APK engine: substituir `Downloads/whiteantidote-android.apk`.
5. (Opcional) incrementar `APP_VERSION` no `init.lua` e republicar no `files-otc`.
6. Testar: client com `data.zip` antigo deve baixar só diffs.

---

## B) Elfbot NG ? updater **separado** (launcher)

### Por que separado

`WhiteAntidoteElfbotNG` é Tibia 8.60 clássico (`Tibia.exe`, `.dat`, `.spr`, `.pic`). **Não** há `updater.lua`.  
O installer atual empacota o client completo (`installer-whiteantidote.iit` ? `WhiteAntidote.exe`).

### Desenho mínimo do launcher

Nome sugerido: `WAUpdater.exe` (ou `WhiteAntidoteLauncher.exe`), instalado **junto** do client clássico.

Fluxo:

1. Player abre o atalho ? sobe o **launcher**, não o `Tibia.exe` direto.
2. Launcher GET/POST `classic-updater.php`.
3. Compara `localVersion` (arquivo `version.txt` na pasta do client) com `remoteVersion`.
4. Se igual ? inicia `Tibia.exe` e encerra.
5. Se diferente ? baixa zip(s) de `files-classic/`, extrai por cima da pasta de instalação (exceto configs do elfbot se marcados), atualiza `version.txt`, inicia `Tibia.exe`.

### Contrato sugerido ? `classic-updater.php`

Request (exemplo):

```json
{ "product": "whiteantidote-classic", "version": "1.0.0", "os": "windows" }
```

Response:

```json
{
  "version": "1.0.1",
  "mandatory": true,
  "notes": "Sprites + IP",
  "url": "https://ot.whiteantidote.com/files-classic/patches/1.0.1.zip",
  "sha256": "...",
  "size": 12345678,
  "launch": "Tibia.exe"
}
```

Se já atualizado:

```json
{ "version": "1.0.0", "upToDate": true, "launch": "Tibia.exe" }
```

### Conteúdo típico do patch zip (classic)

- `Tibia.dat` / `Tibia.spr` / `Tibia.pic` (quando mudarem)
- `Tibia.exe` (raro)
- **Não** sobrescrever configs do usuário/elfbot sem necessidade (excluir `*.cfg` de usuário no zip, ou lista `preserve` no manifest)

### Manifest local

Na pasta instalada:

```
version.txt          # ex: 1.0.0
WAUpdater.exe
Tibia.exe
Tibia.dat
Tibia.spr
Tibia.pic
...
```

Atalho do Menu Iniciar / Desktop aponta para `WAUpdater.exe`.

### Implementação do launcher (escolha simples)

Preferência: **um exe pequeno** (C# / Go / Rust) ? sem depender de PowerShell do usuário.

Escopo v1:

- UI mínima (barra de progresso + erro)
- HTTPS download + SHA256
- Extrair zip para o diretório do launcher
- Restart / run `Tibia.exe`
- Sem auto-update do próprio launcher na v1 (se precisar, incluir `WAUpdater.exe` no zip e reiniciar)

Alternativa ainda mais simples (MVP): script + `curl` embutido ? evitar se players tiverem AV agressivo; preferir exe único.

### Packaging / installer ElfbotNG

1. Incluir `WAUpdater.exe` + `version.txt` no InstallAware (`installer-whiteantidote.iit`).
2. Atalho ? launcher.
3. Continuar publicando `Downloads/WhiteAntidote.exe` como instalador **full** (novos players / reinstall).
4. Patches vão só para `files-classic/` + bump no `classic-updater.php` / `manifest.json`.

### Rotina manual de release classic

1. Alterar assets em `WhiteAntidoteElfbotNG\`.
2. Montar zip do patch com só arquivos mudados (ou full se preferir simplicidade no v1).
3. Subir zip em `html/files-classic/patches/<versão>.zip`.
4. Atualizar resposta do `classic-updater.php` (versão + sha256 + url).
5. Atualizar installer full em `Downloads/` quando a base mudar muito (opcional, mas recomendado periodicamente).

---

## Ordem de implementação sugerida

### Fase 1 - OTC (maior ganho, API ja conhecida) - **portal + init.lua feitos**

1. ~~Criar `portal/api/updater.php` + pasta `html/files-otc/`.~~
2. ~~Apontar `Services.updater` nos `init.lua` (workspace OTC).~~
3. Empacotar release com `data.zip` via `P:\OT\OTC\tools\publish-otc-release.ps1`; espelhar arvore em `files-otc/`; testar diff de 1 arquivo.
4. Espelhar processo no APK (assets).
5. Aplicar Alias Apache `/files-otc` no host + criar `/home/ubuntu/portal/assets/files-otc`.

### Fase 2 ? Classic launcher

1. Criar `portal/api/classic-updater.php` + `html/files-classic/`.
2. Implementar `WAUpdater.exe` mínimo.
3. Integrar no installer ElfbotNG + atalho.
4. Testar patch de `.spr`/`.dat`.

### Fase 3 ? Portal UX (opcional)

- Textos na `/download` explicando ?atualiza sozinho após instalar?.
- Link de ?reinstalação completa? vs ?já tenho o client?.

---

## O que **não** fazer neste plano

- Repo GitHub / Actions para publicar patch ao player.
- Depender de `otclient.ovh` em produção.
- Esperar que pasta `data/` solta (dev) rode o updater OTC.
- Usar o updater OTC no client ElfbotNG.

---

## Como prosseguir neste workspace (instruções ao agente / humano)

Abrir no Cursor:

`P:\OT\OTSERVER\pedro\otx2-html.code-workspace`

Prompt sugerido (copiar):

```
Leia html/docs/client-updater-context.md e implemente a Fase 1:
- portal/api/updater.php (contrato OTCv8)
- pasta html/files-otc/ (estrutura + README de como publicar)
- não use GitHub Actions
Depois descreva o checklist manual de release.
Não implemente ainda o launcher ElfbotNG (Fase 2) a menos que eu peça.
```

Para a Fase 2 (classic), no **mesmo** workspace portal + um projeto pequeno do launcher (pode viver em `P:\OT\OTC\tools\wa-classic-updater\` ou pasta dedicada):

```
Leia html/docs/client-updater-context.md seção B e implemente:
- classic-updater.php + files-classic/
- especificação + esqueleto do WAUpdater.exe
Clients fonte: P:\OT\OTC\WhiteAntidoteElfbotNG
```

No workspace OTC, em paralelo quando a API existir:

```
Aponte Services.updater para https://ot.whiteantidote.com/api/updater.php
em otclient-meu/init.lua e WhiteAntidoteCustomOTC/init.lua
e documente/gere script local de data.zip para release (sem GitHub).
```

**Feito:** updater URL + `tools/publish-otc-release.ps1`. Proximo: `-MirrorFilesOtc` + sync no host.

---

## Referências rápidas de paths

| Item | Path |
|---|---|
| Workspace portal | `P:\OT\OTSERVER\pedro\otx2-html.code-workspace` |
| Portal API | `P:\OT\OTSERVER\pedro\html\portal\api\` |
| Downloads prod | `P:\OT\OTSERVER\pedro\html\Downloads\` |
| Download UI | `P:\OT\OTSERVER\pedro\html\portal\src\app\download\page.tsx` |
| OTC source | `P:\OT\OTC\otclient-meu\` |
| OTC dist | `P:\OT\OTC\WhiteAntidoteCustomOTC\` |
| Classic dist | `P:\OT\OTC\WhiteAntidoteElfbotNG\` |
| Updater módulo | `...\modules\updater\updater.lua` |
| Android zip script | `otclient-meu\create_android_assets.ps1` |
| Installers | `P:\OT\OTC\installer-whiteantidote*.iit` |

<!-- END CHANGE -->
