夜晚越黑暗取募,星星就越明亮--布隆
很多游戲存在過被刷資源(裝備琐谤、道具蟆技、經驗元寶等)的bug玩敏,一旦資源被刷斗忌,就會破壞游戲平衡,不僅影響公司名譽旺聚,補救措施也會帶來不少損失织阳,比如可能要停機緊急維護,可能要回檔(游戲數據還原)砰粹,可能要封號唧躲,可能要補償其他未刷的玩家,無論哪種操作碱璃,都可能造成玩家的流失弄痹,而如果是大R(高人民幣玩家)的流失,影響則更嚴重嵌器,因為公司會少了充值收入了肛真,而補償也會導致一些玩家暫時不會充錢了。所以寫好一個健全強壯的功能至關重要爽航。
游戲被刷的根本原因就在于程序邏輯本身的不健壯蚓让,安全檢查做少了,以致前端可繞過某些限制讥珍,頻繁請求該玩法協(xié)議历极,導致該玩法獎勵無限發(fā)放,就出現了資源被刷的現象衷佃。
所以資源被刷離不開兩點:
一趟卸、安全檢查做少了;
二纲酗、客戶端頻繁請求衰腌。
比如某項獎勵被領取之后,就應記錄它已被領取的狀態(tài)觅赊,當客戶端再次請求領取時右蕊,先判斷它是否已被領取,如果已領取了吮螺,那就不再發(fā)放饶囚。但是,即便這樣做了鸠补,它就一定安全了嗎萝风?未必。如果發(fā)獎的邏輯寫在前紫岩,記錄領獎狀態(tài)的邏輯寫在后规惰,因為發(fā)獎的資源可能要發(fā)放到多個功能模塊去(比如元寶可能要發(fā)放到角色模塊,裝備要發(fā)放到裝備模塊泉蝌,道具要發(fā)放到道具模塊歇万,這樣的目的是邏輯解藕)揩晴,或在發(fā)獎和記錄狀態(tài)邏輯之間加了其它業(yè)務邏輯,如果發(fā)生道具發(fā)獎異程盎牵或其它業(yè)務邏輯異常硫兰,后面的記錄狀態(tài)邏輯就不會執(zhí)行了,但獎勵已發(fā)了一半或全部發(fā)了寒锚,因此當客戶端再次請求時劫映,因為沒記錄已領獎狀態(tài),那么仍可領部分或全部獎勵刹前,然后再報道具發(fā)獎異秤靖常或其它業(yè)務邏輯異常,仍然沒記錄領獎狀態(tài)喇喉,如此循環(huán)摹蘑,就出現資源被刷了。因此轧飞,在寫領獎邏輯時衅鹿,要遵循一個原則:先判斷領獎狀態(tài),再做領獎記錄过咬,最后才發(fā)獎大渤。發(fā)獎異常補償總比被刷好。
上述是獲得資源時遵循的原則掸绞,但在消耗資源時泵三,原則順序卻要變一下,不然也會導致漏扣或不扣資源的情況衔掸,變成了變相被刷烫幕。比如我要消耗一些材料去強化某件裝備,強化成功敞映,裝備強化等級加1较曼,屬性獲得提高。首先判斷材料是否足夠振愿,這沒毛病捷犹。但是如果再做強化成功記錄,再扣資源冕末,這就可能有問題了萍歉,因為扣材料時有可能發(fā)生異常,導致少扣或沒扣材料档桃,而強化卻已記錄成功了枪孩,客戶端循環(huán)如此請求,就變成刷其它養(yǎng)成系統(tǒng)了,也是一種變相刷資源蔑舞,因此丛晌,在寫消耗資源邏輯時,應遵循的原則是:先判斷是否足夠斗幼,再做資源扣除,最后做養(yǎng)成記錄抚垄。關鍵是要防止被刷蜕窿。
以上是兩個常見被刷的地方,除此之外呆馁,還有很多業(yè)務邏輯也可能被刷桐经,不勝枚舉。比如浙滤,上面消耗資源判斷邏輯時阴挣,也不一定是安全的,比如我在商店要用銀兩買一個道具纺腊,假設服務端存儲銀兩是用一個int來存儲的畔咧,請求購買時需傳一個int的數量過來,而配置中揖膜,該商品的單價也是一個int值誓沸,在購買時確實判斷了身上的銀兩是否小于數量x單價的總量,這看起來是沒錯啊壹粟,但是忽略了一點拜隧,計算機在計算int*int時,可能溢出為一個負數了趁仙,所以我身上的銀兩數不可能小于一個負數洪添,這樣就通過了判斷邏輯,最后扣除銀兩時雀费,減去一個負數干奢,銀兩不減反增了,這也出現被刷了盏袄。所以這種情況律胀,還需判斷最后的總價應該要是一個大于0的值。
要寫一個健壯的邏輯貌矿,在寫的時候炭菌,我們就應多考慮下客戶端作弊的情況,想下如果客戶端頻繁請求該協(xié)議會出現什么樣的現象逛漫?如果客戶端隨意更改請求協(xié)議字段的值又會怎樣黑低?你要相信一定會有這樣的牛人繞過客戶端的限制做到隨意請求協(xié)議隨意更改協(xié)議內容的,再舉兩個曾經改協(xié)議被刷的例子,比如一個爬塔玩法克握,每成功爬一層蕾管,就會有寶箱獎勵,當時協(xié)議內容是請求要打的層數菩暗,獎勵的邏輯是掰曾,第一,要打贏了才能領停团,第二旷坦,要寶箱沒被領才可領,成功則爬塔層數加1佑稠,寶箱狀態(tài)重置秒梅,這種在正常的玩法下不會出事,但是一旦改協(xié)議舌胶,這樣的邏輯就會被刷了捆蜀,因為我爬完后我可以改協(xié)議重新請求從第一層爬啊,這樣獎勵就重新被刷了幔嫂,要防止被刷辆它,就應記錄每層的領獎狀態(tài),然而就算記了每層獎勵狀態(tài)履恩,也不完美娩井,玩家還可以從第一層打,雖然不能刷獎了似袁,但可增加服務器消息量變相攻擊服務器啊洞辣,因此要么判斷請求的層數不能小于當前已打到的最高層數,要么就不讓前端傳要打的層數昙衅,因為后端自己是知道玩家當前打到多少層的扬霜,這就是做邏輯(定協(xié)議)時的另外一個原則,盡量讓前端少傳已知的數據上來而涉。
還有同樣的一個例子盾鳞,使用物品時书妻,協(xié)議內容是該物品的唯一編碼和該物品的配置id及數量檐薯,結果在判斷物品有沒有和判斷物品足不足夠的檢查中通過了练俐,而且該協(xié)議里的配置id也確實是使用時規(guī)定消耗的物品id,但是扣除物品時季眷,卻扣了另外一個廉價的道具余蟹,原因是通過唯一編碼取得物品時服務端沒有判斷該唯一編碼對應的物品的配置id是否和協(xié)議上傳的物品配置id相等了,而是用協(xié)議上傳的配置id通過了是正確規(guī)定需要的材料子刮,結果繞過了安全檢查威酒,導致實際扣的卻是另一種背包里的廉價道具窑睁,這例子足矣說明玩家的“機智”,同樣的葵孤,也說明盡量少傳服務端的已知的數據担钮,上面例子,如果不傳配置id尤仍,服務端以唯一編碼取得該物品時就已知該物品的配置id了箫津,用它與規(guī)定的配置id對比即可,而不需要用前端上傳的配置id與規(guī)定的配置id一致來判斷宰啦。通過上面兩個例子可以知道苏遥,有些玩法在正常玩耍時,不會有事绑莺,畢竟會改協(xié)議的人少,但一旦被改協(xié)議惕耕,就可能出現被刷的風險纺裁。
被刷防不勝防而邏輯又難做到萬無一失,要如何才能防止被刷呢司澎?除了上述幾個原則欺缘,及寫功能時多想象協(xié)議被重復請求或協(xié)議被修改請求會有什么后果時,我們還有一些預防方案可以避免:
一挤安、協(xié)議安全防范
給協(xié)議做簡單加密即可谚殊,不需那么復雜,影響效率蛤铜,如每個客戶端和服務端都維護一個自己的協(xié)議序號嫩絮,如果協(xié)議序號不相等,便可能是造假協(xié)議围肥,丟棄剿干。再比如給協(xié)議做簡單的循環(huán)冗余校驗。有些人說的給協(xié)議做非對稱加密算法加密穆刻,還不如先等游戲火起來再說置尔。
二、做郵件預警
基本思路是記錄在線玩家每天獲得的道具總量氢伟,然后在道具表和裝備等表策劃配置一個預期一天最多正常獲得的數量榜轿,如果玩家獲得的超過了這個數量,就發(fā)郵件到管理人員報警朵锣,針對數額巨大的可寫踢下線或封號處理邏輯谬盐,核查后如果是誤報的情況再行解封。在做郵件預警時诚些,因為每個玩家每天要做裝備道具數量緩存设褐,因此會有一定內存消耗,對在線人數不多的游戲可以考慮使用。(后續(xù)會寫郵件預警實現的博文助析,郵件預警還可監(jiān)控后臺異常報錯報警)
三犀被、請求間隔控制
有些功能雖然做到安全檢查了,確實不會存在刷資源的問題了外冀,比如有個卡牌類的組隊副本玩法寡键,一點挑戰(zhàn)就計算好戰(zhàn)斗并發(fā)獎并把戰(zhàn)斗流發(fā)給前端播放,三個人組好隊后每人還有10次組隊收益雪隧,當一點挑戰(zhàn)時西轩,各個安全檢查都通過,比如都還有收益次數脑沿,都符合挑戰(zhàn)需求的等級等等藕畔,但是,當網絡卡的時候庄拇,那個挑戰(zhàn)按鈕還是可以點擊的注服,這樣隊長連續(xù)點10次以上,那么一下就把10次收益做完了措近,這不能算是刷資源溶弟,因為只可挑戰(zhàn)規(guī)定的那么多次,而且每次點擊時瞭郑,都符合挑戰(zhàn)和發(fā)獎要求辜御,只是這樣不好,因為本來預計讓玩家半個小時的玩法屈张,現在1秒內就玩完了擒权,這樣他就可以抽出時間打其他資源了,對別的玩家不公平阁谆,所以這個時候菜拓,可以加個請求間隔控制的功能,對同一個玩家同一個功能限制多少秒(比如3秒)時間內不可以請求第二次笛厦,過了這個時間才可請求纳鼎,這個時間足以抵擋網絡延遲,讓前端正常播放戰(zhàn)斗流裳凸,符合玩法預期目標了贱鄙。注意的是這樣也會有一部分內存消耗,在內存條件允許的情況下可以這么做姨谷,下線清掉玩家的各個功能間隔限制緩存逗宁。
四、機器人測試
既然資源被刷的明顯特點就是客戶端的頻繁請求梦湘,那么我們可以做個功能機器人(后續(xù)博文會實例介紹如何做各種功能的壓測機器人)瞎颗,讓機器人隨意組裝協(xié)議件甥,隨意大量請求該功能玩法協(xié)議,也有一定幾率能測出被刷bug哼拔,提前做好預防引有。
五、治標不如治本
做好預防倦逐,最好的方法就是寫功能邏輯時譬正,特別是關乎資源產出和消耗的功能時,一定要做好各種安全檢查檬姥,多想象下客戶端作弊攻擊的情景曾我,不要相信前端上傳的字段,不要相信策劃說功能驗收OK了健民,不要相信測試說功能測好沒問題了抒巢,必須自己至少測一遍,特別是臨界點的測試秉犹,時刻關注數據表是否按預期更新記錄了蛉谜,自己測好的才最放心,因為線上一旦出事被刷凤优,最大背鍋的人是寫這個程序的你悦陋。