這篇單獨講 times_check,這是我在 2013 年為 sanc 導入的
新系統,它的最基本意義是減少心跳物件的使用量:
傳統: 每一個需做心跳判斷的物件都需要 set_heart_beat(1)
現在: 統一只由一個物件做 set_heart_beat(1),再讓它去呼
叫其它物件的 heart_beat 或其它函數
(然後出發點都是要減少 call_out 的使用量)
我在 tmi2_v3_改 所寫的 MYHOMD_D→every myhome 也是應用
相同的概念,因 myhome 不只一個附屬系統所以讓 MYHOME_D
去負責呼叫事宜。
times_check 的控制資料分為兩部份
mapping times_check=([]); // 要存進 .o 的資料
static mapping times_no_save=([]); // 不存進 .o 的資料以 static 宣告
例如以 /std/boat.c 定期航班式交通工具為例,它會同時使
用到這兩部份的資料:
times_check : 儲存經過多久再回 call 航班設定的函數
times_no_save : 暫存航行資訊,由航班設定函數所產生:
n1 秒後執行哪些東西
n2 秒後執行哪些東西
.
.
> call times_check;check_names;"/d/area/test/boat"
times_check 區:
({ "/d/area/test/boat", "/d/area/test/boat", ({ "start_plane" }) }) 193 秒後.
經過 193 秒後回 call 航班設定函數並帶 "start_plane" 參數過去
times_check_no_save 區:
底下就是由航班設定函數所產生的航班暫存資料,分別標示了幾秒後
會帶什麼參數過去,/std/boat.c 就是靠這些參數來判斷這時候應該
做什麼事:
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_0_2" }) }) 13 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_0_3" }) }) 33 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_0_4" }) }) 53 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_0_5" }) }) 73 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_1_1" }) }) 93 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_1_2" }) }) 113 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_1_3" }) }) 133 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_1_4" }) }) 153 秒後.
({ "/d/area/test/boat", "/d/area/test/boat", ({ "plane_1_5" }) }) 173 秒後.
像上面那樣子排序過後就很清楚,它就類似行事曆,時間到了就做
什麼事這樣。
這樣子設計的好處就是,「暫存」的資料是可以由「儲存」的資料
產生,比方船隻正在航行中我們卻 update times_check.c 的話,
「最起碼的」再經過多少秒之後它一定會再回 call 一次航班設定
函數。這裡帶出一個概念就是暫存的資料是可以丟棄的,反之不可
丟棄的就不要設定為暫存資料。
然後每一個想藉由 times_check 做週期控制的物件,必定有底下的
片段:
> more /std/boat.c
static object times_check;
int times_check(string names,string files,mixed tmps)
{
// 繼承用樣本本身不需執行
if(names==BOAT) return 1;
// times_check 載入不成功的話絕對不執行
if(!times_check)
if(catch(times_check=find_object_or_load(TIMES_CHECK_OB)))
return 1;
然後假設我們設計 n 秒後會回 call 自己,則必定有底下片斷:
// 先移掉舊的資料(這是保險起見,其實不見得要做)
times_check->remove_names(names);
// 再設定新的資料
times_check->set_times_check(names,files,({"start_plane"}),n秒);
至於 set_times_no_save 則支援兩種呼叫方式:
// 一次只設定單筆
times_check->set_times_check(names,files,({"start_plane"}),秒數);
// 一次設定多筆
times_check->set_times_check(names,files,({
({ ({要傳過去的參數群a}), 秒數a }),
({ ({要傳過去的參數群b}), 秒數b }),
.
.
}),
);
例如底下 /std/boat.c 的設定範例
tmps+=({ ({ ({"plane_"+i+"_1"}),t }) }); // 停靠, 此時 t 為幾秒後停靠
t=t+(int)planes[i][2]; // 累加時間
tmps+=({ ({ ({"plane_"+i+"_2"}),t}) }); // 廣播, 此時 t 為幾秒後廣播
t=t+(int)planes[i][2]; // 再累加時間
.
.
然後我們就可以想像 times_check 可能儲存了底下的資料
"時刻1": 要做什麼事
"時刻2": 要做什麼事
.
.
(時刻以字串型式儲存)
則它的心跳函數架構就不難想像:
keys_times=keys(times_check);
t = time();
// 當現在時刻已超過或等於某一紀錄的時刻時就做 something
foreach(tmp in keys_times)
{
if(t>=atoi(tmp))
{
do_check(times_check[tmp]);
// 做完就把該時刻紀錄 delete 掉
map_delete(times_check,tmp);
}
}
目前在 sanc 除了船隻之外,書店的定期閱讀也是用 times_check:
static object times_check;
int times_check(string names,string files,mixed tmps)
{
int s,sk;
string str;
object ppl;
if(!times_check)
if(catch(times_check=find_object_or_load("/open/cmds/times_check")))
return 1;
if(!ppl=find_player(names)) return 1;
if(!interactive(ppl)) return 1;
str=base_name(environment(ppl));
if(str!=__DIR__+"room1" && str!=__DIR__+"room2" &&
str!=shop_files)
{
tell_object(ppl,HIC"* 你離開了書店,書店小姐將你租閱\的書收走囉。"NOR"\n");
return 1;
}
s=member_array(tmps[0],books);
sk=(int)ppl->query("skill/"+BOOKS[s][1]);
if(sk>=BOOKS[s][2][1])
{
tell_object(ppl,HIC"* 你已經不需要再在書店讀《"+tmps[0]+"》囉!"NOR"\n");
return 1;
}
tell_object(ppl,"你閱\讀了《"+tmps[0]+"》裡頭艱深的文字,得到了一些心得...\n");
ppl->improve_skill(BOOKS[s][1],(sk+(int)ppl->query("stat/int"))/10);
// 再設定回 call 資料
if(sk/10 > 300)
// 玩家 書店檔 要傳過去的參數群 秒數
times_check->set_times_check(names,shop_files,({tmps[0],sk}),sk/50);
else
times_check->set_times_check(names,shop_files,({tmps[0],sk}),sk/10);
return 1;
}
上面可發現它是使用了 names 及 shop_files 做為組合搭配,
這意思是說不同的 names、相同的 shop_files 是被允許的,
例如多人於書店閱讀的情況:
書店內閱室
這裡是設於書店一角的開放式內閱室,除了提供巫師們租借
書報雜誌在裡頭閱讀,也有簡易的餐飲服務,舒適的大沙發
更是讓你一看到就想窩著不動。
明顯出口有: north 和 south.
(聖潔光芒) 希望(28957)正站在這裡。
這是一本書(ThisIsABook)正站在這裡。
(聖潔光芒) Celedia(Celedia:進階法師)正站在這裡。
(聖潔光芒) Kasim(Kasim:進階法師)正站在這裡。
而船隻因為沒有 names,但是因為每一艘船的 base_name 都是
獨立的,所以就讓 names = files = base_name(船隻)。
當然,相同的 names,不同的 files 亦是被允許的。
times_check 可支援的其它常見系統舉例:
料理製作、武防鑄造、鍊金煉藥、閉關修練內功、...
這篇會存在 tmi2_v3_改 的 /d/area/times_check使用說明.txt
Laechan