-
Notifications
You must be signed in to change notification settings - Fork 9
/
logs_base.py
328 lines (272 loc) · 10 KB
/
logs_base.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
from c_player_classes import SPECS_LIST
import json
from bisect import bisect_left
import logs_fight_separator
import logs_get_time
import logs_player_spec
import logs_spells_list
import logs_units_guid
from h_debug import Loggers, running_time
PLAYER = "0x0"
TYPES = (str, bool, type(None))
LOGGER_REPORTS = Loggers.reports
TOTAL_DUMMY_SPEC = {
"spec": "Total",
"icon": "ability_hunter_readiness",
"name": "Total",
"class": "total",
}
def cache_wrap(func):
def cache_inner(self, s, f, *args, **kwargs):
slice_ID = f"{s}_{f}"
cached_data = self.CACHE[func.__name__]
for arg in args:
if not isinstance(arg, TYPES):
break
cached_data = cached_data[arg]
if slice_ID in cached_data:
return cached_data[slice_ID]
data = func(self, s, f, *args, **kwargs)
cached_data[slice_ID] = data
return data
return cache_inner
class THE_LOGS(
logs_fight_separator.Fights,
logs_spells_list.Spells,
logs_get_time.Timestamps,
):
@property
def ALL_GUIDS(self) -> dict[str, dict[str, str]]:
try:
return self._guids_all
except AttributeError:
pass
self._guids_data()
return self._guids_all
@property
def PLAYERS_GUIDS(self) -> dict[str, str]:
try:
return self._guids_players
except AttributeError:
pass
self._guids_data()
return self._guids_players
@property
def PLAYER_CLASSES(self) -> dict[str, str]:
try:
return self._guids_classes
except AttributeError:
pass
self._guids_data()
return self._guids_classes
@property
def PLAYERS_NAMES(self):
try:
return self._players_names
except AttributeError:
players = self.get_players_guids()
self._players_names = {v:k for k,v in players.items()}
return self._players_names
@property
def PLAYERS_PETS(self):
try:
return self._players_pets
except AttributeError:
self._players_pets = {
guid
for guid, p in self.ALL_GUIDS.items()
if p.get("master_guid", "").startswith(PLAYER)
}
return self._players_pets
@property
def FRIENDLY_IDS(self):
try:
return self._friendly_ids
except AttributeError:
d = {
guid[6:12]
for guid in self.PLAYERS_PETS
}
d.add("000000")
self._friendly_ids = d
return d
@property
def CLASSES_NAMES(self) -> dict[str, str]:
try:
return self._classes_names
except AttributeError:
self._classes_names = self.convert_dict_guids_to_names(self.PLAYER_CLASSES)
return self._classes_names
@property
def UNIT_ID_TO_NAME(self):
try:
return self._unit_id_to_name
except AttributeError:
d = {
guid[6:-6]: info["name"]
for guid, info in self.ALL_GUIDS.items()
}
d |= self.get_players_guids()
d["000000"] = "nil"
d["0x0000000000000000"] = "nil"
self._unit_id_to_name = d
return self._unit_id_to_name
@property
def CONTROLLED_UNITS(self):
try:
return self._controlled_units
except AttributeError:
self._controlled_units = {}
return self._controlled_units
def _guids_data(self):
try:
self._read_guids()
except FileNotFoundError:
self._redo_guids()
def _read_guids(self):
self._guids_all = self.relative_path("GUIDS_DATA.json").json()
self._guids_players = self.relative_path("PLAYERS_DATA.json").json()
self._guids_classes = self.relative_path("CLASSES_DATA.json").json()
@running_time
def _redo_guids(self):
parsed = logs_units_guid.guids_main(self.LOGS, self.ENCOUNTER_DATA)
if parsed['missing_owner']:
LOGGER_REPORTS.error(f"{self.NAME} | Missing owners: {parsed['missing_owner']}")
self._guids_all = parsed['everything']
self._guids_players = parsed['players']
self._guids_classes = parsed['classes']
to_write = (
("GUIDS_DATA.json", self._guids_all),
("PLAYERS_DATA.json", self._guids_players),
("CLASSES_DATA.json", self._guids_classes),
)
for file_name, data in to_write:
self.relative_path(file_name).json_write(data, indent=2)
def get_players_guids(self, whitelist_guids=None, whitelist_names=None):
players = self.PLAYERS_GUIDS
if whitelist_guids is not None:
return {k:v for k,v in players.items() if k in whitelist_guids}
elif whitelist_names is not None:
return {k:v for k,v in players.items() if v in whitelist_names}
else:
return players
@cache_wrap
def get_players_specs_in_segments(self, s, f) -> dict[str, int]:
'''specs = {guid: spec_index}'''
logs_slice = self.LOGS[s:f]
return logs_player_spec.get_specs(logs_slice, self.PLAYERS_GUIDS, self.PLAYER_CLASSES)
def get_slice_spec_info(self, s, f):
new_specs: dict[str, tuple[str, str]] = {}
specs = self.get_players_specs_in_segments(s, f)
for unit_guid, spec_index in specs.items():
spec_data = SPECS_LIST[spec_index]
new_specs[unit_guid] = {
"spec": spec_data.name,
"icon": spec_data.icon,
"name": self.guid_to_name(unit_guid),
"class": self.PLAYER_CLASSES[unit_guid],
}
new_specs["Total"] = TOTAL_DUMMY_SPEC
return new_specs
def name_to_guid(self, name: str) -> str:
if name.startswith("0x"):
return name
if name in self.PLAYERS_NAMES:
return self.PLAYERS_NAMES[name]
for guid, data in self.ALL_GUIDS.items():
if data['name'] == name:
return guid
return None
def guid_to_name(self, guid: str):
name = self.UNIT_ID_TO_NAME.get(guid)
if not name:
name = self.ALL_GUIDS.get(guid, {}).get("name", "Unknown")
return name
def sort_data_guids_by_name(self, data: dict):
return dict(sorted(data.items(), key=lambda x: self.guid_to_name(x[0])))
def convert_dict_guids_to_names(self, data: dict):
return {
self.guid_to_name(guid): value
for guid, value in data.items()
}
def get_master_guid(self, guid: str):
master_guid = self.ALL_GUIDS[guid].get('master_guid')
if not master_guid:
return guid
return self.ALL_GUIDS.get(master_guid, {}).get('master_guid', master_guid)
def get_pet_name(self, guid):
is_pet = guid != self.get_master_guid(guid)
if is_pet:
return self.guid_to_name(guid)
return None
def get_units_controlled_by(self, master_guid: str):
if not master_guid.startswith("0x"):
master_guid = self.name_to_guid(master_guid)
if master_guid in self.CONTROLLED_UNITS:
return self.CONTROLLED_UNITS[master_guid]
controlled_units = {
guid
for guid, p in self.ALL_GUIDS.items()
if p.get("master_guid") == master_guid or master_guid in guid
}
controlled_units.add(master_guid)
self.CONTROLLED_UNITS[master_guid] = controlled_units
return controlled_units
def get_players_and_pets_guids(self):
try:
return self.PLAYERS_AND_PETS
except AttributeError:
players = set(self.get_players_guids())
self.PLAYERS_AND_PETS = players | self.PLAYERS_PETS
return self.PLAYERS_AND_PETS
def add_missing_players(self, data, default=0, players=None):
if players is None:
players = self.get_players_guids()
for guid in players:
if guid not in data:
data[guid] = default
return data
def get_classes_with_names_json(self):
try:
return self._classes_with_names_json
except AttributeError:
self._classes_with_names_json = json.dumps(self.CLASSES_NAMES)
return self._classes_with_names_json
def find_index(self, line_index: int, shift: int=0, slice_end=False):
if line_index is None:
if slice_end:
return self.TIMESTAMPS[-1]
return 0
if not shift:
shift = 0
shifted = bisect_left(self.TIMESTAMPS, line_index) + shift
return max(shifted, 0)
def find_shifted_log_line(self, line_index: int, shift: int):
if not line_index or not shift:
return line_index
new_index = self.find_index(line_index, shift)
return self.TIMESTAMPS[new_index]
def find_sec_from_start(self, s):
return self.get_timedelta_seconds(self.LOGS[0], self.LOGS[s])
def precise_shift(self, from_index: int, shift_seconds: int):
if not shift_seconds:
return from_index
first_line = self.LOGS[from_index]
shifted_index = shift_seconds + int(self.find_sec_from_start(from_index))
s = self.TIMESTAMPS[shifted_index-1]
f = self.TIMESTAMPS[shifted_index+1]
for i, current_line in enumerate(self.LOGS[s:f]):
td = self.get_timedelta(first_line, current_line).total_seconds()
if td > shift_seconds:
return s+i
return self.TIMESTAMPS[shifted_index]
def get_all_guids(self):
return self.ALL_GUIDS
def get_classes(self):
return self.PLAYER_CLASSES
def get_enc_data(self):
return self.ENCOUNTER_DATA
def get_timestamp(self):
return self.TIMESTAMPS
def get_spells(self):
return self.SPELLS