Este repositório contém a base para um servidor de Priston Tale, resultado de um esforço de refatoração de um código-fonte legado. O objetivo principal é transformar a base de código original, escrita em um estilo C mais antigo, em uma aplicação C++ moderna, robusta, segura e de fácil manutenção.
O foco não é apenas fazer o servidor funcionar, mas construí-lo sobre uma fundação de boas práticas de engenharia de software e Padrões de Design (Design Patterns).
⚠️ Status do Projeto: Em Desenvolvimento Ativo. A base da arquitetura, incluindo a camada de rede assíncrona, criptografia, configuração e o sistema de banco de dados, está implementada e funcional. A implementação dos handlers de pacotes e da lógica de jogo está em andamento.
A reestruturação deste projeto segue três pilares principais:
- Modularidade e Desacoplamento: Isolar as responsabilidades em serviços independentes (
AccountService,LogService, etc.) para que as alterações em uma parte do sistema não afetem outras. - Segurança e Robustez: Utilizar recursos modernos do C++ (Smart Pointers, RAII,
enum class) para eliminar classes inteiras de bugs, como vazamentos de memória, corrupção de dados e falhas inesperadas. - Performance e Escalabilidade: Construir a camada de rede com Boost.Asio, utilizando um modelo assíncrono e multithread para lidar com um grande número de conexões simultâneas de forma eficiente.
A nova arquitetura foi construída utilizando vários Padrões de Design fundamentais para garantir um código limpo e escalável:
O padrão mais importante da nossa arquitetura. Em vez de serviços criarem suas próprias dependências (o que gera forte acoplamento), as dependências são "injetadas" de fora. A classe Application atua como nosso "Contêiner de Injeção de Dependência", criando todos os serviços e passando-os para os construtores dos serviços que deles precisam.
A lógica de manipulação de pacotes é implementada com o padrão Strategy. O PacketDispatcher é o "Contexto" que recebe um pacote. Ele não sabe o que fazer com o pacote, apenas consulta um mapa de "Estratégias" (IPacketHandler). Cada Opcode é mapeado a uma classe de handler concreta (LoginHandler, PingHandler, etc.), que contém a lógica específica para aquele pacote. Isso torna a adição de novos pacotes trivial, sem a necessidade de modificar switch-cases gigantes.
Um princípio fundamental do C++ moderno. Todos os recursos (memória, conexões de banco de dados, arquivos, threads) são gerenciados por objetos. A aquisição do recurso acontece no construtor do objeto, e a liberação acontece automaticamente no destrutor. Usamos isso extensivamente com:
std::unique_ptrpara gerenciar o ciclo de vida dos nossos serviços.std::lock_guardpara garantir quemutexessejam sempre liberados.- Nossa classe
PooledConnectionpara garantir que as conexões de banco de dados sejam sempre devolvidas ao pool.
Para evitar o alto custo de abrir e fechar conexões com o banco de dados a cada consulta, a classe DatabasePool mantém um conjunto de conexões prontas para uso. A lógica é thread-safe, utilizando std::mutex e std::condition_variable para que múltiplas threads possam "emprestar" e "devolver" conexões de forma segura e eficiente.
A camada de rede é construída com Boost.Asio, seguindo o padrão Proactor. As operações de I/O (leitura e escrita de rede) são iniciadas e não bloqueiam a execução. Quando uma operação é concluída, uma função de "callback" (um lambda) é chamada para processar o resultado. Isso permite que um pequeno número de threads gerencie milhares de conexões de clientes simultaneamente.
A solução no Visual Studio está dividida em:
Core(Biblioteca Estática -.lib): Contém toda a lógica, serviços, rede e classes do servidor. É o "motor" do projeto.GameServer(Executável -.exe): Contém apenas a funçãomain(). Sua única responsabilidade é criar uma instância da classeApplication(doCore) e executá-la.
- Visual Studio 2022 (com toolset para C++20).
- Biblioteca Boost (versão 1.76 ou superior).
- Clone este repositório.
- Compile as bibliotecas do Boost necessárias (
system,thread, etc.) executandobootstrap.bateb2.exena pasta do Boost. - Abra o arquivo
.slnno Visual Studio. - Configure as propriedades do projeto para apontar para os diretórios de
includeelibdo Boost. - Compile a solução.
- Estabelecer arquitetura base com Injeção de Dependência.
- Implementar camada de rede assíncrona com Boost.Asio.
- Implementar o sistema de criptografia de duas camadas.
- Implementar o Pool de Conexões de Banco de Dados.
- Implementar o fluxo de login completo.
- Implementar o handler de seleção de personagens.
- Refatorar e implementar os serviços
CharacterServiceeUserServicecom lógica de jogo. - Adicionar handlers para todos os outros pacotes do jogo.