代碼: 選擇全部
// 2017 05 07 by Natsu, using Google recaptcha to identify bots
// 流程說明
// mob的init觸發第一個web連線 把資料傳給web端
// web端紀錄用戶是否通過驗證
// mob定期從web取得全部資料 確認玩家是否通過驗證
#include <login.h>
#include <ansi.h>
#include <net/socket.h>
#include <socket_err.h>
inherit NPC;
// test_time 是驗證的時間,超過時間沒完成驗證就抓去關
int test_time = 300;
// 提供給玩家的基本url,後面會加上一次性token
string url = "http://wf.twkang.net/robot/";
void create()
{
set_name("冥府判官",({"hell judge", "judge"}));
set("invis",1);
set("gender", "男性" );
set("age", 4321);
setup();
}
void init()
{
object me=this_player();
::init();
if(!query_heart_beat(this_object()))
set_heart_beat(1);
this_player()->start_busy(1);
// 呼叫judge(),進行第一次web連線傳送玩家資料過去
call_out("judge", 1, me);
// 下面是開始檢查的部分
if(!this_object()->query_temp("callout")){
this_object()->set_temp("callout",1);
call_out("check", 10);
}
}
// 用來建立一次性token的function
string rand_string(int length)
{
string output = "", words = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
int i, tmp;
for (i=0; i<length; i++) {
tmp = random(strlen(words));
output += words[tmp..tmp];
}
return output;
}
// 建立連線最主要的function
// read_callback: 處理socket response
// write_callback: 處理socket request
// sendData: 要在request時送過去web的資料
int do_request(string read_callback, string write_callback, string sendData) {
int err, m_socket;
// 建立socket並將resource id 存在m_socket
m_socket = socket_create(STREAM, read_callback,"socket_close");
if ( m_socket < 0 ){
log_file("www",sprintf("[%s]無法初始化 Socket .\n", change_time_scale(time()) ));
return ;
}
// 將資料放在obj的tempdata之後才能取用
this_object()->set_temp("d_"+m_socket, sendData);
// 實際建立連線的部分
// m_socket會被傳到read_callback,write_callback
err = socket_connect(m_socket,"127.0.0.1 80",read_callback,write_callback );
if( err!=EESUCCESS ) {
CHANNEL_D->do_channel(this_object(),"sys",sprintf("[%s]啟動 Socket 失敗.\n",
change_time_scale(time()) ));
}
}
// 告訴玩家他還有多久可以進行驗證
void tell_url(object user, int remain_time)
{
string output = "\n" + HIR + "請開啟以下連結,並完成驗證。\n";
output += "剩餘驗證時間:";
if(remain_time>=60) {
output += chinese_number(remain_time/60) + "分";
}
if(remain_time % 60 !=0 || remain_time == 0) {
output += chinese_number(remain_time%60) + "秒";
} else if(remain_time>=60) {
output += "鐘";
}
output += "。\n\n" + HIW + url + user->query_temp("answer/token") + NOR + "\n";
tell_object(user, output);
}
// 定期從web取回資料
void check()
{
object *inv;
int a;
do_request("check_response", "check_request", "");
remove_call_out("check");
// 只要環境內還有user就每10秒執行一次
inv = all_inventory(environment(this_object()));
for (a = 0; a < sizeof(inv); a++) {
if (userp(inv[a])) {
call_out("check", 10);
return;
}
}
this_object()->delete_temp("callout");
}
// 開始呼叫的部分
// 傳過去web的格式是 "token,id,time,ip,passed(0 or 1),\n";
void judge(object player)
{
int answer_time;
string token, id, sendData;
answer_time = time();
id = player->query("id");
token = rand_string(12);
player->set_temp("answer/token", token);
player->set_temp("answer/time", answer_time);
sendData = token + "," + id + "," + answer_time + "," + query_ip_number(player) + ",0,\n";
do_request("start_response", "start_request", sendData);
tell_url(player, test_time);
}
void start_response(int fd, mixed message)
{
// no need to get status
}
void start_request(int fd)
{
string sendData = this_object()->query_temp("d_"+fd);
socket_write(fd,
sprintf("GET /robotStart?data=%s HTTP/1.1\nHOST: wf.twkang.net\n\n", sendData));
this_object()->delete_temp("d_"+fd);
return ;
}
// 取得清單確認玩家是否通過驗證
void check_response(int fd, mixed message)
{
string data, reading, *tmp, ot;
int a, remain_time, line_num;
object *inv;
inv = all_inventory(environment(this_object()));
for (a = 0; a < sizeof(inv); a++) {
// 是玩家而且還在進行驗證中
if (userp(inv[a]) && inv[a]->query_temp("answer/token")) {
// 確認剩餘時間
remain_time = test_time - (time() - inv[a]->query_temp("answer/time"));
if (remain_time >= 0) {
// do check
// 取回資料是csv格式
// 欄位與送過去的相同
// 這邊先拆每筆資料
foreach(reading in explode(message,"\n"))
{
if(sscanf(reading, "data: %s", data)==1){
if (strlen(data)<3) {
continue;
}
// 再取得每個欄位
tmp = explode(data, ",");
// 如果token相同且已經通過驗證
if(tmp[0] == inv[a]->query_temp("answer/token") && tmp[4] != "0") {
if(tmp[4]=="1"){
// same ip
inv[a]->set_temp("answer/right", 1);
log_file("ROBOT", sprintf(HIW"[%s] %s(%s) passed with same ip\n"NOR,
change_time_scale(time()), inv[a]->name(1),inv[a]->query("id")));
} else if (tmp[4]=="2"){
// different ip
inv[a]->set_temp("answer/right", 1);
log_file("ROBOT", sprintf(HIY"[%s] %s(%s) passed with different ip\n"NOR,
change_time_scale(time()), inv[a]->name(1),inv[a]->query("id")));
}
command("say 「"HIC+ inv[a]->name() +HIY"」通過了驗證,請按<free>離開\n"NOR);
inv[a]->delete_temp("answer/token");
inv[a]->delete_temp("answer/time");
}
}
}
// 如果時間還沒到而且沒通過驗證
// 就繼續告知剩餘時間
if(inv[a]->query_temp("answer/token")) {
tell_url(inv[a], remain_time);
}
// 超過驗證時間沒回應 掰掰
} else {
// bye~~
command("say 「"HIM+ inv[a]->name() +HIY"」,你反應太慢了喔!進牢房蹲吧!!\n"NOR);
inv[a]->delete_temp("accuse");
inv[a]->delete_temp("answer/token");
inv[a]->delete_temp("answer/time");
inv[a]->receive_damage("kee", 1, this_object());
inv[a]->move("/d/auso/room2");
inv[a]->set("startroom","/d/auso/room2");
ot = (string)inv[a]->query("title");
inv[a]->set("old_title",ot);
inv[a]->set("title",HIW BLK"死囚"NOR);
inv[a]->set("bot_jail",1);
inv[a]->save();
tell_object(users(), HIR"該死的"+inv[a]->name(1)+"因掛機器人經審判查證屬實被關入死牢囉!\n"NOR);
log_file("ROBOT", sprintf(HIM"[%s] %s(%s) jailed because no answer\n"NOR,
change_time_scale(time()), inv[a]->name(1),inv[a]->query("id")));
}
}
}
}
void check_request(int fd)
{
socket_write(fd,
sprintf("GET /robotCheck HTTP/1.1\nHOST: wf.twkang.net\n\n"));
return ;
}