SA-MP Rich Presence to ASI dla SA-MP (San Andreas Multiplayer), które automatycznie aktualizuje Twój status Discord ze szczegółowymi informacjami o serwerze. ASI jest wstrzykiwane do gry, gdy łączysz się z serwerem SA-MP, wyświetlając informacje o serwerze.
- Português: README
- Deutsch: README
- English: README
- Español: README
- Français: README
- Italiano: README
- Русский: README
- Svenska: README
- Türkçe: README
- SA-MP Rich Presence
- Języki
- Indeks
- Czym jest ASI?
- Funkcje
- Wymagania
- Instalacja
- Szczegółowa architektura techniczna
- Integracja z SPC
- Dla deweloperów serwerów
- Struktura kodu
- Obsługa błędów
- System przetwarzania wiersza poleceń
- Protokół komunikacji SA-MP
- Korzyści z SA-MP Rich Presence
- Dodatkowe informacje techniczne
- Licencja
ASI to biblioteka dynamiczna (.dll) przemianowana z rozszerzeniem .asi, która jest automatycznie ładowana przez loader ASI podczas uruchamiania GTA San Andreas. Wtyczki ASI pozwalają modyfikować lub dodawać funkcje do gry bez zmiany oryginalnych plików, będąc jedną z najpopularniejszych form moddingu dla GTA San Andreas i innych gier z serii GTA. Mając zainstalowany loader ASI, możesz łatwo instalować lub usuwać modyfikacje, po prostu dodając lub usuwając pliki .asi z folderu gry.
Discord Rich Presence to funkcja, która pozwala aplikacjom wyświetlać szczegółowe informacje o tym, co użytkownik robi, bezpośrednio w profilu Discord. W przypadku SA-MP Rich Presence wyświetlane są następujące informacje:
- Nazwa serwera (Hostname)
- Nazwa gracza
- Aktualna liczba graczy i maksymalna liczba graczy
- IP i port serwera
- Niestandardowy obraz serwera (wymaga, aby serwer miał wtyczkę SPC Integration)
- Czas gry
- Status połączenia
- Klient SA-MP (jeśli nie masz: clients-samp)
- Aplikacja Discord Desktop
- System operacyjny Windows
- Visual C++ Runtime
- ASI Loader zainstalowany w GTA San Andreas
- Pobierz najnowszą skompilowaną wersję ASI z releases
- Upewnij się, że masz zainstalowany ASI Loader w swoim GTA San Andreas
- Skopiuj pobrany plik .asi do folderu GTA San Andreas
- Uruchom grę z SA-MP
- ASI zostanie automatycznie zainicjowane po wejściu na serwer
Plik main.cpp jest punktem wejścia ASI i odpowiada za:
-
Inicjalizację systemu:
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; }
-
Zarządzanie wątkiem aktualizacji:
DWORD WINAPI Update_Thread(LPVOID) { // Inicjalizuje WSA dla operacji sieciowych WSAData wsa_data; if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) return 1; // Przetwarza parametry wiersza poleceń std::string server_ip, player_name; int server_port; // Inicjalizuje menedżerów Global_Server_Manager = std::make_unique<Server_Manager>(server_ip, server_port, player_name); Global_Discord_Manager = std::make_unique<Discord_Manager>(); // Główna pętla aktualizacji while (true) { Global_Server_Manager->Update(); Global_Discord_Manager->Update_Presence(...); Sleep(Constants::PRESENCE_UPDATE_INTERVAL); } }
Discord_Manager jest odpowiedzialny za całą integrację z Discord Rich Presence:
-
Inicjalizacja Discord RPC:
void Discord_Manager::Initialize() noexcept { Sleep(5000); // Czeka na inicjalizację Discord DiscordEventHandlers discord_handlers{}; Discord_Initialize("DISCORD_APP_ID", &discord_handlers, 1, NULL); // "DISCORD_APP_ID > Application ID" start_timestamp = time(nullptr); // Oznacza początek sesji }
-
Aktualizacja obecności:
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{}; // Konfiguruje podstawowe informacje discord_presence.state = player_name.data(); discord_presence.details = server_info.Hostname.c_str(); discord_presence.largeImageKey = current_image.data(); // Dodaje informacje o serwerze 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); }
Server_Manager koordynuje wszystkie operacje związane z serwerem:
-
Zarządzanie stanem:
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) {}
-
Aktualizacja informacji:
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(); // Próbuje pobrać niestandardowy obraz serwera 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; } // ... }
Network_Manager implementuje całą komunikację sieciową:
- Inicjalizacja gniazda:
bool Network_Manager::Initialize(std::string_view ip, int port) noexcept { network_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Konfiguruje timeout timeval Socket_Timeout{ 0, Constants::QUERY_TIMEOUT }; setsockopt(network_socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&Socket_Timeout), sizeof(Socket_Timeout)); // Konfiguruje adres serwera server_address.sin_family = AF_INET; server_address.sin_port = htons(port); inet_pton(AF_INET, ip.data(), &server_address.sin_addr); return true; }
Server_Query implementuje protokół zapytań SA-MP:
-
Montaż pakietu zapytania:
bool Server_Query::Assemble_Query_Packet(char* packet) noexcept { memcpy(packet, QUERY_SIGNATURE, 4); // "SAMP" packet[10] = 'i'; // Zapytanie o informacje return true; }
-
Przetwarzanie odpowiedzi:
bool Server_Query::Parse_Response(char* response_buffer, int received_bytes, Server_Information& server_data) noexcept { // Wyodrębnia informacje takie jak: // - Liczba graczy // - Maksymalna liczba graczy // - Nazwa hosta }
SPC Integration to wtyczka opracowana dla SA-MP (San Andreas Multiplayer) i open.mp (Open Multiplayer), która działa jako most między serwerami a ekosystemem SPC. Wtyczka zbiera kluczowe informacje o serwerze, w tym:
- IP i port
- URL strony serwera
- URL niestandardowego obrazu
- Informacje o konfiguracji
Te informacje są wysyłane i bezpiecznie przechowywane w magazynie danych SPC (SA-MP Programming Community), umożliwiając integrację z różnymi aplikacjami SPC, w tym Rich Presence.
- Serwer używa wtyczki SPC Integration do zarejestrowania swojego niestandardowego obrazu
- Obraz jest przechowywany w magazynie danych SPC w pliku JSON serwera
- Gdy gracz łączy się z serwerem:
- URL_Manager ASI próbuje pobrać plik JSON serwera (format:
ip-port.json
) - Jeśli zostanie znaleziony, wyodrębnia URL obrazu z parametru
spc_integration_image
- Obraz jest następnie wyświetlany w Discord Rich Presence gracza
- URL_Manager ASI próbuje pobrać plik JSON serwera (format:
URL_Manager jest odpowiedzialny za całą komunikację z magazynem danych SPC:
-
Pobieranie obrazu serwera:
std::string URL_Manager::Get_Image_URL(const std::string& server_address) { std::string formatted_address = server_address; // Konwertuje ":" na "-" w adresie size_t colon_pos = formatted_address.find(':'); if (colon_pos != std::string::npos) formatted_address.replace(colon_pos, 1, "-"); // Pobiera JSON serwera std::string image_url; if (!Get_URL_From_File(formatted_address, image_url)) return Constants::DEFAULT_IMAGE; return image_url; }
-
Przetwarzanie 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); // Wyodrębnia URL obrazu z JSON // ... return image_url; }
Jeśli jesteś deweloperem serwera SA-MP, zdecydowanie zalecamy zainstalowanie wtyczki SPC Integration z następujących powodów:
-
Widoczność:
- Niestandardowy obraz twojego serwera będzie wyświetlany w Discord wszystkich graczy korzystających z tego ASI
- Zwiększa rozpoznawalność marki twojego serwera
- Zapewnia bardziej profesjonalną prezentację
-
Korzyści:
- Większe zaangażowanie graczy
- Wizualne wyróżnienie w Discord
- Automatyczna integracja z Rich Presence
-
Jak wdrożyć:
- Przeczytaj repozytorium wtyczki SPC Integration, aby dowiedzieć się, jak wdrożyć
Projekt jest zorganizowany w kilka komponentów:
src/
├── discord/ # Biblioteka Discord RPC
├── command_line_parser.* # Przetwarzanie argumentów
├── constants.h # Stałe globalne
├── discord_manager.* # Integracja z Discord
├── main.cpp # Punkt wejścia
├── network_manager.* # Operacje sieciowe
├── server_manager.* # Zarządzanie serwerem
├── server_query.* # Protokół SA-MP
├── server_types.h # Struktury danych
└── url_manager.* # Integracja z SPC
Plik definiuje kilka ważnych stałych:
namespace Constants {
// Wartości domyślne
inline constexpr char DEFAULT_SERVER_IP[] = "127.0.0.1";
inline constexpr int DEFAULT_SERVER_PORT = 7777;
// Ustawienia sieciowe
inline constexpr int QUERY_TIMEOUT = 1000;
inline constexpr int QUERY_PACKET_SIZE = 11;
inline constexpr int QUERY_BUFFER_SIZE = 512;
// Ustawienia ponownych prób
inline constexpr int MAX_RETRY_ATTEMPTS = 3;
// Interwały
inline constexpr int PRESENCE_UPDATE_INTERVAL = 5000;
}
Definiuje główne struktury:
struct Server_Information {
uint16_t Players{ 0 }; // Gracze online
uint16_t Max_Players{ 0 }; // Maksymalna liczba graczy
std::string Hostname; // Nazwa serwera
std::string Server_Image; // URL obrazu
std::string Server_Address; // Adres serwera
};
System implementuje solidny system obsługi błędów:
-
Timeout sieci:
timeval Socket_Timeout{ 0, Constants::QUERY_TIMEOUT }; setsockopt(network_socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&Socket_Timeout), sizeof(Socket_Timeout));
-
System ponownych prób:
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; // Czeka przed ponowną próbą std::this_thread::sleep_for(std::chrono::milliseconds(100 * (attempt + 1))); } return false; }
-
Fallback do domyślnego obrazu:
std::string URL_Manager::Get_Image_URL(const std::string& server_address) { // Jeśli coś pójdzie nie tak, zwraca domyślny obraz 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; }
-
Monitorowanie stanu połączenia:
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); // Jeśli nie ma odpowiedzi przez ponad 15 sekund, uznaje się za rozłączone if (duration.count() > 15) { connection_status = false; current_image_url = Constants::DEFAULT_IMAGE; } }
ASI implementuje elastyczny system do przetwarzania argumentów wiersza poleceń:
-
Główne przetwarzanie:
bool Command_Line_Parser::Parse(const wchar_t* command_line, std::string& server_ip, int& server_port, std::string& player_name) { // Ustawia wartości domyślne server_ip = Constants::DEFAULT_SERVER_IP; server_port = Constants::DEFAULT_SERVER_PORT; // Przetwarza każdy parametr 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"); // Konwertuje i waliduje wartości 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); // Sprawdza, czy wszystkie wymagane parametry są obecne return !server_ip.empty() && server_port > 0 && !player_name.empty(); }
-
Ekstrakcja parametrów:
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""; }
ASI implementuje protokół zapytań SA-MP do uzyskiwania informacji o serwerze:
-
Struktura pakietu zapytania:
- 4 bajty: Sygnatura "SAMP"
- 7 bajtów: Dodatkowe dane
- Ostatni bajt: 'i' (wskazuje zapytanie o informacje)
-
Struktura odpowiedzi:
- Nagłówek pakietu
- 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;
// Pomija "password"
current_pos += sizeof(bool);
// Odczytuje stałe dane
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);
// Odczytuje Hostname i pomija "gamemode" i "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;
// Pomija "gamemode" i "language"
// ...
return true;
}
-
Dla graczy:
- Szybki podgląd aktualnego serwera
- Szczegółowe informacje o serwerze
- Doskonała integracja z Discord
- Bardziej społeczne doświadczenie z gry
-
Dla serwerów:
- Większa widoczność przez Discord
- Marketing organiczny przez graczy
- Spersonalizowana tożsamość wizualna (z wtyczką SPC Integration)
- Statystyki w czasie rzeczywistym
-
Bezpieczeństwo wątków:
- ASI używa pojedynczego wątku do aktualizacji
- Menedżerowie są z założenia bezpieczni dla wątków
- Zasoby są odpowiednio zsynchronizowane
-
Wykorzystanie pamięci:
- Minimalna alokacja pamięci
- Wstępnie zaalokowane bufory dla zapytań
- Automatyczne zarządzanie zasobami (RAII)
-
Wydajność:
- Zoptymalizowane zapytania z timeoutem
- Inteligentny system ponownych prób
- Konfigurowalny interwał aktualizacji
-
Kompatybilność:
- Wsparcie dla Unicode
- Kompatybilny z nowoczesnymi systemami Windows
- Wsparcie dla IPv4
Ten ASI jest chroniony Licencją Apache 2.0, która zezwala na:
- ✔️ Użytek komercyjny i prywatny
- ✔️ Modyfikację kodu źródłowego
- ✔️ Dystrybucję kodu
- ✔️ Udzielanie patentów
- Zachowanie informacji o prawach autorskich
- Dokumentowanie znaczących zmian
- Dołączenie kopii licencji Apache 2.0
Więcej szczegółów o licencji: http://www.apache.org/licenses/LICENSE-2.0
Copyright (c) Calasans - Wszelkie prawa zastrzeżone