Oyunlar için bot koruması
Oyuncular birkaç resme tıklar, botlar takılır. Token'ları oyun sunucunuz kendisi imzalar ve doğrular; kuralları değiştirdiğinizde sunucunuzda hiçbir şey değişmez.
Görünüm ve kurulumDeneyin
Örnek bir soru. Soruya uyan bütün hayvanları seçin, sonra İleri'ye basın.
Nasıl çalışır
- 1Oyun sunucusu→Oyun client'ı
Oyuncu saldırır ya da balık tutar. Sunucu işlemi durdurur ve client'a sizin gizli anahtarınızla imzalı bir token verir: hangi oyuncu, ne zaman ve tek kullanımlık bir kod.
- 2Oyun client'ı→Captcha servisi
Client bu token'ı servise gönderip bir soru ister. Servis imzayı kontrol eder, böylece sadece sizin sunucunuzdan gelen isteklere soru verilir.
- 3Captcha servisi→Oyun client'ı
Servis bir resim, bir soru ve bir soru kimliği gönderir.
- 4Oyun client'ı→Captcha servisi
Oyuncu uyan hayvanları seçer. Client tıklamaları soru kimliğiyle birlikte gönderir.
- 5Captcha servisi→Oyun client'ı
Doğruysa servis, sizin gizli anahtarınızla imzalı ve aynı tek kullanımlık kodu taşıyan bir geçiş token'ı gönderir. Yanlışsa oyuncu birkaç saniye bekler, client aynı token'la tekrar soru ister.
- 6Oyun client'ı→Oyun sunucusu
Client geçiş token'ını sunucuya gönderir. Sunucu imzayı gizli anahtarınızla, tek kullanımlık kodu da kendi verdiğiyle karşılaştırır ve işleme izin verir. Sunucu servise hiç bağlanmaz.
Panelden yönetilir
- Sunucunuzu güncellemeden bot kontrolünü açıp kapatın
- Seviye aralığına göre kaç challenge geçilmesi gerektiğini ayarlayın
- Belirli bir seviyenin üstünde kontrolü kapatın
- Sorular oyuncunun dilinde, varsayılan İngilizce
Metin2 örnek kurulum
Kaynakta zaten bulunan Crypto++ ve rapidjson kullanılır, ek kütüphane gerekmez.
Dosyaları panelinizden indirin
Bu sayfadaki dosyalar şablondur. Paneldeki kopyalarda API adresiniz, site kimliğiniz ve gizli anahtarınız hazır yazılıdır; her sunucu kendi dosyalarını alır. Sunucu dosyalarını kimseyle paylaşmayın: İçindeki gizli anahtara sahip olan herkes sunucunuz için geçerli captcha token'ı üretebilir. Client dosyalarında sadece API adresi bulunur.
Client (arayüz)
- 1
Panelinizden indirdiğiniz thick_captcha.js dosyasını sayfanıza ekleyin.
thick_captcha.js
<script src="thick_captcha.js"></script> - 2
Oyun sunucunuzdan gelen token ile açın, geçiş token'ını sunucuya geri gönderin.
<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>
Oyun sunucusu
- 1
Sistemi bu define ile açın. Satırı silerseniz sistem tamamen kapanır.
common/service.h (CommonDefines.h)
#define ENABLE_THICK_CAPTCHA - 2
Panelinizden indirdiğiniz thick_captcha.h ve thick_captcha.cpp dosyalarını game/src içine kopyalayın, .cpp dosyasını Makefile'a ekleyin.
game/src/Makefile
CPPFILE = ... thick_captcha.cpp - 3
main.cpp içinde yöneticiyi oluşturun.
game/src/main.cpp
#ifdef ENABLE_THICK_CAPTCHA #include "thick_captcha.h" #endif #ifdef ENABLE_THICK_CAPTCHA CThickCaptchaManager thick_captcha_manager; #endif - 4
CONFIG_PATH değerini tüm core'lar aynı dosyayı gösterecek şekilde ayarlayın. GM komutları ayarları bu dosyaya kaydeder, her core dosyayı dakikada bir yeniden okur; böylece bir kanalda yapılan değişiklik tüm kanallara ulaşır ve yeniden başlatmada kaybolmaz. Dosya ilk açılışta otomatik oluşturulur.
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
Girişten sonraki ilk canavar/metin saldırısında ve ilk balık tutmada, sonra oyuncunun seviyesine ait süre dolunca (varsayılan 30 dakika, thick_captcha_interval ile ayarlanır) tekrar sorulur. Oyunculara yapılan saldırılarda (savaş, lonca savaşı, PvP) ve boşta duran oyunculara hiç sorulmaz.
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
Oyuncu çıkış yapınca kayıtları temizleyin. Bekleyen bir captcha, oyuncu tekrar girdikten sonraki ilk saldırıda veya balık tutmada yeniden gönderilir.
game/src/char.cpp
// CHARACTER::Disconnect #ifdef ENABLE_THICK_CAPTCHA CThickCaptchaManager::instance().OnLogout(this); #endif - 7
Oyuncu komutlarını ve GM komutlarını ekleyin. thick_captcha_ask test captcha'sı gönderir, thick_captcha_toggle sistemi açıp kapatır, thick_captcha_difficulty 1-10 zorluğu ayarlar, thick_captcha_interval <seviye> <dakika> o seviyeden itibaren ne sıklıkla sorulacağını ayarlar (0 = hiç sorulmaz, del <seviye> kuralı siler, parametresiz yazılınca kuralları listeler). Değişiklikler config dosyasına kaydedilir ve bir dakika içinde tüm kanallara ulaşır.
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
Başka bir eylemi korumak için aynı kontrolü oraya ekleyin.
#ifdef ENABLE_THICK_CAPTCHA if (!CThickCaptchaManager::instance().CheckAction(ch)) return; #endif