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 kurulum

Deneyin

Örnek bir soru. Soruya uyan bütün hayvanları seçin, sonra İleri'ye basın.

Nasıl çalışır

  1. 1
    Oyun 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.

  2. 2
    Oyun 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.

  3. 3
    Captcha servisi→Oyun client'ı

    Servis bir resim, bir soru ve bir soru kimliği gönderir.

  4. 4
    Oyun client'ı→Captcha servisi

    Oyuncu uyan hayvanları seçer. Client tıklamaları soru kimliğiyle birlikte gönderir.

  5. 5
    Captcha 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.

  6. 6
    Oyun 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. 1

    Panelinizden indirdiğiniz thick_captcha.js dosyasını sayfanıza ekleyin.

    thick_captcha.js

    <script src="thick_captcha.js"></script>
  2. 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. 1

    Sistemi bu define ile açın. Satırı silerseniz sistem tamamen kapanır.

    common/service.h (CommonDefines.h)

    #define ENABLE_THICK_CAPTCHA
  2. 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. 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. 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. 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. 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. 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. 8

    Başka bir eylemi korumak için aynı kontrolü oraya ekleyin.

    #ifdef ENABLE_THICK_CAPTCHA
    	if (!CThickCaptchaManager::instance().CheckAction(ch))
    		return;
    #endif