OSDN Git Service

api_startの処理に失敗したら続くAPIを処理しない
[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 ItemInfo _itemInfo = new ItemInfo();\r
28         private readonly MaterialInfo _materialInfo = new MaterialInfo();\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 MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
40         private readonly Status _status = new Status();\r
41         private bool _saveState;\r
42         private readonly List<IHaveState> _haveState;\r
43 \r
44         [Flags]\r
45         public enum Update\r
46         {\r
47             None = 0,\r
48             Error = 1 << 0,\r
49             Start = 1 << 1,\r
50             Item = 1 << 2,\r
51             Ship = 1 << 3,\r
52             Timer = 1 << 4,\r
53             NDock = 1 << 5,\r
54             Mission = 1 << 6,\r
55             QuestList = 1 << 7,\r
56             Battle = 1 << 8,\r
57             All = (1 << 9) - 1\r
58         }\r
59 \r
60         public Sniffer()\r
61         {\r
62             _shipInfo = new ShipInfo(_itemInfo);\r
63             _conditionTimer = new ConditionTimer(_shipInfo);\r
64             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
65             _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo);\r
66             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
67             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
68             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
69         }\r
70 \r
71         private void SaveState()\r
72         {\r
73             if (!_saveState)\r
74                 return;\r
75             if (!_haveState.Any(x => x.NeedSave))\r
76                 return;\r
77             foreach (var x in _haveState)\r
78                 x.SaveState(_status);\r
79             _status.Save();\r
80         }\r
81 \r
82         public void LoadState()\r
83         {\r
84             _status.Load();\r
85             foreach (var x in _haveState)\r
86                 x.LoadState(_status);\r
87             _saveState = true;\r
88         }\r
89 \r
90         public Update Sniff(string url, string request, dynamic json)\r
91         {\r
92             if (!json.api_result() || (int)json.api_result != 1)\r
93                 return Update.Error;\r
94             var data = json.api_data() ? json.api_data : new object();\r
95 \r
96             if (url.EndsWith("api_start2"))\r
97             {\r
98                 return ApiStart(data);\r
99             }\r
100             if (!_start)\r
101                 return Update.None;\r
102             if (url.EndsWith("api_port/port"))\r
103                 return ApiPort(data);\r
104             if (url.Contains("member"))\r
105                 return ApiMember(url, json);\r
106             if (url.Contains("kousyou"))\r
107                 return ApiKousyou(url, request, data);\r
108             if (url.Contains("battle"))\r
109                 return ApiBattle(url, request, data);\r
110             return ApiOthers(url, request, data);\r
111         }\r
112 \r
113         private Update ApiStart(dynamic data)\r
114         {\r
115             _shipInfo.InspectMaster(data);\r
116             _missionInfo.InspectMaster(data.api_mst_mission);\r
117             _itemInfo.InspectMaster(data);\r
118             _exMapInfo.ResetIfNeeded();\r
119             _start = true;\r
120             return Update.Start;\r
121         }\r
122 \r
123         private Update ApiPort(dynamic data)\r
124         {\r
125             _itemInfo.InspectBasic(data.api_basic);\r
126             _materialInfo.InspectMaterial(data.api_material, true);\r
127             _logger.InspectBasic(data.api_basic);\r
128             _logger.InspectMaterial(data.api_material);\r
129             _shipInfo.InspectShip(data);\r
130             _conditionTimer.CalcRegenTime();\r
131             _missionInfo.InspectDeck(data.api_deck_port);\r
132             _dockInfo.InspectNDock(data.api_ndock);\r
133             _akashiTimer.SetTimer(true);\r
134             _achievement.InspectBasic(data.api_basic);\r
135             if (data.api_parallel_quest_count()) // 昔のログにはないので\r
136                 _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
137             _battleInfo.CleanupResult();\r
138             _battleInfo.InBattle = false;\r
139             _battleInfo.HasDamagedShip = false;\r
140             _shipInfo.ClearEscapedShips();\r
141             _miscTextInfo.ClearIfNeeded();\r
142             SaveState();\r
143             return Update.All;\r
144         }\r
145 \r
146         private Update ApiMember(string url, dynamic json)\r
147         {\r
148             var data = json.api_data() ? json.api_data : new object();\r
149 \r
150             if (url.EndsWith("api_get_member/basic"))\r
151             {\r
152                 _itemInfo.InspectBasic(data);\r
153                 _logger.InspectBasic(data);\r
154                 return Update.None;\r
155             }\r
156             if (url.EndsWith("api_get_member/slot_item"))\r
157             {\r
158                 _itemInfo.InspectSlotItem(data, true);\r
159                 return Update.None;\r
160             }\r
161             if (url.EndsWith("api_get_member/kdock"))\r
162             {\r
163                 _dockInfo.InspectKDock(data);\r
164                 _logger.InspectKDock(data);\r
165                 return Update.Timer;\r
166             }\r
167             if (url.EndsWith("api_get_member/ndock"))\r
168             {\r
169                 _dockInfo.InspectNDock(data);\r
170                 _conditionTimer.CheckCond();\r
171                 _akashiTimer.SetTimer();\r
172                 return Update.NDock | Update.Timer | Update.Ship;\r
173             }\r
174             if (url.EndsWith("api_get_member/questlist"))\r
175             {\r
176                 _questInfo.Inspect(data);\r
177                 return Update.QuestList;\r
178             }\r
179             if (url.EndsWith("api_get_member/deck"))\r
180             {\r
181                 _shipInfo.InspectDeck(data);\r
182                 _missionInfo.InspectDeck(data);\r
183                 _akashiTimer.SetTimer();\r
184                 return Update.Mission | Update.Timer;\r
185             }\r
186             if (url.EndsWith("api_get_member/ship2"))\r
187             {\r
188                 // ここだけjsonなので注意\r
189                 _shipInfo.InspectShip(json);\r
190                 _akashiTimer.SetTimer();\r
191                 _battleInfo.InBattle = false;\r
192                 return Update.Item | Update.Ship | Update.Battle;\r
193             }\r
194             if (url.EndsWith("api_get_member/ship_deck"))\r
195             {\r
196                 _shipInfo.InspectShip(data);\r
197                 _akashiTimer.SetTimer();\r
198                 _battleInfo.InBattle = false;\r
199                 return Update.Ship | Update.Battle;\r
200             }\r
201             if (url.EndsWith("api_get_member/ship3"))\r
202             {\r
203                 _shipInfo.InspectShip(data);\r
204                 _akashiTimer.SetTimer();\r
205                 _conditionTimer.CheckCond();\r
206                 return Update.Ship;\r
207             }\r
208             if (url.EndsWith("api_get_member/material"))\r
209             {\r
210                 _materialInfo.InspectMaterial(data);\r
211                 return Update.Item;\r
212             }\r
213             if (url.EndsWith("api_get_member/mapinfo"))\r
214             {\r
215                 _exMapInfo.InspectMapInfo(data);\r
216                 _miscTextInfo.InspectMapInfo(data);\r
217                 return Update.Item;\r
218             }\r
219             if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
220             {\r
221                 _miscTextInfo.InspectPracticeEnemyInfo(data);\r
222                 return Update.Item;\r
223             }\r
224             return Update.None;\r
225         }\r
226 \r
227         private Update ApiKousyou(string url, string request, dynamic data)\r
228         {\r
229             if (url.EndsWith("api_req_kousyou/createitem"))\r
230             {\r
231                 _itemInfo.InspectCreateItem(data);\r
232                 _materialInfo.InspectCreateIem(data);\r
233                 _logger.InspectCreateItem(request, data);\r
234                 return Update.Item;\r
235             }\r
236             if (url.EndsWith("api_req_kousyou/getship"))\r
237             {\r
238                 _itemInfo.InspectGetShip(data);\r
239                 _shipInfo.InspectShip(data);\r
240                 _dockInfo.InspectKDock(data.api_kdock);\r
241                 _conditionTimer.CheckCond();\r
242                 return Update.Item | Update.Timer;\r
243             }\r
244             if (url.EndsWith("api_req_kousyou/destroyship"))\r
245             {\r
246                 _shipInfo.InspectDestroyShip(request, data);\r
247                 _materialInfo.InspectDestroyShip(data);\r
248                 _conditionTimer.CheckCond();\r
249                 _akashiTimer.SetTimer();\r
250                 return Update.Item | Update.Ship;\r
251             }\r
252             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
253             {\r
254                 _itemInfo.InspectDestroyItem(request, data);\r
255                 _materialInfo.InspectDestroyItem(data);\r
256                 return Update.Item;\r
257             }\r
258             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
259             {\r
260                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
261                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
262                 _itemInfo.InspectRemodelSlot(data);\r
263                 _materialInfo.InspectRemodelSlot(data);\r
264                 return Update.Item;\r
265             }\r
266             if (url.EndsWith("api_req_kousyou/createship"))\r
267             {\r
268                 _logger.InspectCreateShip(request);\r
269                 return Update.None;\r
270             }\r
271             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
272             {\r
273                 _dockInfo.InspectCreateShipSpeedChange(request);\r
274                 return Update.Timer;\r
275             }\r
276             return Update.None;\r
277         }\r
278 \r
279         private Update ApiBattle(string url, string request, dynamic data)\r
280         {\r
281             if (IsNormalBattleAPI(url))\r
282             {\r
283                 _battleInfo.InspectBattle(data);\r
284                 _logger.InspectBattle(data);\r
285                 return Update.Ship | Update.Battle;\r
286             }\r
287             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
288             {\r
289                 if (url.EndsWith("/battle"))\r
290                 {\r
291                     _shipInfo.StartSortie(request); // 演習を出撃中とみなす\r
292                     _conditionTimer.InvalidateCond();\r
293                     _miscTextInfo.ClearFlag = true;\r
294                 }\r
295                 _battleInfo.InspectBattle(data);\r
296                 return Update.Ship | Update.Battle | Update.Timer;\r
297             }\r
298             if (url.EndsWith("api_req_sortie/battleresult"))\r
299             {\r
300                 _battleInfo.InspectBattleResult(data);\r
301                 _exMapInfo.InspectBattleResult(data);\r
302                 _logger.InspectBattleResult(data);\r
303                 return Update.Ship;\r
304             }\r
305             if (url.EndsWith("api_req_practice/battle_result"))\r
306             {\r
307                 _battleInfo.InspectPracticeResult(data);\r
308                 return Update.Ship;\r
309             }\r
310             if (IsCombinedBattleAPI(url))\r
311             {\r
312                 _battleInfo.InspectCombinedBattle(data, url.EndsWith("battle_water"));\r
313                 _logger.InspectBattle(data);\r
314                 return Update.Ship | Update.Battle;\r
315             }\r
316             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
317             {\r
318                 _battleInfo.InspectCombinedBattleResult(data);\r
319                 _logger.InspectBattleResult(data);\r
320                 return Update.Ship;\r
321             }\r
322             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
323             {\r
324                 _battleInfo.CauseCombinedBattleEscape();\r
325                 return Update.Ship;\r
326             }\r
327             return Update.None;\r
328         }\r
329 \r
330         private bool IsNormalBattleAPI(string url)\r
331         {\r
332             return url.EndsWith("api_req_sortie/battle") ||\r
333                    url.EndsWith("api_req_sortie/airbattle") ||\r
334                    url.EndsWith("api_req_battle_midnight/battle") ||\r
335                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
336         }\r
337 \r
338         private bool IsCombinedBattleAPI(string url)\r
339         {\r
340             return url.EndsWith("api_req_combined_battle/battle") ||\r
341                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
342                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
343                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
344                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
345         }\r
346 \r
347         private Update ApiOthers(string url, string request, dynamic data)\r
348         {\r
349             if (url.EndsWith("api_req_hensei/change"))\r
350             {\r
351                 _shipInfo.InspectChange(request);\r
352                 _akashiTimer.SetTimer();\r
353                 return Update.Ship;\r
354             }\r
355             if (url.EndsWith("api_req_hokyu/charge"))\r
356             {\r
357                 _shipInfo.InspectCharge(data);\r
358                 _materialInfo.InspectCharge(data);\r
359                 return Update.Item | Update.Ship;\r
360             }\r
361             if (url.EndsWith("api_req_kaisou/powerup"))\r
362             {\r
363                 _shipInfo.InspectPowerup(request, data);\r
364                 _conditionTimer.CheckCond();\r
365                 _akashiTimer.SetTimer();\r
366                 return Update.Item | Update.Ship;\r
367             }\r
368             if (url.EndsWith("api_req_nyukyo/start"))\r
369             {\r
370                 _dockInfo.InspectNyukyo(request);\r
371                 _conditionTimer.CheckCond();\r
372                 _akashiTimer.SetTimer();\r
373                 return Update.Item | Update.Ship;\r
374             }\r
375             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
376             {\r
377                 _dockInfo.InspectSpeedChange(request);\r
378                 _conditionTimer.CheckCond();\r
379                 return Update.NDock | Update.Timer | Update.Ship;\r
380             }\r
381             if (url.EndsWith("api_req_map/start"))\r
382             {\r
383                 _shipInfo.StartSortie(request);\r
384                 _conditionTimer.InvalidateCond();\r
385                 _exMapInfo.InspectMapStart(data);\r
386                 _logger.InspectMapStart(data);\r
387                 _miscTextInfo.ClearFlag = true;\r
388                 return Update.Timer;\r
389             }\r
390             if (url.EndsWith("api_req_map/next"))\r
391             {\r
392                 _exMapInfo.InspectMapNext(data);\r
393                 _logger.InspectMapNext(data);\r
394                 return Update.None;\r
395             }\r
396             if (url.EndsWith("api_req_mission/result"))\r
397             {\r
398                 _materialInfo.InspectMissionResult(data);\r
399                 _logger.InspectMissionResult(data);\r
400                 return Update.Item;\r
401             }\r
402             return Update.None;\r
403         }\r
404 \r
405         public NameAndTimer[] NDock => _dockInfo.NDock;\r
406 \r
407         public RingTimer[] KDock => _dockInfo.KDock;\r
408 \r
409         public ItemInfo Item => _itemInfo;\r
410 \r
411         public MaterialInfo Material => _materialInfo;\r
412 \r
413         public QuestStatus[] Quests => _questInfo.Quests;\r
414 \r
415         public NameAndTimer[] Missions => _missionInfo.Missions;\r
416 \r
417         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
418 \r
419         public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
420 \r
421         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
422 \r
423         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
424 \r
425         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
426 \r
427         public int GetFighterPower(int fleet, bool withBonus) => _shipInfo.GetFighterPower(fleet, withBonus);\r
428 \r
429         public double GetFleetLineOfSights(int fleet) => _shipInfo.GetLineOfSights(fleet);\r
430 \r
431         public ShipStatus[] DamagedShipList => _shipInfo.GetDamagedShipList(_dockInfo);\r
432 \r
433         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
434 \r
435         public ItemStatus[] ItemList => _itemInfo.GetItemListWithOwner(ShipList);\r
436 \r
437         public AkashiTimer.RepairSpan[] GetAkashiTimers(int fleet) => _akashiTimer.GetTimers(fleet);\r
438 \r
439         public AkashiTimer.Notice[] GetAkashiTimerNotice() => _akashiTimer.GetNotice();\r
440 \r
441         public Achievement Achievement => _achievement;\r
442 \r
443         public BattleInfo Battle => _battleInfo;\r
444 \r
445         public ExMapInfo ExMap => _exMapInfo;\r
446 \r
447         public string MiscText => _miscTextInfo.Text;\r
448 \r
449         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
450         {\r
451             _logger.SetWriter(writer, nowFunc);\r
452         }\r
453 \r
454         public void SkipMaster()\r
455         {\r
456             _start = true;\r
457         }\r
458 \r
459         public void EnableLog(LogType type)\r
460         {\r
461             _logger.EnableLog(type);\r
462         }\r
463 \r
464         public int MaterialLogInterval\r
465         {\r
466             set { _logger.MaterialLogInterval = value; }\r
467         }\r
468 \r
469         public string LogOutputDir\r
470         {\r
471             set { _logger.OutputDir = value; }\r
472         }\r
473     }\r
474 \r
475     public class NameAndTimer\r
476     {\r
477         public string Name { get; set; }\r
478         public RingTimer Timer { get; set; }\r
479 \r
480         public NameAndTimer()\r
481         {\r
482             Timer = new RingTimer();\r
483         }\r
484     }\r
485 \r
486     public class RingTimer\r
487     {\r
488         private DateTime _endTime;\r
489         private readonly TimeSpan _spare;\r
490 \r
491         public TimeSpan Rest { get; private set; }\r
492 \r
493         public bool IsFinished => _endTime != DateTime.MinValue && Rest <= _spare;\r
494 \r
495         public bool NeedRing { get; set; }\r
496 \r
497         public RingTimer(int spare = 60)\r
498         {\r
499             _spare = TimeSpan.FromSeconds(spare);\r
500         }\r
501 \r
502         public void SetEndTime(double time)\r
503         {\r
504             SetEndTime((int)time == 0\r
505                 ? DateTime.MinValue\r
506                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
507         }\r
508 \r
509         public void SetEndTime(DateTime time)\r
510         {\r
511             _endTime = time;\r
512         }\r
513 \r
514         public void Update()\r
515         {\r
516             if (_endTime == DateTime.MinValue)\r
517             {\r
518                 Rest = TimeSpan.Zero;\r
519                 return;\r
520             }\r
521             var prev = Rest;\r
522             Rest = _endTime - DateTime.Now;\r
523             if (Rest < TimeSpan.Zero)\r
524                 Rest = TimeSpan.Zero;\r
525             if (prev > _spare && _spare >= Rest)\r
526                 NeedRing = true;\r
527         }\r
528     }\r
529 }