O SA-MP Rich Presence é um ASI para SA-MP (San Andreas Multiplayer) que atualiza automaticamente seu status do Discord com informações detalhadas do servidor. O ASI é injetado no jogo quando você se conecta a um servidor SA-MP, exibindo informações do servidor.
- Deutsch: README
- English: README
- Español: README
- Français: README
- Italiano: README
- Polski: README
- Русский: README
- Svenska: README
- Türkçe: README
- SA-MP Rich Presence
- Idiomas
- Índice
- O que é um ASI?
- Funcionalidades
- Requisitos
- Instalação
- Arquitetura Técnica Detalhada
- Integração com a SPC
- Para Desenvolvedores de Servidor
- Estrutura do Código
- Tratamento de Erros
- Sistema de Processamento de Linha de Comando
- Protocolo de Comunicação SA-MP
- Benefícios do SA-MP Rich Presence
- Notas Técnicas Adicionais
- Licença
ASI é uma biblioteca dinâmica (.dll) renomeada com a extensão .asi que é automaticamente carregada por um loader ASI quando o GTA San Andreas é iniciado. Os plugins ASI permitem modificar ou adicionar funcionalidades ao jogo sem alterar os arquivos originais, sendo uma das formas mais populares de modding para o GTA San Andreas e outros jogos da série GTA. Com um ASI loader instalado, você pode facilmente instalar ou remover modificações simplesmente adicionando ou removendo arquivos .asi da pasta do jogo.
O Discord Rich Presence é uma funcionalidade que permite que aplicativos mostrem informações detalhadas sobre o que o usuário está fazendo diretamente no perfil do Discord. No caso do SA-MP Rich Presence, as seguintes informações são exibidas:
- Nome do servidor (Hostname).
- Nome do jogador.
- Número atual de jogadores e máximo de jogadores.
- IP e porta do servidor.
- Imagem personalizada do servidor (requer que o servidor tenha o plugin SPC Integration).
- Tempo de jogo.
- Status da conexão.
- Cliente SA-MP (se não tiver: clients-samp).
- Aplicativo Discord Desktop.
- Sistema Operacional Windows.
- Visual C++ Runtime.
- ASI Loader instalado no GTA San Andreas.
- Baixe a versão mais recente do ASI compilado em releases.
- Certifique-se de ter um ASI Loader instalado no seu GTA San Andreas.
- Copie o arquivo .asi baixado para a pasta do seu GTA San Andreas.
- Inicie o jogo com o SA-MP.
- O ASI será automaticamente inicializado quando você entrar em um servidor.
O arquivo main.cpp é o ponto de entrada do ASI e responsável por:
-
Inicialização do sistema:
BOOL APIENTRY DllMain(HMODULE module_handle, DWORD reason, LPVOID) { switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(module_handle); CreateThread(nullptr, 0, Update_Thread, nullptr, 0, nullptr); break; // ... } return TRUE; }
-
Gerenciamento da thread de atualização:
DWORD WINAPI Update_Thread(LPVOID) { // Inicializa WSA para operações de rede WSAData wsa_data; if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) return 1; // Processa parâmetros de linha de comando std::string server_ip, player_name; int server_port; // Inicializa gerenciadores Global_Server_Manager = std::make_unique<Server_Manager>(server_ip, server_port, player_name); Global_Discord_Manager = std::make_unique<Discord_Manager>(); // Loop principal de atualização while (true) { Global_Server_Manager->Update(); Global_Discord_Manager->Update_Presence(...); Sleep(Constants::PRESENCE_UPDATE_INTERVAL); } }
O Discord_Manager é responsável por toda a integração com o Discord Rich Presence:
-
Inicialização do Discord RPC:
void Discord_Manager::Initialize() noexcept { Sleep(5000); // Aguarda inicialização do Discord DiscordEventHandlers discord_handlers{}; Discord_Initialize("DISCORD_APP_ID", &discord_handlers, 1, NULL); // "DISCORD_APP_ID > Application ID" start_timestamp = time(nullptr); // Marca o início da sessão }
-
Atualização da presença:
void Discord_Manager::Update_Presence(const Server_Information& server_info, std::string_view player_name, std::string_view current_image, bool is_connected) noexcept { DiscordRichPresence discord_presence{}; // Configura informações básicas discord_presence.state = player_name.data(); discord_presence.details = server_info.Hostname.c_str(); discord_presence.largeImageKey = current_image.data(); // Adiciona informações do servidor discord_presence.largeImageText = server_info.Server_Address.c_str(); discord_presence.startTimestamp = start_timestamp; discord_presence.partySize = server_info.Players; discord_presence.partyMax = server_info.Max_Players; Discord_UpdatePresence(&discord_presence); }
O Server_Manager coordena todas as operações relacionadas ao servidor:
-
Gerenciamento de estado:
Server_Manager::Server_Manager(const std::string& ip, int port, const std::string& name) : connection_status(false) , server_ip(ip) , server_port(port) , player_name(name) , current_image_url(Constants::DEFAULT_IMAGE) {}
-
Atualização de informações:
bool Server_Manager::Update() { bool query_success = server_query.Query(server_data); if (query_success) { connection_status = true; server_data.Server_Address = Get_Server_Address(); // Tenta obter imagem personalizada do servidor std::string new_image_url = url_manager.Get_Image_URL(server_data.Server_Address); if (!new_image_url.empty()) current_image_url = new_image_url; } // ... }
O Network_Manager implementa toda a comunicação de rede:
- Inicialização do socket:
bool Network_Manager::Initialize(std::string_view ip, int port) noexcept { network_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Configura timeout timeval Socket_Timeout{ 0, Constants::QUERY_TIMEOUT }; setsockopt(network_socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&Socket_Timeout), sizeof(Socket_Timeout)); // Configura endereço do servidor server_address.sin_family = AF_INET; server_address.sin_port = htons(port); inet_pton(AF_INET, ip.data(), &server_address.sin_addr); return true; }
O Server_Query implementa o protocolo de consulta do SA-MP:
-
Montagem do pacote de consulta:
bool Server_Query::Assemble_Query_Packet(char* packet) noexcept { memcpy(packet, QUERY_SIGNATURE, 4); // "SAMP" packet[10] = 'i'; // Information query return true; }
-
Processamento da resposta:
bool Server_Query::Parse_Response(char* response_buffer, int received_bytes, Server_Information& server_data) noexcept { // Extrai informações como: // - Número de jogadores // - Máximo de jogadores // - Hostname }
SPC Integration é um plugin desenvolvido para SA-MP (San Andreas Multiplayer) e open.mp (Open Multiplayer) que atua como uma ponte entre servidores e o ecossistema da SPC. O plugin coleta informações cruciais do servidor, incluindo:
- IP e porta.
- URL do site do servidor.
- URL da imagem personalizada.
- Informações de configuração.
Essas informações são enviadas e armazenadas de forma segura no armazenador de dados da SPC (SA-MP Programming Community), permitindo uma integração com várias aplicações da SPC, incluindo o Rich Presence.
- O servidor utiliza o plugin SPC Integration para registrar sua imagem personalizada.
- A imagem é armazenada no armazenamento de dados da SPC dentro do arquivo JSON do servidor.
- Quando um jogador conecta no servidor:
- O URL_Manager do ASI tenta buscar o arquivo JSON do servidor (formato:
ip-port.json
). - Se encontrado, extrai a URL da imagem do parâmetro
spc_integration_image
. - A imagem é então exibida no Discord Rich Presence do jogador.
- O URL_Manager do ASI tenta buscar o arquivo JSON do servidor (formato:
O URL_Manager é responsável por toda a comunicação com o armazenamento de dados da SPC:
-
Busca da imagem do servidor:
std::string URL_Manager::Get_Image_URL(const std::string& server_address) { std::string formatted_address = server_address; // Converte ":" para "-" no endereço size_t colon_pos = formatted_address.find(':'); if (colon_pos != std::string::npos) formatted_address.replace(colon_pos, 1, "-"); // Busca JSON do servidor std::string image_url; if (!Get_URL_From_File(formatted_address, image_url)) return Constants::DEFAULT_IMAGE; return image_url; }
-
Processamento do JSON:
std::string URL_Manager::Parse_JSON(const std::string& json_content) { const std::string key = "\"spc_integration_image\":"; size_t key_pos = json_content.find(key); // Extrai URL da imagem do JSON // ... return image_url; }
Se você é um desenvolvedor de servidor SA-MP, recomendamos fortemente a instalação do plugin SPC Integration pelos seguintes motivos:
-
Visibilidade:
- A imagem personalizada do seu servidor será exibida no Discord de todos os jogadores que usam este ASI.
- Aumenta o reconhecimento da marca do seu servidor.
- Proporciona uma apresentação mais profissional.
-
Benefícios:
- Maior engajamento dos jogadores.
- Diferenciação visual no Discord.
- Integração automática com o Rich Presence.
-
Como implementar:
- Leia o repositório do plugin SPC Integration para saber como implementar.
O projeto é organizado em vários componentes:
src/
├── discord/ # Biblioteca Discord RPC
├── command_line_parser.* # Processamento de argumentos
├── constants.h # Constantes globais
├── discord_manager.* # Integração com Discord
├── main.cpp # Ponto de entrada
├── network_manager.* # Operações de rede
├── server_manager.* # Gerenciamento do servidor
├── server_query.* # Protocolo SA-MP
├── server_types.h # Estruturas de dados
└── url_manager.* # Integração com SPC
O arquivo define várias constantes importantes:
namespace Constants {
// Valores padrão
inline constexpr char DEFAULT_SERVER_IP[] = "127.0.0.1";
inline constexpr int DEFAULT_SERVER_PORT = 7777;
// Configurações de rede
inline constexpr int QUERY_TIMEOUT = 1000;
inline constexpr int QUERY_PACKET_SIZE = 11;
inline constexpr int QUERY_BUFFER_SIZE = 512;
// Configurações de retry
inline constexpr int MAX_RETRY_ATTEMPTS = 3;
// Intervalos
inline constexpr int PRESENCE_UPDATE_INTERVAL = 5000;
}
Define as estruturas principais:
struct Server_Information {
uint16_t Players{ 0 }; // Jogadores online
uint16_t Max_Players{ 0 }; // Máximo de jogadores
std::string Hostname; // Nome do servidor
std::string Server_Image; // URL da imagem
std::string Server_Address; // Endereço do servidor
};
O sistema implementa um robusto sistema de tratamento de erros:
-
Timeout de rede:
timeval Socket_Timeout{ 0, Constants::QUERY_TIMEOUT }; setsockopt(network_socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&Socket_Timeout), sizeof(Socket_Timeout));
-
Sistema de retry:
bool Server_Query::Try_Query(Server_Information& server_data, int retry_count) noexcept { for (int attempt = 0; attempt <= retry_count; attempt++) { if (network_manager.Send_Query(...)) return true; // Aguarda antes de tentar novamente std::this_thread::sleep_for(std::chrono::milliseconds(100 * (attempt + 1))); } return false; }
-
Fallback para imagem padrão:
std::string URL_Manager::Get_Image_URL(const std::string& server_address) { // Se algo der errado, retorna a imagem padrão if (!session_handle) return Constants::DEFAULT_IMAGE; std::string image_url; if (!Get_URL_From_File(formatted_address, image_url)) return Constants::DEFAULT_IMAGE; return image_url; }
-
Monitoramento de estado de conexão:
bool Server_Manager::Update() { auto current_time = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - last_successful_query); // Se ficar mais de 15 segundos sem resposta, considera desconectado if (duration.count() > 15) { connection_status = false; current_image_url = Constants::DEFAULT_IMAGE; } }
O ASI implementa um sistema flexível para processar argumentos de linha de comando:
-
Processamento principal:
bool Command_Line_Parser::Parse(const wchar_t* command_line, std::string& server_ip, int& server_port, std::string& player_name) { // Define valores padrão server_ip = Constants::DEFAULT_SERVER_IP; server_port = Constants::DEFAULT_SERVER_PORT; // Processa cada parâmetro std::wstring ip_parameter = Parse_Parameter(command_string, L"-h"); std::wstring port_parameter = Parse_Parameter(command_string, L"-p"); std::wstring name_parameter = Parse_Parameter(command_string, L"-n"); // Converte e valida os valores if (!ip_parameter.empty()) server_ip = Convert_To_String(ip_parameter); if (!port_parameter.empty()) server_port = std::stoi(port_parameter); if (!name_parameter.empty()) player_name = Convert_To_String(name_parameter); // Verifica se todos os parâmetros necessários estão presentes return !server_ip.empty() && server_port > 0 && !player_name.empty(); }
-
Extração de parâmetros:
std::wstring Command_Line_Parser::Parse_Parameter(const std::wstring& command_string, const wchar_t* parameter) { size_t position = command_string.find(parameter); if (position != std::wstring::npos) { position += wcslen(parameter) + 1; size_t end_position = command_string.find(L" ", position); if (end_position == std::wstring::npos) end_position = command_string.length(); return command_string.substr(position, end_position - position); } return L""; }
O ASI implementa o protocolo de consulta do SA-MP para obter informações do servidor:
-
Estrutura do pacote de consulta:
- 4 bytes: Assinatura "SAMP"
- 7 bytes: Dados adicionais
- Último byte: 'i' (indica consulta de informações)
-
Estrutura da resposta:
- Cabeçalho do pacote
- Players (uint16_t)
- Max_Players (uint16_t)
- Hostname (string)
bool Server_Query::Parse_Response(char* response_buffer, int received_bytes, Server_Information& server_data) noexcept {
char* current_pos = response_buffer + Constants::QUERY_PACKET_SIZE;
const char* buffer_end = response_buffer + received_bytes;
// Pular "password"
current_pos += sizeof(bool);
// Lê dados fixos
memcpy(&server_data.Players, current_pos, sizeof(server_data.Players));
current_pos += sizeof(server_data.Players);
memcpy(&server_data.Max_Players, current_pos, sizeof(server_data.Max_Players));
current_pos += sizeof(server_data.Max_Players);
// Lê Hostname e pula "gamemode" e "language"
auto read_string = [](char*& pos, const char* end, std::string& str) noexcept -> bool {
uint32_t length;
memcpy(&length, pos, sizeof(length));
pos += sizeof(length);
str.assign(pos, length);
pos += length;
return true;
};
if (!read_string(current_pos, buffer_end, server_data.Hostname))
return false;
// Pular "gamemode" e "language"
// ...
return true;
}
-
Para Jogadores:
- Visualização rápida do servidor atual.
- Informações detalhadas sobre o servidor.
- Integração perfeita com o Discord.
- Experiência de jogo mais social.
-
Para Servidores:
- Maior visibilidade através do Discord.
- Marketing orgânico através dos jogadores.
- Identidade visual personalizada (com o plugin SPC Integration).
- Estatísticas em tempo real.
-
Thread Safety:
- O ASI utiliza um único thread para atualizações.
- Gerenciadores são thread-safe por design.
- Recursos são propriamente sincronizados.
-
Uso de Memória:
- Alocação mínima de memória.
- Buffers pré-alocados para queries.
- Gerenciamento automático de recursos (RAII).
-
Performance:
- Queries otimizadas com timeout.
- Sistema de retry inteligente.
- Intervalo de atualização configurável.
-
Compatibilidade:
- Suporte a Unicode.
- Compatível com Windows moderno.
- Suporte a IPv4.
Este ASI está protegido sob a Licença Apache 2.0, que permite:
- ✔️ Uso comercial e privado
- ✔️ Modificação do código fonte
- ✔️ Distribuição do código
- ✔️ Concessão de patentes
- Manter o aviso de direitos autorais
- Documentar alterações significativas
- Incluir cópia da licença Apache 2.0
Para mais detalhes sobre a licença: http://www.apache.org/licenses/LICENSE-2.0
Copyright (c) Calasans - Todos os direitos reservados