OSDN Git Service

ファイルのヘッダをApache Licenseのものにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 \r
19 namespace KancolleSniffer\r
20 {\r
21     public class Sniffer\r
22     {\r
23         private bool _start;\r
24         private readonly ItemInfo _itemInfo = new ItemInfo();\r
25         private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
26         private readonly QuestInfo _questInfo = new QuestInfo();\r
27         private readonly MissionInfo _missionInfo = new MissionInfo();\r
28         private readonly ShipInfo _shipInfo;\r
29         private readonly ConditionTimer _conditionTimer;\r
30         private readonly DockInfo _dockInfo;\r
31         private readonly AkashiTimer _akashiTimer;\r
32         private readonly Achievement _achievement = new Achievement();\r
33         private readonly BattleInfo _battleInfo;\r
34         private readonly Logger _logger;\r
35         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
36         private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
37         private readonly Status _status = new Status();\r
38         private bool _saveState;\r
39         private readonly List<IHaveState> _haveState;\r
40 \r
41         [Flags]\r
42         public enum Update\r
43         {\r
44             None = 0,\r
45             Error = 1 << 0,\r
46             Start = 1 << 1,\r
47             Item = 1 << 2,\r
48             Ship = 1 << 3,\r
49             Timer = 1 << 4,\r
50             NDock = 1 << 5,\r
51             Mission = 1 << 6,\r
52             QuestList = 1 << 7,\r
53             Battle = 1 << 8,\r
54             All = (1 << 9) - 1\r
55         }\r
56 \r
57         public Sniffer()\r
58         {\r
59             _shipInfo = new ShipInfo(_itemInfo);\r
60             _conditionTimer = new ConditionTimer(_shipInfo);\r
61             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
62             _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo);\r
63             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
64             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
65             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
66         }\r
67 \r
68         private void SaveState()\r
69         {\r
70             if (!_saveState)\r
71                 return;\r
72             if (!_haveState.Any(x => x.NeedSave))\r
73                 return;\r
74             foreach (var x in _haveState)\r
75                 x.SaveState(_status);\r
76             _status.Save();\r
77         }\r
78 \r
79         public void LoadState()\r
80         {\r
81             _status.Load();\r
82             foreach (var x in _haveState)\r
83                 x.LoadState(_status);\r
84             _saveState = true;\r
85         }\r
86 \r
87         public Update Sniff(string url, string request, dynamic json)\r
88         {\r
89             if (!json.api_result())\r
90                 return Update.Error;\r
91             var r = (int)json.api_result;\r
92             if (r != 1)\r
93                 return r == 201 ? Update.None : 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             _shipInfo.ClearBadlyDamagedShips();\r
131             _conditionTimer.CalcRegenTime();\r
132             _missionInfo.InspectDeck(data.api_deck_port);\r
133             _dockInfo.InspectNDock(data.api_ndock);\r
134             _akashiTimer.Port();\r
135             _achievement.InspectBasic(data.api_basic);\r
136             if (data.api_parallel_quest_count()) // 昔のログにはないので\r
137                 _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
138             _battleInfo.CleanupResult();\r
139             _battleInfo.InBattle = 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.CheckFleet();\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.CheckFleet();\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.CheckFleet();\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.CheckFleet();\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.CheckFleet();\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             if (url.EndsWith("api_get_member/preset_deck"))\r
225             {\r
226                 _shipInfo.InspectPresetDeck(data);\r
227                 return Update.None;\r
228             }\r
229             return Update.None;\r
230         }\r
231 \r
232         private Update ApiKousyou(string url, string request, dynamic data)\r
233         {\r
234             if (url.EndsWith("api_req_kousyou/createitem"))\r
235             {\r
236                 _itemInfo.InspectCreateItem(data);\r
237                 _materialInfo.InspectCreateIem(data);\r
238                 _logger.InspectCreateItem(request, data);\r
239                 return Update.Item;\r
240             }\r
241             if (url.EndsWith("api_req_kousyou/getship"))\r
242             {\r
243                 _itemInfo.InspectGetShip(data);\r
244                 _shipInfo.InspectShip(data);\r
245                 _dockInfo.InspectKDock(data.api_kdock);\r
246                 _conditionTimer.CheckCond();\r
247                 return Update.Item | Update.Timer;\r
248             }\r
249             if (url.EndsWith("api_req_kousyou/destroyship"))\r
250             {\r
251                 _shipInfo.InspectDestroyShip(request, data);\r
252                 _materialInfo.InspectDestroyShip(data);\r
253                 _conditionTimer.CheckCond();\r
254                 _akashiTimer.CheckFleet();\r
255                 return Update.Item | Update.Ship;\r
256             }\r
257             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
258             {\r
259                 _itemInfo.InspectDestroyItem(request, data);\r
260                 _materialInfo.InspectDestroyItem(data);\r
261                 return Update.Item;\r
262             }\r
263             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
264             {\r
265                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
266                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
267                 _itemInfo.InspectRemodelSlot(data);\r
268                 _materialInfo.InspectRemodelSlot(data);\r
269                 return Update.Item;\r
270             }\r
271             if (url.EndsWith("api_req_kousyou/createship"))\r
272             {\r
273                 _logger.InspectCreateShip(request);\r
274                 return Update.None;\r
275             }\r
276             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
277             {\r
278                 _dockInfo.InspectCreateShipSpeedChange(request);\r
279                 return Update.Timer;\r
280             }\r
281             return Update.None;\r
282         }\r
283 \r
284         private Update ApiBattle(string url, string request, dynamic data)\r
285         {\r
286             if (IsNormalBattleAPI(url))\r
287             {\r
288                 _battleInfo.InspectBattle(data);\r
289                 _logger.InspectBattle(data);\r
290                 return Update.Ship | Update.Battle;\r
291             }\r
292             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
293             {\r
294                 if (url.EndsWith("/battle"))\r
295                 {\r
296                     _shipInfo.InspectMapStart(request); // 演習を出撃中とみなす\r
297                     _conditionTimer.InvalidateCond();\r
298                     _miscTextInfo.ClearFlag = true;\r
299                 }\r
300                 _battleInfo.InspectBattle(data);\r
301                 return Update.Ship | Update.Battle | Update.Timer;\r
302             }\r
303             if (url.EndsWith("api_req_sortie/battleresult"))\r
304             {\r
305                 _battleInfo.InspectBattleResult(data);\r
306                 _exMapInfo.InspectBattleResult(data);\r
307                 _logger.InspectBattleResult(data);\r
308                 return Update.Ship;\r
309             }\r
310             if (url.EndsWith("api_req_practice/battle_result"))\r
311             {\r
312                 _battleInfo.InspectPracticeResult(data);\r
313                 return Update.Ship;\r
314             }\r
315             if (IsCombinedBattleAPI(url))\r
316             {\r
317                 _battleInfo.InspectCombinedBattle(data, url.EndsWith("battle_water"));\r
318                 _logger.InspectBattle(data);\r
319                 return Update.Ship | Update.Battle;\r
320             }\r
321             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
322             {\r
323                 _battleInfo.InspectCombinedBattleResult(data);\r
324                 _logger.InspectBattleResult(data);\r
325                 return Update.Ship;\r
326             }\r
327             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
328             {\r
329                 _battleInfo.CauseCombinedBattleEscape();\r
330                 return Update.Ship;\r
331             }\r
332             return Update.None;\r
333         }\r
334 \r
335         private bool IsNormalBattleAPI(string url)\r
336         {\r
337             return url.EndsWith("api_req_sortie/battle") ||\r
338                    url.EndsWith("api_req_sortie/airbattle") ||\r
339                    url.EndsWith("api_req_battle_midnight/battle") ||\r
340                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
341         }\r
342 \r
343         private bool IsCombinedBattleAPI(string url)\r
344         {\r
345             return url.EndsWith("api_req_combined_battle/battle") ||\r
346                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
347                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
348                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
349                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
350         }\r
351 \r
352         private Update ApiOthers(string url, string request, dynamic data)\r
353         {\r
354             if (url.EndsWith("api_req_hensei/change"))\r
355             {\r
356                 _shipInfo.InspectChange(request);\r
357                 _akashiTimer.InspectChange(request);\r
358                 return Update.Ship;\r
359             }\r
360             if (url.EndsWith("api_req_hensei/preset_select"))\r
361             {\r
362                 _shipInfo.InspectDeck(new[] {data});\r
363                 _akashiTimer.CheckFleet();\r
364                 return Update.Ship;\r
365             }\r
366             if (url.EndsWith("api_req_hensei/preset_register"))\r
367             {\r
368                 _shipInfo.InspectPresetRegister(data);\r
369                 return Update.None;\r
370             }\r
371             if (url.EndsWith("api_req_hensei/preset_delete"))\r
372             {\r
373                 _shipInfo.InspectPresetDelete(request);\r
374                 return Update.Timer;\r
375             }\r
376             if (url.EndsWith("api_req_hensei/combined"))\r
377             {\r
378                 _shipInfo.InspectCombined(request);\r
379                 return Update.Ship;\r
380             }\r
381             if (url.EndsWith("api_req_hokyu/charge"))\r
382             {\r
383                 _shipInfo.InspectCharge(data);\r
384                 _materialInfo.InspectCharge(data);\r
385                 return Update.Item | Update.Ship;\r
386             }\r
387             if (url.EndsWith("api_req_kaisou/powerup"))\r
388             {\r
389                 _shipInfo.InspectPowerup(request, data);\r
390                 _conditionTimer.CheckCond();\r
391                 _akashiTimer.CheckFleet();\r
392                 return Update.Item | Update.Ship;\r
393             }\r
394             if (url.EndsWith("api_req_kaisou/slot_exchange_index"))\r
395             {\r
396                 _shipInfo.InspectSlotExchange(request, data);\r
397                 return Update.Ship;\r
398             }\r
399             if (url.EndsWith("api_req_nyukyo/start"))\r
400             {\r
401                 _dockInfo.InspectNyukyo(request);\r
402                 _conditionTimer.CheckCond();\r
403                 _akashiTimer.CheckFleet();\r
404                 return Update.Item | Update.Ship;\r
405             }\r
406             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
407             {\r
408                 _dockInfo.InspectSpeedChange(request);\r
409                 _conditionTimer.CheckCond();\r
410                 return Update.NDock | Update.Timer | Update.Ship;\r
411             }\r
412             if (url.EndsWith("api_req_map/start"))\r
413             {\r
414                 _shipInfo.InspectMapStart(request); // 出撃中判定が必要なので_conditionTimerより前\r
415                 _conditionTimer.InvalidateCond();\r
416                 _exMapInfo.InspectMapStart(data);\r
417                 _logger.InspectMapStart(data);\r
418                 _miscTextInfo.ClearFlag = true;\r
419                 return Update.Timer | Update.Ship;\r
420             }\r
421             if (url.EndsWith("api_req_map/next"))\r
422             {\r
423                 _exMapInfo.InspectMapNext(data);\r
424                 _logger.InspectMapNext(data);\r
425                 return Update.None;\r
426             }\r
427             if (url.EndsWith("api_req_mission/result"))\r
428             {\r
429                 _materialInfo.InspectMissionResult(data);\r
430                 _logger.InspectMissionResult(data);\r
431                 return Update.Item;\r
432             }\r
433             return Update.None;\r
434         }\r
435 \r
436         public NameAndTimer[] NDock => _dockInfo.NDock;\r
437 \r
438         public RingTimer[] KDock => _dockInfo.KDock;\r
439 \r
440         public ItemInfo Item => _itemInfo;\r
441 \r
442         public MaterialInfo Material => _materialInfo;\r
443 \r
444         public QuestStatus[] Quests => _questInfo.Quests;\r
445 \r
446         public NameAndTimer[] Missions => _missionInfo.Missions;\r
447 \r
448         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
449 \r
450         public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
451 \r
452         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
453 \r
454         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
455 \r
456         public int CombinedFleetType => _shipInfo.CombinedFleetType;\r
457 \r
458         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
459 \r
460         public int[] GetFighterPower(int fleet) => _shipInfo.GetFighterPower(fleet);\r
461 \r
462         public double GetFleetLineOfSights(int fleet) => _shipInfo.GetLineOfSights(fleet);\r
463 \r
464         public ShipStatus[] DamagedShipList => _shipInfo.GetDamagedShipList(_dockInfo);\r
465 \r
466         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
467 \r
468         public string[] BadlyDamagedShips => _shipInfo.BadlyDamagedShips;\r
469 \r
470         public ItemStatus[] ItemList => _itemInfo.GetItemListWithOwner(ShipList);\r
471 \r
472         public AkashiTimer AkashiTimer => _akashiTimer;\r
473 \r
474         public Achievement Achievement => _achievement;\r
475 \r
476         public BattleInfo Battle => _battleInfo;\r
477 \r
478         public ExMapInfo ExMap => _exMapInfo;\r
479 \r
480         public string MiscText => _miscTextInfo.Text;\r
481 \r
482         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
483         {\r
484             _logger.SetWriter(writer, nowFunc);\r
485         }\r
486 \r
487         public void SkipMaster()\r
488         {\r
489             _start = true;\r
490         }\r
491 \r
492         public void EnableLog(LogType type)\r
493         {\r
494             _logger.EnableLog(type);\r
495         }\r
496 \r
497         public int MaterialLogInterval\r
498         {\r
499             set { _logger.MaterialLogInterval = value; }\r
500         }\r
501 \r
502         public string LogOutputDir\r
503         {\r
504             set { _logger.OutputDir = value; }\r
505         }\r
506     }\r
507 \r
508     public class NameAndTimer\r
509     {\r
510         public string Name { get; set; }\r
511         public RingTimer Timer { get; set; }\r
512 \r
513         public NameAndTimer()\r
514         {\r
515             Timer = new RingTimer();\r
516         }\r
517     }\r
518 \r
519     public class RingTimer\r
520     {\r
521         private readonly TimeSpan _spare;\r
522 \r
523         public TimeSpan Rest { get; private set; }\r
524 \r
525         public bool IsFinished => EndTime != DateTime.MinValue && Rest <= _spare;\r
526 \r
527         public DateTime EndTime { get; private set; }\r
528 \r
529         public bool NeedRing { get; set; }\r
530 \r
531         public RingTimer(int spare = 60)\r
532         {\r
533             _spare = TimeSpan.FromSeconds(spare);\r
534         }\r
535 \r
536         public void SetEndTime(double time)\r
537         {\r
538             SetEndTime((int)time == 0\r
539                 ? DateTime.MinValue\r
540                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
541         }\r
542 \r
543         public void SetEndTime(DateTime time)\r
544         {\r
545             EndTime = time;\r
546         }\r
547 \r
548         public void Update()\r
549         {\r
550             if (EndTime == DateTime.MinValue)\r
551             {\r
552                 Rest = TimeSpan.Zero;\r
553                 return;\r
554             }\r
555             var prev = Rest;\r
556             Rest = EndTime - DateTime.Now;\r
557             if (Rest < TimeSpan.Zero)\r
558                 Rest = TimeSpan.Zero;\r
559             if (prev > _spare && _spare >= Rest)\r
560                 NeedRing = true;\r
561         }\r
562     }\r
563 }