# Compatibilidade de protocolo — Web × OpenMU > **Atualização 2026-10-03 (D15/D16):** o projeto usa **só o cliente web**, e o web passou a falar o > **protocolo estendido** do OpenMU (o mesmo do cliente open source): versão `season6x` do nosso build, > login `20404`, ConnectServer **44406**, GameServers **55902/55904/55906**. Implementação: patch 0003 em > `services/web-client/patches/`. As seções abaixo comparam os dois protocolos (até 2026-10-02 > o web usava o clássico). ## 0. O que o cliente web faz no modo estendido (patch 0003) | Assunto | Implementação | |---|---| | Despacho | variantes `…Extended` vencem; as gêmeas clássicas (mesmo código + subcódigo) saem da tabela de despacho (`createSocket.ts`); patch 0006 também tira a clássica de outro código (`MoneyDropped` × `MoneyDroppedExtended` C1 2F) e as 0.75/0.95 que têm versão mais nova | | Itens | `ItemSerializerExtended` (5–15 bytes, número de 12 bits); listas percorridas pelo tamanho real (`common/itemLists.ts`): inventário, `StoreItemList` (loja/baú/Chaos Machine), itens no chão, loja pessoal | | Aparência | 27 bytes (`AppearanceSerializerExtended`) na lista de personagens e no campo de visão — grupo + número reais de armas, armaduras, wings e pets; bits do Fenrir mascarados | | Tratadores novos | ObjectWalked, CharacterList, PlayerShopItemList, PlayerShopBuyResult, ItemConsumptionFailed, MasterStatsUpdate, OpenLetter, QuestProgress, QuestState (+ quests resetam com CharacterInformationExtended) | | Cliente → servidor | `ItemMoveRequestExtended` (7 bytes, obrigatório: o servidor troca o tratador de 0x24) e `LoginLongPassword` (senha até 20) | | Correção | EXP master não soma mais na barra de nível normal (`ExperienceGainedExtended.Type`) | | Patch 0005 | `AddTransformedCharacterToScopeExtended` (C2 45, definição copiada do XML do servidor); lista de comandos do servidor (`ChatCommandListRequest` C1 F5 00 → `AvailableChatCommand` C2 F5 01) no autocompletar do chat; gerador com `LongLittleEndian` (`RespawnAfterDeathExtended.Experience`) | > Levantado em 2026-10-02 sobre OpenMU `204c11b5` e OpenMu-Client-Babylon `faa54d71`. Itens marcados > **(medido)** foram verificados por nós contra o servidor real; o resto vem de leitura de código/documentação > upstream. O cliente desktop foi removido do projeto (D15); a comparação abaixo fica para explicar por que o > web usa o protocolo estendido. ## 1. Resumo | | Estendido (usado pelo web) | Clássico (sem uso) | |---|---|---| | Transporte do web | WebSocket → proxy WS↔TCP (Bun) → TCP | (idem; era o modo do web até 2026-10-02) | | ConnectServer | **44406** ("Open Source Client") | **44405** ("GMO Client", o do cliente original) | | Versão enviada no login | `20404` (= Season **106**, Ep 3 no OpenMU) | `10404` (Season 6 E3 ENG 1.04d) | | Serial | `k1Pk2jcET48mxL3b` | `k1Pk2jcET48mxL3b` | | GameServer do servidor 0 | **55902** (medido) | **55901** (medido) | | Pacote de login | `LoginLongPassword` (senha até 20) | `LoginShortPassword` (**senha até 10**) | | Variante de pacotes | `*Extended` (32-bit LE para dano/HP/stats) | clássico S6E3 | | Criptografia C→S (GS) | SimpleModulus + Xor32 | SimpleModulus + Xor32 | | Criptografia S→C (GS) | SimpleModulus (C3/C4) | SimpleModulus (C3/C4) | | Credenciais | Xor3 | Xor3 | | ConnectServer | sem criptografia | sem criptografia | Os dois dialetos chegam à **mesma instância de GameServer** (mesmo `ServerId`, mesmo mundo, mesmo banco). Cada GameServer do OpenMU abre **um endpoint por tipo de cliente**: ``` Game Server 0 ──┬── :55901 clássico (anunciado pelo CS 44405) — sem uso └── :55902 estendido / WEB (anunciado pelo CS 44406) Game Server 1 ──┬── :55903 / :55904 Game Server 2 ──┬── :55905 / :55906 ``` Medido no log do OpenMU (`GameServer "Server 0" ... registering with endpoint ...:55901` e `...:55902`) e pela resposta `ConnectionInfo` (`C1 16 F4 03 ... 5d da` = 55901 no CS 44405; `5e da` = 55902 no CS 44406). A view de cada jogador é serializada pelo plugin de view correspondente à versão do *seu* cliente (`[MinimumClient(106, 3, …)]` para o estendido). ## 2. Fluxo de conexão (web) ``` Browser ──WS──▶ ws.DOMAIN/?host=openmu&port=44406 ──proxy──TCP──▶ CS :44406 ◀── IP anunciado (127.127.127.127, fixo), porta 55902 Browser ──WS──▶ ws.DOMAIN/?host=openmu&port=55902 ──proxy──TCP──▶ GS :55902 (LoginLongPassword, versão 20404) ``` O IP anunciado é ignorado pelo Babylon quando o `csHost` é um nome interno (política `gsAddress: 'auto'` → usa `csHost` + porta anunciada; `?gs=cs` força). Dentro de container o ConnectServer **sempre** anuncia o IP configurado (`RESOLVE_IP` no compose) (`ServerInfoRequestHandler`: o atalho "mesmo host" é desligado quando `DOTNET_RUNNING_IN_CONTAINER=true`). Como nenhuma porta do jogo é publicada, o valor é fixo e sem uso: o **web** reconecta pelo nome `openmu` dentro da rede docker. O proxy só disca o que está em `ALLOW_TARGETS` (padrão: `openmu:44406,55902,55904,55906,55980`). ## 3. Diferenças de pacotes O OpenMU nunca altera um pacote original; para o cliente open source ele adiciona variantes `…Extended` (`src/Network/Packets/ServerToClient/ServerToClientPackets.xml`). Plugins de view marcados com `[MinimumClient(106,3,…)]` as enviam. | Assunto | Clássico (sem uso) | Estendido (usado pelo web) | |---|---|---| | Dano (`ObjectHit`, C1 0x11) | 16-bit | `ObjectHitExtended`: HealthDamage/ShieldDamage 32-bit LE + flags | | HP/SD/Mana/AG atuais/máx (C1 0x26 FF/FE) | 16-bit | `CurrentStatsExtended`/`MaximumStatsExtended` 32-bit LE | | EXP ganha (0x16) | 16-bit (`ExperienceGained`) | `ExperienceGainedExtended` 32-bit (+dano) | | EXP total do personagem | 64-bit BE | 64-bit BE (igual) | | Stats base / master | 16-bit | `BaseStatsExtended`, `MasterStatsUpdateExtended` | | Lista de personagens | `CharacterList` | `CharacterListExtended` | | Appearance (viewport) | `AddCharactersToScope` + appearance 18 bytes | `AddCharacterToScopeExtended`, `AppearanceChangedExtended` (estrutura de itens explícita) | | Itens (serialização) | `ItemSerializer` clássico (S6: 12 bytes) | `ItemSerializerExtended` | | Mover item | `ItemMoveRequest` | `ItemMoveRequestExtended` | | Loja pessoal, quest, cartas, respawn | clássicos | `PlayerShopItemListExtended`, `QuestStateExtended`, `OpenLetterExtended`, `RespawnAfterDeathExtended` | Implicações (motivo da D16): - **Dano/HP acima de 65535**: o clássico satura/trunca; o estendido exibe corretamente. Importa para o conteúdo custom de alto nível (Custom 1+). - **IDs de item/wing**: a serialização clássica S6 limita grupo (4 bits = 16 grupos) e número (9 bits = 512 por grupo, combinando bits). A estendida tem mais espaço (número de 12 bits) — ver [custom-content.md](custom-content.md#wings). ## 4. Encryption Mesmas chaves padrão do OpenMU nos dois dialetos (`versions/season6/index.ts` no Babylon): - **ConnectServer**: texto puro. - **GameServer**: C→S SimpleModulus (*DefaultClientKey*) + Xor32; S→C SimpleModulus para C3/C4. - Login: usuário/senha com Xor3 (`FC CF AB`). - Hoje `OpenSourceClientNetworkEncryptionFactoryPlugIn` (106,3) espelha a de S6E3; se o upstream divergir as chaves, o proxy **não** precisa mudar (ele não decifra o fluxo do jogo para repassar), mas o *tracker* do proxy decifra para análise e precisaria acompanhar. ## 5. Limitações conhecidas | # | Limitação | Impacto | Status | |---|---|---|---| | P1 | ~~Web limita senha a 10~~ | — | ✅ resolvido: `LoginLongPassword` no modo estendido (senha até 20) | | P2 | ~~Web não usa pacotes `*Extended`~~ | — | ✅ resolvido: protocolo estendido (patch 0003) | | P3 | Todo jogador web chega ao OpenMU com o **IP do proxy** | limite `MaxConnectionsPerAddress` (30, por CS — agora o CS **44406**) e bans por IP afetam todos os jogadores web juntos; logs sem IP real | ✅ mitigado (D20): limite por IP real no Traefik (`WS_MAX_CONN_PER_IP`), CS 44406 com teto global 1000 (`set-connection-limit.sh`); bans por IP do OpenMU continuam inúteis para o web; IP real só nos logs do Traefik | | P4 | Clientes "errados" no CS: hoje funcionam com warnings | — | aviso upstream: deixará de funcionar se as chaves divergirem | | P5 | Login automático web (SSO) não existe; o protocolo só aceita usuário+senha | — | ver seção 6 | ## 6. Login único (site → jogo) — investigação Hoje o único caminho de autenticação no GameServer é `LoginShortPassword/LoginLongPassword` com credenciais Xor3. Alternativas avaliadas: 1. **Token no lugar da senha** (portal gera token temporário de ≤10 chars, válido 60 s, uso único; um plugin de login no OpenMU aceita senha **ou** token). Não muda o protocolo nem o cliente web. Requer verificar se o OpenMU expõe um ponto de extensão no login (hoje a checagem de senha está em `LoginAction`/`LoginServer`; provavelmente exige um plugin novo upstream ou pequena extensão). **Preferido.** 2. Proxy injeta credenciais — rejeitado: o proxy passaria a conhecer senhas e a decifrar o fluxo. 3. Pacote custom de "ticket" — exige alterar o servidor e o cliente web; só se (1) for inviável. Nada disso foi implementado (decisão: não comprometer a segurança por conveniência agora). Está no ROADMAP (M11). ## 7. Funcionalidades por cliente Ver a matriz em [compatibility-matrix.md](compatibility-matrix.md). ## 8. Pacotes MU Custom (código `0xFC`) Camada própria sobre o protocolo estendido (`custom/server/src/MuCustom.Systems`, roteados por `MuCustomPacketHandlerPlugIn`). Pedido do cliente: `C1 FC sub ...` (ou `C2 FC sub ...` para envios grandes); resposta: `C2 FC (sub|0x80) JSON` (ou bytes brutos nas imagens). Só valem no protocolo estendido. | Pedido | Resposta | O que é | Doc | |---|---|---|---| | 01 / 02 | 81 / 82 | painel de quests disponíveis / teleporte até o NPC | D25 | | 04 | 84 | diálogo e ação (1 aceitar, 2 concluir) das quests de NPC; o servidor também envia o 84 ao falar com o NPC | `docs/quests-npc.md` | | 05 | 85 | dados do mapa detalhado (NPCs, monstros, áreas) | `docs/mapa-detalhado.md` | | 08 / 09 / 0A / 0C | 88 / 89 / 8A | dungeons: estado, abrir sala, aceitar convite, sair; 89 convite, 8A painel da partida | `docs/dungeons.md` | | 10 / 11 | 90 | Gremory Case: lista / resgatar | D25 | | 20 / 21 | A0 | quests diárias e registro de caça / resgatar | D25 | | 30 – 32 | B0 | banco de joias | D25 | | 40 / 41 | C0 | Ruud e loja | D25 | | 50 – 52 | D0, D2 | foto de perfil: enviar, remover, buscar por nome | D28 | | 53 – 55 | D3, D4 | foto da guilda: enviar (mestre), buscar por nome, a minha | D28 | | 60 – 62 | E0 | brincos: estado, equipar, tirar | D29 | | 70 | F0 (F1 aviso) | login diário | D32 |