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