Protecție anti-bot pentru jocuri
Jucătorii dau clic pe câteva imagini. Boții se blochează. Serverul tău de joc semnează și verifică singur fiecare token, așa că nu schimbi nimic pe server când ajustezi regulile.
Previzualizare și instalareÎncearcă
O întrebare de exemplu. Alege toate animalele potrivite, apoi apasă Înainte.
Cum funcționează
- 1Server de joc→Client de joc
Un jucător atacă sau pescuiește. Serverul oprește acțiunea și îi dă clientului un token semnat cu cheia ta secretă: ce jucător, când și un cod de unică folosință.
- 2Client de joc→Serviciu captcha
Clientul trimite acest token serviciului și cere o întrebare. Serviciul verifică semnătura, deci doar cererile de la serverul tău primesc întrebări.
- 3Serviciu captcha→Client de joc
Serviciul trimite o imagine, o întrebare și un id al întrebării.
- 4Client de joc→Serviciu captcha
Jucătorul alege animalele potrivite. Clientul trimite clicurile împreună cu id-ul întrebării.
- 5Serviciu captcha→Client de joc
Corect: serviciul trimite un token de trecere semnat cu cheia ta secretă, cu același cod de unică folosință. Greșit: jucătorul așteaptă câteva secunde, apoi clientul cere din nou cu același token.
- 6Client de joc→Server de joc
Clientul trimite tokenul de trecere serverului. Serverul verifică semnătura cu cheia ta secretă și că codul de unică folosință este cel emis de el, apoi permite acțiunea. Serverul nu contactează niciodată serviciul.
Controlat din panou
- Pornești sau oprești verificarea fără să actualizezi serverul
- Stabilești câte provocări trebuie trecute pe fiecare interval de nivel
- Dezactivezi verificarea peste un anumit nivel
- Întrebări în limba jucătorului, implicit în engleză
Exemplu de instalare Metin2
Folosește Crypto++ și rapidjson care există deja în sursă, deci nu e nevoie de altă bibliotecă.
Descarcă fișierele din panou
Fișierele de pe această pagină sunt șabloane. Copiile din panoul tău conțin deja adresa API, ID-ul site-ului și secretul, deci fiecare server primește propriile fișiere. Păstrează fișierele de server private: oricine are secretul din ele poate crea tokenuri captcha valide pentru serverul tău. Fișierele de client conțin doar adresa API.
Client (interfață)
- 1
Adaugă în pagină thick_captcha.js din panoul tău.
thick_captcha.js
<script src="thick_captcha.js"></script> - 2
Deschide-l cu tokenul de la serverul de joc și trimite înapoi tokenul de trecere.
<div id="captcha"></div> <script> ThickCaptcha.open(document.getElementById("captcha"), { token: REQUEST_TOKEN_FROM_GAME_SERVER, lang: navigator.language, onPass: function (passToken) { sendToGameServer("/thick_captcha " + passToken); }, onRenew: function () { return askGameServerForNewToken(); }, }); </script>
Server de joc
- 1
Pornește sistemul cu acest define. Fără linie, sistemul e oprit complet.
common/service.h (CommonDefines.h)
#define ENABLE_THICK_CAPTCHA - 2
Copiază thick_captcha.h și thick_captcha.cpp din panou în game/src și adaugă .cpp în Makefile.
game/src/Makefile
CPPFILE = ... thick_captcha.cpp - 3
Creează managerul în main.cpp.
game/src/main.cpp
#ifdef ENABLE_THICK_CAPTCHA #include "thick_captcha.h" #endif #ifdef ENABLE_THICK_CAPTCHA CThickCaptchaManager thick_captcha_manager; #endif - 4
Setează CONFIG_PATH astfel încât toate core-urile să indice același fișier. Comenzile GM își salvează setările acolo și fiecare core recitește fișierul o dată pe minut, așa că o schimbare făcută pe un canal ajunge pe toate canalele și rămâne după repornire. Fișierul este creat la prima pornire.
game/src/thick_captcha.cpp, thick_captcha.txt
const char* CONFIG_PATH = "../../thick_captcha.txt"; // thick_captcha.txt enabled 1 difficulty 1 interval 1 30 interval 75 60 interval 120 0 - 5
Se cere la primul atac asupra unui monstru sau metin și la primul pescuit după autentificare, apoi din nou după intervalul pentru nivelul jucătorului (implicit 30 de minute, setat cu thick_captcha_interval). Atacurile asupra jucătorilor (războaie, războaie de breaslă, PvP) și jucătorii inactivi nu declanșează niciodată întrebarea.
game/src/char_battle.cpp, char_skill.cpp, char.cpp
// CHARACTER::Attack, after battle_is_attackable #ifdef ENABLE_THICK_CAPTCHA if (IsPC() && !CThickCaptchaManager::instance().CheckAttack(this, pkVictim)) return false; #endif // CHARACTER::ComputeSkill, at the top #ifdef ENABLE_THICK_CAPTCHA if (IsPC() && pkVictim && !CThickCaptchaManager::instance().CheckAttack(this, pkVictim)) return BATTLE_NONE; #endif // CHARACTER::fishing, after the "if (m_pkFishingEvent) { fishing_take(); return; }" block #ifdef ENABLE_THICK_CAPTCHA if (!CThickCaptchaManager::instance().CheckAction(this)) return; #endif - 6
Curăță datele când jucătorul se deconectează. Un captcha în așteptare este trimis din nou la următorul atac sau pescuit după reconectare.
game/src/char.cpp
// CHARACTER::Disconnect #ifdef ENABLE_THICK_CAPTCHA CThickCaptchaManager::instance().OnLogout(this); #endif - 7
Adaugă comenzile pentru jucători și comenzile GM. thick_captcha_ask trimite un captcha de test, thick_captcha_toggle pornește sau oprește sistemul, thick_captcha_difficulty 1-10 setează dificultatea, thick_captcha_interval <nivel> <minute> stabilește cât de des sunt întrebați jucătorii de la acel nivel (0 = niciodată, del <nivel> șterge o regulă, fără argumente afișează regulile). Modificările se salvează în fișierul de configurare și ajung pe toate canalele într-un minut.
game/src/cmd.cpp, cmd_general.cpp
#ifdef ENABLE_THICK_CAPTCHA ACMD(do_thick_captcha); ACMD(do_thick_captcha_renew); ACMD(do_thick_captcha_ask); ACMD(do_thick_captcha_toggle); ACMD(do_thick_captcha_difficulty); ACMD(do_thick_captcha_interval); #endif #ifdef ENABLE_THICK_CAPTCHA { "thick_captcha", do_thick_captcha, 0, POS_DEAD, GM_PLAYER }, { "thick_captcha_renew", do_thick_captcha_renew, 0, POS_DEAD, GM_PLAYER }, { "thick_captcha_ask", do_thick_captcha_ask, 0, POS_DEAD, GM_LOW_WIZARD }, { "thick_captcha_toggle", do_thick_captcha_toggle, 0, POS_DEAD, GM_IMPLEMENTOR }, { "thick_captcha_difficulty", do_thick_captcha_difficulty, 0, POS_DEAD, GM_IMPLEMENTOR }, { "thick_captcha_interval", do_thick_captcha_interval, 0, POS_DEAD, GM_IMPLEMENTOR }, #endif #ifdef ENABLE_THICK_CAPTCHA ACMD(do_thick_captcha) { while (*argument == ' ') ++argument; if (!CThickCaptchaManager::instance().Verify(ch, argument)) ch->ChatPacket(CHAT_TYPE_COMMAND, "ThickCaptchaFailed"); } ACMD(do_thick_captcha_renew) { CThickCaptchaManager::instance().Renew(ch); } ACMD(do_thick_captcha_ask) { char arg1[256]; one_argument(argument, arg1, sizeof(arg1)); LPCHARACTER tch = *arg1 ? CHARACTER_MANAGER::instance().FindPC(arg1) : ch; if (tch) CThickCaptchaManager::instance().Request(tch); } ACMD(do_thick_captcha_toggle) { CThickCaptchaManager& mgr = CThickCaptchaManager::instance(); mgr.SetEnabled(!mgr.IsEnabled()); ch->ChatPacket(CHAT_TYPE_INFO, "Thick Captcha: %s", mgr.IsEnabled() ? "on" : "off"); } ACMD(do_thick_captcha_difficulty) { char arg1[256]; one_argument(argument, arg1, sizeof(arg1)); CThickCaptchaManager& mgr = CThickCaptchaManager::instance(); if (*arg1 && isdigit(*arg1)) mgr.SetDifficulty(atoi(arg1)); ch->ChatPacket(CHAT_TYPE_INFO, "Thick Captcha difficulty: %d (1-10)", mgr.GetDifficulty()); } ACMD(do_thick_captcha_interval) { char arg1[256], arg2[256]; two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); CThickCaptchaManager& mgr = CThickCaptchaManager::instance(); if (!strcmp(arg1, "del") && isdigit(*arg2)) mgr.RemoveInterval(atoi(arg2)); else if (isdigit(*arg1) && isdigit(*arg2)) mgr.SetInterval(atoi(arg1), atoi(arg2)); for (const auto& rule : mgr.GetIntervals()) { if (rule.second) ch->ChatPacket(CHAT_TYPE_INFO, "Thick Captcha level %d+: every %d min", rule.first, rule.second); else ch->ChatPacket(CHAT_TYPE_INFO, "Thick Captcha level %d+: off", rule.first); } } #endif - 8
Pentru a proteja altă acțiune, adaugă aceeași verificare acolo.
#ifdef ENABLE_THICK_CAPTCHA if (!CThickCaptchaManager::instance().CheckAction(ch)) return; #endif