OSDN Git Service

艦これに仕様変更があったときにメッセージを表示する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Collections.Generic;\r
20 using System.Linq;\r
21 \r
22 namespace KancolleSniffer\r
23 {\r
24     public class Sniffer\r
25     {\r
26         private bool _start;\r
27         private readonly ShipMaster _shipMaster = new ShipMaster();\r
28         private readonly ItemInfo _itemInfo = new ItemInfo();\r
29         private readonly QuestInfo _questInfo = new QuestInfo();\r
30         private readonly MissionInfo _missionInfo = new MissionInfo();\r
31         private readonly ShipInfo _shipInfo;\r
32         private readonly ConditionTimer _conditionTimer;\r
33         private readonly DockInfo _dockInfo;\r
34         private readonly AkashiTimer _akashiTimer;\r
35         private readonly Achievement _achievement = new Achievement();\r
36         private readonly BattleInfo _battleInfo;\r
37         private readonly Logger _logger;\r
38         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
39         private readonly Status _status = new Status();\r
40         private bool _saveState;\r
41         private readonly List<IHaveState> _haveState;\r
42 \r
43         [Flags]\r
44         public enum Update\r
45         {\r
46             None = 0,\r
47             Start = 1,\r
48             Item = 2,\r
49             Ship = 4,\r
50             Timer = 8,\r
51             NDock = 16,\r
52             Mission = 32,\r
53             QuestList = 64,\r
54             Battle = 128,\r
55             All = 255\r
56         }\r
57 \r
58         public Sniffer()\r
59         {\r
60             _shipInfo = new ShipInfo(_shipMaster, _itemInfo);\r
61             _conditionTimer = new ConditionTimer(_shipInfo);\r
62             _dockInfo = new DockInfo(_shipInfo, _itemInfo);\r
63             _akashiTimer = new AkashiTimer(_shipInfo, _itemInfo, _dockInfo);\r
64             _battleInfo = new BattleInfo(_shipMaster, _shipInfo, _itemInfo);\r
65             _logger = new Logger(_shipMaster, _shipInfo, _itemInfo);\r
66             _haveState = new List<IHaveState> {_achievement, _itemInfo, _conditionTimer, _exMapInfo};\r
67         }\r
68 \r
69         private void SaveState()\r
70         {\r
71             if (!_saveState)\r
72                 return;\r
73             if (!_haveState.Any(x=>x.NeedSave))\r
74                 return;\r
75             foreach (var x in _haveState)\r
76                 x.SaveState(_status);\r
77             _status.Save();\r
78         }\r
79 \r
80         public void LoadState()\r
81         {\r
82             _status.Load();\r
83             foreach (var x in _haveState)\r
84                 x.LoadState(_status);\r
85             _saveState = true;\r
86         }\r
87 \r
88         public Update Sniff(string url, string request, dynamic json)\r
89         {\r
90             var data = json.IsDefined("api_data") ? json.api_data : new object();\r
91 \r
92             if (url.EndsWith("api_start2"))\r
93             {\r
94                 _shipMaster.Inspect(data);\r
95                 _missionInfo.InspectMaster(data.api_mst_mission);\r
96                 _itemInfo.InspectMaster(data);\r
97                 _exMapInfo.ResetIfNeeded();\r
98                 _start = true;\r
99                 return Update.Start;\r
100             }\r
101             if (!_start)\r
102                 return Update.None;\r
103             if (url.EndsWith("api_port/port"))\r
104             {\r
105                 _itemInfo.InspectBasic(data.api_basic);\r
106                 _itemInfo.InspectMaterial(data.api_material);\r
107                 _logger.InspectBasic(data.api_basic);\r
108                 _logger.InspectMaterial(data.api_material);\r
109                 _shipInfo.InspectShip(data);\r
110                 _conditionTimer.CalcRegenTime();\r
111                 _missionInfo.InspectDeck(data.api_deck_port);\r
112                 _dockInfo.InspectNDock(data.api_ndock);\r
113                 _akashiTimer.SetTimer(true);\r
114                 _achievement.InspectBasic(data.api_basic);\r
115                 if (data.api_parallel_quest_count()) // 昔のログにはないので\r
116                     _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
117                 _battleInfo.CleanupResult();\r
118                 _battleInfo.InBattle = false;\r
119                 _battleInfo.HasDamagedShip = false;\r
120                 _shipInfo.ClearEscapedShips();\r
121                 SaveState();\r
122                 return Update.All;\r
123             }\r
124             if (url.EndsWith("api_get_member/basic"))\r
125             {\r
126                 _itemInfo.InspectBasic(data);\r
127                 _logger.InspectBasic(data);\r
128                 return Update.None;\r
129             }\r
130             if (url.EndsWith("api_get_member/slot_item"))\r
131             {\r
132                 _itemInfo.InspectSlotItem(data, true);\r
133                 return Update.None;\r
134             }\r
135             if (url.EndsWith("api_get_member/kdock"))\r
136             {\r
137                 _dockInfo.InspectKDock(data);\r
138                 _logger.InspectKDock(data);\r
139                 return Update.Timer;\r
140             }\r
141             if (url.EndsWith("api_get_member/ndock"))\r
142             {\r
143                 _dockInfo.InspectNDock(data);\r
144                 _conditionTimer.CheckCond();\r
145                 _akashiTimer.SetTimer();\r
146                 return Update.NDock | Update.Timer | Update.Ship;\r
147             }\r
148             if (url.EndsWith("api_req_hensei/change"))\r
149             {\r
150                 _shipInfo.InspectChange(request);\r
151                 _akashiTimer.SetTimer();\r
152                 return Update.Ship;\r
153             }\r
154             if (url.EndsWith("api_get_member/questlist"))\r
155             {\r
156                 _questInfo.Inspect(data);\r
157                 return Update.QuestList;\r
158             }\r
159             if (url.EndsWith("api_get_member/deck"))\r
160             {\r
161                 _shipInfo.InspectDeck(data);\r
162                 _missionInfo.InspectDeck(data);\r
163                 _akashiTimer.SetTimer();\r
164                 return Update.Mission | Update.Timer;\r
165             }\r
166             if (url.EndsWith("api_get_member/ship2"))\r
167             {\r
168                 // ここだけjsonなので注意\r
169                 _shipInfo.InspectShip(json);\r
170                 _akashiTimer.SetTimer();\r
171                 _battleInfo.InBattle = false;\r
172                 return Update.Item | Update.Ship | Update.Battle;\r
173             }\r
174             if (url.EndsWith("api_get_member/ship_deck"))\r
175             {\r
176                 _shipInfo.InspectShip(data);\r
177                 _akashiTimer.SetTimer();\r
178                 _battleInfo.InBattle = false;\r
179                 return Update.Ship | Update.Battle;\r
180             }\r
181             if (url.EndsWith("api_get_member/ship3"))\r
182             {\r
183                 _shipInfo.InspectShip(data);\r
184                 _akashiTimer.SetTimer();\r
185                 _conditionTimer.CheckCond();\r
186                 return Update.Ship;\r
187             }\r
188             if (url.EndsWith("api_get_member/material"))\r
189             {\r
190                 _itemInfo.InspectMaterial(data);\r
191                 return Update.Item;\r
192             }\r
193             if (url.EndsWith("api_req_hokyu/charge"))\r
194             {\r
195                 _shipInfo.InspectCharge(data);\r
196                 return Update.Item | Update.Ship;\r
197             }\r
198             if (url.EndsWith("api_req_kousyou/createitem"))\r
199             {\r
200                 _itemInfo.InspectCreateItem(data);\r
201                 _logger.InspectCreateItem(request, data);\r
202                 return Update.Item;\r
203             }\r
204             if (url.EndsWith("api_req_kousyou/getship"))\r
205             {\r
206                 _itemInfo.InspectGetShip(data);\r
207                 _shipInfo.InspectShip(data);\r
208                 _dockInfo.InspectKDock(data.api_kdock);\r
209                 _conditionTimer.CheckCond();\r
210                 return Update.Item | Update.Timer;\r
211             }\r
212             if (url.EndsWith("api_req_kousyou/destroyship"))\r
213             {\r
214                 _shipInfo.InspectDestroyShip(request, data);\r
215                 _conditionTimer.CheckCond();\r
216                 _akashiTimer.SetTimer();\r
217                 return Update.Item | Update.Ship;\r
218             }\r
219             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
220             {\r
221                 _itemInfo.InspectDestroyItem(request, data);\r
222                 return Update.Item;\r
223             }\r
224             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
225             {\r
226                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_itemInfoより前\r
227                 _itemInfo.InspectRemodelSlot(data);\r
228                 return Update.Item;\r
229             }\r
230             if (url.EndsWith("api_req_kousyou/createship"))\r
231             {\r
232                 _logger.InspectCreateShip(request);\r
233                 return Update.None;\r
234             }\r
235             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
236             {\r
237                 _dockInfo.InspectCreateShipSpeedChange(request);\r
238                 return Update.Timer;\r
239             }\r
240             if (url.EndsWith("api_req_kaisou/powerup"))\r
241             {\r
242                 _shipInfo.InspectPowerup(request, data);\r
243                 _conditionTimer.CheckCond();\r
244                 _akashiTimer.SetTimer();\r
245                 return Update.Item | Update.Ship;\r
246             }\r
247             if (url.EndsWith("api_req_nyukyo/start"))\r
248             {\r
249                 _dockInfo.InspectNyukyo(request);\r
250                 _conditionTimer.CheckCond();\r
251                 _akashiTimer.SetTimer();\r
252                 return Update.Item | Update.Ship;\r
253             }\r
254             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
255             {\r
256                 _dockInfo.InspectSpeedChange(request);\r
257                 _conditionTimer.CheckCond();\r
258                 return Update.NDock | Update.Timer | Update.Ship;\r
259             }\r
260             if (IsNormalBattleAPI(url))\r
261             {\r
262                 _battleInfo.InspectBattle(data);\r
263                 _logger.InspectBattle(data);\r
264                 return Update.Ship | Update.Battle;\r
265             }\r
266             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
267             {\r
268                 if (url.EndsWith("/battle"))\r
269                 {\r
270                     _shipInfo.StartSortie(request); // 演習を出撃中とみなす\r
271                     _conditionTimer.InvalidateCond();\r
272                 }\r
273                 _battleInfo.InspectBattle(data);\r
274                 return Update.Ship | Update.Battle | Update.Timer;\r
275             }\r
276             if (url.EndsWith("api_req_sortie/battleresult"))\r
277             {\r
278                 _battleInfo.InspectBattleResult(json);\r
279                 _exMapInfo.InspectBattleResult(data);\r
280                 _logger.InspectBattleResult(data);\r
281                 return Update.Ship;\r
282             }\r
283             if (url.EndsWith("api_req_practice/battle_result"))\r
284             {\r
285                 _battleInfo.InspectPracticeResult(json);\r
286                 return Update.Ship;\r
287             }\r
288             if (IsCombinedBattleAPI(url))\r
289             {\r
290                 _battleInfo.InspectCombinedBattle(data, url.EndsWith("battle_water"));\r
291                 _logger.InspectBattle(data);\r
292                 return Update.Ship | Update.Battle;\r
293             }\r
294             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
295             {\r
296                 _battleInfo.InspectCombinedBattleResult(data);\r
297                 _logger.InspectBattleResult(data);\r
298                 return Update.Ship;\r
299             }\r
300             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
301             {\r
302                 _battleInfo.CauseCombinedBattleEscape();\r
303                 return Update.Ship;\r
304             }\r
305             if (url.EndsWith("api_req_map/start"))\r
306             {\r
307                 _shipInfo.StartSortie(request);\r
308                 _conditionTimer.InvalidateCond();\r
309                 _exMapInfo.InspectMapStart(data);\r
310                 _logger.InspectMapStart(data);\r
311                 return Update.Timer;\r
312             }\r
313             if (url.EndsWith("api_req_map/next"))\r
314             {\r
315                 _exMapInfo.InspectMapNext(data);\r
316                 _logger.InspectMapNext(data);\r
317                 return Update.None;\r
318             }\r
319             if (url.EndsWith("api_req_mission/result"))\r
320             {\r
321                 _itemInfo.InspectMissionResult(data);\r
322                 _logger.InspectMissionResult(data);\r
323                 return Update.Item;\r
324             }\r
325             if (url.EndsWith("api_get_member/mapinfo"))\r
326             {\r
327                 _exMapInfo.InspectMapInfo(data);\r
328                 return Update.None;\r
329             }\r
330             return Update.None;\r
331         }\r
332 \r
333         private bool IsNormalBattleAPI(string url)\r
334         {\r
335             return url.EndsWith("api_req_sortie/battle") ||\r
336                    url.EndsWith("api_req_sortie/airbattle") ||\r
337                    url.EndsWith("api_req_battle_midnight/battle") ||\r
338                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
339         }\r
340 \r
341         private bool IsCombinedBattleAPI(string url)\r
342         {\r
343             return url.EndsWith("api_req_combined_battle/battle") ||\r
344                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
345                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
346                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
347                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
348         }\r
349 \r
350         public NameAndTimer[] NDock\r
351         {\r
352             get { return _dockInfo.NDock; }\r
353         }\r
354 \r
355         public RingTimer[] KDock\r
356         {\r
357             get { return _dockInfo.KDock; }\r
358         }\r
359 \r
360         public ItemInfo Item\r
361         {\r
362             get { return _itemInfo; }\r
363         }\r
364 \r
365         public QuestStatus[] Quests\r
366         {\r
367             get { return _questInfo.Quests; }\r
368         }\r
369 \r
370         public NameAndTimer[] Missions\r
371         {\r
372             get { return _missionInfo.Missions; }\r
373         }\r
374 \r
375         public DateTime GetConditionTimer(int fleet)\r
376         {\r
377             return _conditionTimer.GetTimer(fleet);\r
378         }\r
379 \r
380         public int[] GetConditionNotice()\r
381         {\r
382             return _conditionTimer.GetNotice();\r
383         }\r
384 \r
385         public ShipStatus[] GetShipStatuses(int fleet)\r
386         {\r
387             return _shipInfo.GetShipStatuses(fleet);\r
388         }\r
389 \r
390         public int[] GetDeck(int fleet)\r
391         {\r
392             return _shipInfo.GetDeck(fleet);\r
393         }\r
394 \r
395         public ChargeStatus[] ChargeStatuses\r
396         {\r
397             get { return _shipInfo.ChargeStatuses; }\r
398         }\r
399 \r
400         public int GetFighterPower(int fleet)\r
401         {\r
402             return _shipInfo.GetFighterPower(fleet);\r
403         }\r
404 \r
405         public double GetFleetLineOfSights(int fleet)\r
406         {\r
407             return _shipInfo.GetLineOfSights(fleet);\r
408         }\r
409 \r
410         public ShipStatus[] DamagedShipList\r
411         {\r
412             get { return _shipInfo.GetDamagedShipList(_dockInfo); }\r
413         }\r
414 \r
415         public ShipStatus[] ShipList\r
416         {\r
417             get { return _shipInfo.ShipList; }\r
418         }\r
419 \r
420         public ItemStatus[] ItemList\r
421         {\r
422             get\r
423             {\r
424                 _itemInfo.SetItemOwner(ShipList);\r
425                 return (from e in _itemInfo.ItemDict where e.Key != -1 select e.Value).ToArray();\r
426             }\r
427         }\r
428 \r
429         public AkashiTimer.RepairSpan[] GetAkashiTimers(int fleet)\r
430         {\r
431             return _akashiTimer.GetTimers(fleet);\r
432         }\r
433 \r
434         public string[] GetAkashiTimerNotice()\r
435         {\r
436             return _akashiTimer.GetNotice();\r
437         }\r
438 \r
439         public Achievement Achievement\r
440         {\r
441             get { return _achievement; }\r
442         }\r
443 \r
444         public BattleInfo Battle\r
445         {\r
446             get { return _battleInfo; }\r
447         }\r
448 \r
449         public ExMapInfo ExMap\r
450         {\r
451             get { return _exMapInfo; }\r
452         }\r
453 \r
454         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
455         {\r
456             _logger.SetWriter(writer, nowFunc);\r
457         }\r
458 \r
459         public void SkipMaster()\r
460         {\r
461             _start = true;\r
462         }\r
463 \r
464         public void EnableLog(LogType type)\r
465         {\r
466             _logger.EnableLog(type);\r
467         }\r
468 \r
469         public int MaterialLogInterval\r
470         {\r
471             set { _logger.MaterialLogInterval = value; }\r
472         }\r
473 \r
474         public string LogOutputDir\r
475         {\r
476             set { _logger.OutputDir = value; }\r
477         }\r
478     }\r
479 \r
480     public class NameAndTimer\r
481     {\r
482         public string Name { get; set; }\r
483         public RingTimer Timer { get; set; }\r
484 \r
485         public NameAndTimer()\r
486         {\r
487             Timer = new RingTimer();\r
488         }\r
489     }\r
490 \r
491     public class RingTimer\r
492     {\r
493         private DateTime _endTime;\r
494         private readonly TimeSpan _spare;\r
495 \r
496         public TimeSpan Rest { get; private set; }\r
497 \r
498         public bool IsFinished\r
499         {\r
500             get { return _endTime != DateTime.MinValue && Rest <= _spare; }\r
501         }\r
502 \r
503         public bool NeedRing { get; set; }\r
504 \r
505         public RingTimer(int spare = 60)\r
506         {\r
507             _spare = TimeSpan.FromSeconds(spare);\r
508         }\r
509 \r
510         public void SetEndTime(double time)\r
511         {\r
512             SetEndTime((int)time == 0\r
513                 ? DateTime.MinValue\r
514                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
515         }\r
516 \r
517         public void SetEndTime(DateTime time)\r
518         {\r
519             _endTime = time;\r
520         }\r
521 \r
522         public void Update()\r
523         {\r
524             if (_endTime == DateTime.MinValue)\r
525             {\r
526                 Rest = TimeSpan.Zero;\r
527                 return;\r
528             }\r
529             var prev = Rest;\r
530             Rest = _endTime - DateTime.Now;\r
531             if (Rest < TimeSpan.Zero)\r
532                 Rest = TimeSpan.Zero;\r
533             if (prev > _spare && _spare >= Rest)\r
534                 NeedRing = true;\r
535         }\r
536     }\r
537 }