一段網上找的代碼突然爆了,項目出現(xiàn)大Bug!
本人是做游戲服務器開發(fā)的,碰到一個需求,給符合某些要求的玩家發(fā)送道具獎勵,獎勵的數(shù)量根據(jù)離線的天數(shù)計算。
圖片來自 Pexels
這個需求實現(xiàn)起來很簡單,只需要在玩家上線的時候計算上次離線時間和當前時間間隔的天數(shù),然后根據(jù)策劃的算法,計算出道具種類與數(shù)量,發(fā)一封郵件給玩家就可以了。
計算兩個時間間隔天數(shù)的函數(shù)沒有現(xiàn)成的,自己又懶得寫,就上谷歌搜了下,選了第一條結果,代碼如下:
- public static int differentDays(Date date1,Date date2)
- {
- Calendar cal1 = Calendar.getInstance();
- cal1.setTime(date1);
- Calendar cal2 = Calendar.getInstance();
- cal2.setTime(date2);
- int day1= cal1.get(Calendar.DAY_OF_YEAR);
- int day2 = cal2.get(Calendar.DAY_OF_YEAR);
- int year1 = cal1.get(Calendar.YEAR);
- int year2 = cal2.get(Calendar.YEAR);
- if(year1 != year2) //同一年
- {
- int timeDistance = 0 ;
- for(int i = year1 ; i < year2 ; i ++)
- {
- if(i%4==0 && i%100!=0 || i%400==0) //閏年
- {
- timeDistance += 366;
- }
- else //不是閏年
- {
- timeDistance += 365;
- }
- }
- return timeDistance + (day2-day1) ;
- }
- else //不同年
- {
- System.out.println("判斷day2 - day1 : " + (day2-day1));
- return day2-day1;
- }
- }
代碼來源:https://www.cnblogs.com/0201zcr/p/5000977.html
把代碼復制到項目里,調試下,發(fā)現(xiàn)沒問題就直接用了,畢竟谷歌結果第一名,放心。
這段代碼跑了幾個月一直沒問題,但是到了 2020-1-1 日那天,有玩家反饋收到了幾百封獎勵郵件,高興壞了,但是出于對游戲的熱愛,還是通知了運營人員。
運營把 Bug 反饋到服務器這邊后我開始排查,百思不得其解的是最近幾天都沒有更新服務器, 而前幾天服務器都穩(wěn)穩(wěn)地,怎么突然就出 Bug 了呢?
接下來就是分析玩家數(shù)據(jù),結合代碼邏輯確定問題所在,最終根據(jù) Bug 的表現(xiàn)排除了所有可能性后,發(fā)現(xiàn)唯一可能出問題的地方就是那個網上復制過來的計算天數(shù)差的函數(shù)。
根據(jù)調試發(fā)現(xiàn),這個函數(shù)在兩個日期參數(shù)是不同的年份并且第一個日期大于第二個日期的時候,會返回一個錯誤的結果,比如:
- differentDays("2020-1-1","2019-12-25")
理論上這么調用正確的結果是 -7,但是因為函數(shù)有 Bug,調用結果是 358。
于是本來不用發(fā)獎勵,因為這種特殊情況一下子發(fā)出去 358 份,嚴重影響了游戲某類道具的平衡性。
最后,我改用 Java8 的日期庫修復了 Bug,代碼如下:
- public static int differentDays(Date date1, Date date2) {
- if (date1 == null || date2 == null) {
- throw new RuntimeException("日期不能為空");
- }
- LocalDate localDate1 = date2LocalDate(date1);
- LocalDate localDate2 = date2LocalDate(date2);
- return Generic.long2int(localDate1.until(localDate2, ChronoUnit.DAYS));
- }
- public static LocalDate date2LocalDate(Date date) {
- Instant instant = date.toInstant();
- ZoneId zoneId = ZoneId.systemDefault();
- LocalDate localDate = instant.atZone(zoneId).toLocalDate();
- return localDate;
- }
至于補救方式就是統(tǒng)計名單,把發(fā)出去但還沒有用掉的道具回收,用掉的就當福利,然后再發(fā)公告道歉,再送些其他物品彌補。
也幸好補救的及時,要是這些道具收不回來,游戲運營的策略都要大變了,我特么肯定沒好果子吃了。
所以千萬別在網上復制來路不明的代碼亂用,如果真的要用,必須反復測試,否則哪一天突然暴雷有你受的。