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. Burada web widget'ının masaüstü ve mobil tarayıcıdaki hali görünüyor.
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.
Kontrol sizde
- Captcha'yı oyun içinden GM komutuyla açıp kapatın, sunucuyu yeniden başlatmadan
- Seviyeye göre ne sıklıkla sorulacağını ayarlayın ya da belirli bir seviyeden sonra hiç sormayın
- Panelden sunucu başına kullanımı görün: son 1 saat ve 24 saatteki sorular, geçen ve kalanlar, bekleyen oyuncular
- 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