精華區beta mud 關於我們 聯絡資訊
我現在覺得持續 coding 也不錯。 最近公司開了個會,有幾個地方看了有所感觸。 同一物件 另一物件 ┌───┬───┐ ┌───┐ │ 訊 │ 訊 │ │ 資 │ │ 息 → 息 │ │ 料 │ │ 產 │ 暫 │─→│ 寫 │ │ 生 → 存 │ │ 入 │ │ 源 │ 區 │ │ 區 │ └───┴───┘ └───┘ 例如說我們想 log 玩家的戰鬥資料,那麼當玩家「進入戰鬥」時, 訊息產生源就送出資料給訊息暫存區,請它「暫存一條玩家已進入 戰鬥的訊息」,例如進入戰鬥的時間啦、戰鬥發生的地點啦、戰鬥 的目標啦、....。 那麼可以想像在某一段時間區間內,暫存區多半會儲存兩條以上的 訊息。 然後當玩家「結束戰鬥」時,訊息產生源一樣送出資料給訊息暫存 區,例如結束戰鬥的時間、結束戰鬥的地點、玩家獲得的戰利品資 訊等,然後暫存區就將這個玩家從戰鬥開始到結束的一些該寫入的 訊息,匯集後送往資料寫入區去寫入。 但是,當玩家的戰鬥有不正常中止(非結束)的情況時,因為訊息產 生源一直沒有觸發到該玩家已結束戰鬥的資訊,它就不會通知暫存 區要將暫存的資料送往寫入區,當這種情況很頻繁地發生又沒有被 察覺時,就會造成暫存區的資料量增加,直到某一天「滿了」造成 明顯的問題狀況時才被發現,而這問題開始發生、到問題爆出來, 中間被認為有問題的戰鬥歷程就全部沒有被完整紀錄。 一、傳統上其實上述三個方塊多半被寫在同一個物件裡頭,那分開 的好處其實顯而易見,就是「分散工作量」,因為暫存訊息需 要經過處理後才暫存,寫入訊息同樣也需要經過處理後才寫入 ,當暫存很頻繁時,由另一物件負責資料寫入(及回存)似乎是 比較適當的做法。 mapping buff_data=([]); ┌────────────────┐ │ mapping save_data=([]); ├──┐ └────────────────┘ │ void create() │ { │ ::create(); │ seteuid(getuid(this_object())); │ ┌────────────────┐ │ │if(file_exists(SAVE_FILES+".o"))│ │ 也就是說這三個區塊可挪到 │ restore_object(SAVE_FILES); ├──┼─另一個物件 └────────────────┘ │ . │ . │ } │ │ ┌────────────────┐ │ │int save_room() │ │ │{ │ │ │ save_object(SAVE_FILES); ├──┘ │ return 1; │ │} │ └────────────────┘ 二、針對暫存的資料,應有一額外檢核的機制,比方說可以預估 玩家「不可能有n秒以上的戰鬥」(例如 n = 3600),那就可 設計讓物件每n秒對所有的暫存訊息做一次檢查,檢查項目   可包括 1.該玩家物件是否仍存在(比方有可能斷線或不正常離線) 2.該玩家所戰鬥的目標是否仍存在(比方可能物件error消失) 3.該玩家物件是否正在戰鬥中 4.該玩家物件戰鬥的對象是否就是暫存訊息所儲存的對象 5.該玩家是否仍在暫存訊息所紀錄的地方進行戰鬥 . . 當有其中一項不符合時馬上就可判定該暫存訊息已經不正常   ,此時就應該做適度的資料補正後送往寫入區,或是剔除該   暫存資料,並以適當的方式通知管理者處理。 更簡單的判斷方式還包括當暫存區是以玩家的 name 當做主   key 去暫存資料時,則當該玩家前一筆暫存資料仍存在於暫   存區、該玩家卻又觸發訊息產生源去產生一筆新的暫存資料   時,就可以做如下判斷 if(buff_data[ppl_name]) save_error("ERROR! "+ppl_name+": "+identify(buff_data[ppl_name])+"\n"); // 然後才做新的儲存 buff_data[ppl_name] = .... ; // 舊的 buff_data 會被新的覆蓋 這樣管理者事後至少有跡可尋,而且資料量也可預期不會肥   胖。 三、暫存資料方式的設定也很重要,以上面為例當暫存資料是以 ppl_name 為主 key 時,相較於以其它方式為主 key 的情   況、或是不使用 mapping 資料而改採陣列資料的串流儲存   方式時,是比較保險的做法。 當然判斷以 ppl_name 為主 key 的方式為可行的依據,就   是一個玩家不可能在同一時間觸發兩場戰鬥,但實際上卻是 可能的,比方戰鬥中「突然又出現新的戰鬥目標」,則考量   到這種情況,就得做適度的修改,例如.. buff_data[ppl_name] = ({ ({暫存一,}), ({暫存二,}), . . }); 這樣有新的戰鬥訊息就累加上去,有新的戰鬥結束訊息時   就從陣列裡面去找出符合的。 總之就是資料的儲存方式必須要做事前的妥善分析規劃。 四、暫存資料本身的檢核。假設我們有個函數叫做 buff_size   ,可用來檢查暫存資料本身的大小時,那就可以在每一次   的暫存資料輸入、或是每一次的暫存資料寫入時,就做底   下的判斷: switch(buff_size(buff_data)) { case 80: sent_alarm_1("buff_data 儲存量已達到 80%。"); break; case 90: sent_alarm_2("buff_data 儲存量已達到 90%。"); break; case 95: sent_alarm_3("buff_data 儲存量已達到 95%。"); break; } 但是這畢竟不如主動式檢核來得好,因為通常等到 80% 的警告訊息出現時通常情況就已經很嚴重了,而告警容量   訂得太低則會經常收到告警「但其實系統正常運作」。 但它還是可以寫,只是當做一個備用的告警就好,平常就   要注意不要讓它 init 到告警值。 五、最後就是該系統運作狀態的觀看介面,不論是寫成指令式   或是選單式,重點都在於要能即時知道系統目前的運作狀   態甚至紀錄的訊息詳情、特定資料的搜尋、比對等等。 以上亦會做為個人在 sanc 的 coding 參考,當然實際上依照 需儲存的資料內容來說,大部份的資料其實都不太有資料滿溢 的情況,這是因為在系統建置之初就已考量好資料儲存的格式 及內容範圍,也就是先做好事前的預防,自可避免事後的問題 ,但即便思慮再周詳,也不能完全保證系統運作一段時間後不 會產生其它問題,因此最好還是在初期就把相關的檢核機制也 一併加進去做把關,這樣至少可更確保系統的穩定運作。 但是有個兩難的問題就是,有時候也可能因為檢核寫的太優良 ,導致「系統即便遇到問題還是能持續運作,但是該問題也同 時持續存在」的情況發生,而可能在某一個時間點導致更大的 問題狀況出現。在漫畫《無敵怪醫》裡面就有這樣的情節,人 工肝臟一般都有產生血栓的問題,導致剛被植入到生物體內沒 多久就出現血栓,但某人發明的人工肝臟「卻因太過優秀」, 導致植物入生物體內後經過快半年都沒出問題,某人就打算進 行人體實驗,然後不幸的就是在人體實驗前幾天,被植入人工 肝臟的生物此時才出現血栓症狀,而導致人體實驗必須中止.. so,總之這也需要經驗的判斷。 Laechan@Sanc -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 122.117.106.224 ※ 文章網址: http://www.ptt.cc/bbs/mud/M.1400725946.A.363.html
tenyfish :推,這樣是做回合或副本結算的機制 42.75.86.41 05/22 18:40
tenyfish :嗎 正在研究大大推的darksoul lib 42.75.86.41 05/22 18:43
嗯嗯,副本是可以這樣做的,然後也因為副本也是有不正常結 束的情況(比方玩家長期在副本內斷線或突然 quit 等),跟戰 鬥的不正常結束是類似的情況。 ※ 編輯: laechan (1.165.164.217), 05/22/2014 22:35:51
nfsong :push 111.241.35.223 05/25 13:38