1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
1 |
|
import six |
4
|
1 |
|
import sys |
5
|
|
|
|
6
|
1 |
|
from .exceptions import MissionParsingError |
7
|
1 |
|
from .sections.main import MainSectionParser |
8
|
1 |
|
from .sections.season import SeasonSectionParser |
9
|
1 |
|
from .sections.weather import WeatherSectionParser |
10
|
1 |
|
from .sections.respawn_time import RespawnTimeSectionParser |
11
|
1 |
|
from .sections.mds import MDSSectionParser |
12
|
1 |
|
from .sections.mds_scouts import MDSScoutsSectionParser |
13
|
1 |
|
from .sections.chiefs import ChiefsSectionParser |
14
|
1 |
|
from .sections.chief_road import ChiefRoadSectionParser |
15
|
1 |
|
from .sections.nstationary import NStationarySectionParser |
16
|
1 |
|
from .sections.buildings import BuildingsSectionParser |
17
|
1 |
|
from .sections.target import TargetSectionParser |
18
|
1 |
|
from .sections.born_place import BornPlaceSectionParser |
19
|
1 |
|
from .sections.born_place_aircrafts import BornPlaceAircraftsSectionParser |
20
|
1 |
|
from .sections.born_place_air_forces import BornPlaceAirForcesSectionParser |
21
|
1 |
|
from .sections.static_camera import StaticCameraSectionParser |
22
|
1 |
|
from .sections.front_marker import FrontMarkerSectionParser |
23
|
1 |
|
from .sections.rocket import RocketSectionParser |
24
|
1 |
|
from .sections.wing import WingSectionParser |
25
|
1 |
|
from .sections.flight_info import FlightInfoSectionParser |
26
|
1 |
|
from .sections.flight_route import FlightRouteSectionParser |
27
|
1 |
|
from .utils import move_if_present, set_if_present, strip_comments |
28
|
|
|
|
29
|
|
|
|
30
|
1 |
|
class MissionParser(object): |
31
|
|
|
""" |
32
|
|
|
Parses a whole mission file. |
33
|
|
|
View :ref:`detailed description <file-parser>`. |
34
|
|
|
""" |
35
|
|
|
|
36
|
1 |
|
def __init__(self): |
37
|
1 |
|
self.parsers = [ |
38
|
|
|
MainSectionParser(), |
39
|
|
|
SeasonSectionParser(), |
40
|
|
|
WeatherSectionParser(), |
41
|
|
|
RespawnTimeSectionParser(), |
42
|
|
|
MDSSectionParser(), |
43
|
|
|
MDSScoutsSectionParser(), |
44
|
|
|
ChiefsSectionParser(), |
45
|
|
|
ChiefRoadSectionParser(), |
46
|
|
|
NStationarySectionParser(), |
47
|
|
|
BuildingsSectionParser(), |
48
|
|
|
TargetSectionParser(), |
49
|
|
|
BornPlaceSectionParser(), |
50
|
|
|
BornPlaceAircraftsSectionParser(), |
51
|
|
|
BornPlaceAirForcesSectionParser(), |
52
|
|
|
StaticCameraSectionParser(), |
53
|
|
|
FrontMarkerSectionParser(), |
54
|
|
|
RocketSectionParser(), |
55
|
|
|
WingSectionParser(), |
56
|
|
|
FlightRouteSectionParser(), |
57
|
|
|
] |
58
|
1 |
|
self.flight_info_parser = FlightInfoSectionParser() |
59
|
|
|
|
60
|
1 |
|
def parse(self, mission): |
61
|
1 |
|
if isinstance(mission, six.string_types): |
62
|
1 |
|
with open(mission, 'r') as f: |
63
|
1 |
|
return self.parse_stream(f) |
64
|
|
|
else: |
65
|
1 |
|
return self.parse_stream(mission) |
66
|
|
|
|
67
|
1 |
|
def parse_stream(self, sequence): |
68
|
1 |
|
self._current_parser = None |
69
|
1 |
|
self.data = {} |
70
|
|
|
|
71
|
1 |
|
for i, line in enumerate(sequence): |
72
|
1 |
|
line = strip_comments(line) |
73
|
1 |
|
if self.is_section_name(line): |
74
|
1 |
|
self._finalize_current_parser() |
75
|
1 |
|
section_name = self.get_section_name(line) |
76
|
1 |
|
self._current_parser = self._get_parser(section_name) |
77
|
1 |
|
elif self._current_parser: |
78
|
1 |
|
self._try_to_parse_line(i, line) |
79
|
|
|
|
80
|
1 |
|
self._finalize_current_parser() |
81
|
1 |
|
return self._clean() |
82
|
|
|
|
83
|
1 |
|
@staticmethod |
84
|
|
|
def is_section_name(line): |
85
|
1 |
|
return line.startswith('[') and line.endswith(']') |
86
|
|
|
|
87
|
1 |
|
@staticmethod |
88
|
|
|
def get_section_name(line): |
89
|
1 |
|
return line.strip('[]') |
90
|
|
|
|
91
|
1 |
|
def _get_parser(self, section_name): |
92
|
1 |
|
parser = self.flight_info_parser |
93
|
1 |
|
flights = self.data.get('flights') |
94
|
|
|
|
95
|
1 |
|
if flights is not None and parser.start(section_name): |
96
|
1 |
|
return parser |
97
|
|
|
|
98
|
1 |
|
for parser in self.parsers: |
99
|
1 |
|
if parser.start(section_name): |
100
|
1 |
|
return parser |
101
|
|
|
|
102
|
1 |
|
return None |
103
|
|
|
|
104
|
1 |
|
def _finalize_current_parser(self): |
105
|
1 |
|
if not self._current_parser: |
106
|
1 |
|
return |
107
|
1 |
|
try: |
108
|
1 |
|
data = self._current_parser.stop() |
109
|
1 |
|
except Exception: |
110
|
1 |
|
error_type, original_msg, traceback = sys.exc_info() |
111
|
1 |
|
msg = ( |
112
|
|
|
"{0} during finalization of \"{1}\": {2}" |
113
|
|
|
.format(error_type.__name__, |
114
|
|
|
self._current_parser.__class__.__name__, |
115
|
|
|
original_msg)) |
116
|
1 |
|
self._raise_error(msg, traceback) |
117
|
|
|
else: |
118
|
1 |
|
self.data.update(data) |
119
|
|
|
finally: |
120
|
1 |
|
self._current_parser = None |
121
|
|
|
|
122
|
1 |
|
def _try_to_parse_line(self, line_number, line): |
123
|
1 |
|
try: |
124
|
1 |
|
self._current_parser.parse_line(line) |
125
|
1 |
|
except Exception: |
126
|
1 |
|
error_type, original_msg, traceback = sys.exc_info() |
127
|
1 |
|
msg = ( |
128
|
|
|
"{0} in line #{1} (\"{2}\"): {3}" |
129
|
|
|
.format(error_type.__name__, line_number, line, original_msg)) |
130
|
1 |
|
self._raise_error(msg, traceback) |
131
|
|
|
|
132
|
1 |
|
@staticmethod |
133
|
|
|
def _raise_error(message, traceback): |
134
|
1 |
|
error = MissionParsingError(message) |
135
|
1 |
|
six.reraise(MissionParsingError, error, traceback) |
136
|
|
|
|
137
|
1 |
|
def _clean(self): |
138
|
1 |
|
result = {} |
139
|
|
|
|
140
|
1 |
|
move_if_present(result, self.data, 'location_loader') |
141
|
1 |
|
move_if_present(result, self.data, 'player') |
142
|
1 |
|
move_if_present(result, self.data, 'targets') |
143
|
|
|
|
144
|
1 |
|
set_if_present(result, 'conditions', self._get_conditions()) |
145
|
1 |
|
set_if_present(result, 'objects', self._get_objects()) |
146
|
|
|
|
147
|
1 |
|
return result |
148
|
|
|
|
149
|
1 |
|
def _get_conditions(self): |
150
|
1 |
|
result = {} |
151
|
|
|
|
152
|
1 |
|
set_if_present(result, 'time_info', self._get_time_info()) |
153
|
1 |
|
set_if_present(result, 'meteorology', self._get_meteorology()) |
154
|
1 |
|
set_if_present(result, 'scouting', self._get_scouting()) |
155
|
|
|
|
156
|
1 |
|
move_if_present(result, self.data, 'respawn_time') |
157
|
|
|
|
158
|
1 |
|
if 'conditions' in self.data: |
159
|
1 |
|
conditions = self.data['conditions'] |
160
|
|
|
|
161
|
1 |
|
move_if_present(result, conditions, 'radar') |
162
|
1 |
|
move_if_present(result, conditions, 'communication') |
163
|
1 |
|
move_if_present(result, conditions, 'home_bases') |
164
|
1 |
|
move_if_present(result, conditions, 'crater_visibility_muptipliers') |
165
|
|
|
|
166
|
1 |
|
return result |
167
|
|
|
|
168
|
1 |
|
def _get_time_info(self): |
169
|
1 |
|
result = {} |
170
|
|
|
|
171
|
1 |
|
move_if_present(result, self.data, 'date') |
172
|
1 |
|
if 'time' in self.data: |
173
|
1 |
|
result.update({ |
174
|
|
|
'time': self.data['time']['value'], |
175
|
|
|
'is_fixed': self.data['time']['is_fixed'], |
176
|
|
|
}) |
177
|
|
|
|
178
|
1 |
|
return result |
179
|
|
|
|
180
|
1 |
|
def _get_meteorology(self): |
181
|
1 |
|
result = {} |
182
|
|
|
|
183
|
1 |
|
move_if_present(result, self.data, 'weather', 'weather_conditions') |
184
|
1 |
|
move_if_present(result, self.data, 'cloud_base') |
185
|
|
|
|
186
|
1 |
|
if 'weather' in self.data: |
187
|
1 |
|
result.update(self.data.pop('weather')) |
188
|
|
|
|
189
|
1 |
|
return result |
190
|
|
|
|
191
|
1 |
|
def _get_scouting(self): |
192
|
1 |
|
result = {} |
193
|
|
|
|
194
|
1 |
|
try: |
195
|
1 |
|
conditions = self.data['conditions'].pop('scouting') |
196
|
1 |
|
result.update(conditions) |
197
|
1 |
|
except KeyError: |
198
|
|
|
pass |
199
|
|
|
|
200
|
1 |
|
keys = filter( |
201
|
|
|
lambda x: x.startswith(MDSScoutsSectionParser.output_prefix), |
202
|
|
|
self.data.keys() |
203
|
|
|
) |
204
|
1 |
|
scouts = { |
205
|
|
|
self.data[key]['belligerent']: self.data[key]['aircrafts'] |
206
|
|
|
for key in keys |
207
|
|
|
} |
208
|
1 |
|
set_if_present(result, 'scouts', scouts) |
209
|
|
|
|
210
|
1 |
|
return result |
211
|
|
|
|
212
|
1 |
|
def _get_objects(self): |
213
|
1 |
|
result = {} |
214
|
|
|
|
215
|
1 |
|
set_if_present(result, 'moving_units', self._get_moving_units()) |
216
|
1 |
|
set_if_present(result, 'flights', self._get_flights()) |
217
|
1 |
|
set_if_present(result, 'home_bases', self._get_home_bases()) |
218
|
|
|
|
219
|
1 |
|
move_if_present(result, self.data, 'stationary') |
220
|
1 |
|
move_if_present(result, self.data, 'buildings') |
221
|
1 |
|
move_if_present(result, self.data, 'cameras') |
222
|
1 |
|
move_if_present(result, self.data, 'markers') |
223
|
1 |
|
move_if_present(result, self.data, 'rockets') |
224
|
|
|
|
225
|
1 |
|
return result |
226
|
|
|
|
227
|
1 |
|
def _get_moving_units(self): |
228
|
1 |
|
units = self.data.pop('moving_units', []) |
229
|
1 |
|
for unit in units: |
230
|
1 |
|
key = "{}{}".format(ChiefRoadSectionParser.output_prefix, unit['id']) |
231
|
1 |
|
unit['route'] = self.data.pop(key, []) |
232
|
1 |
|
return units |
233
|
|
|
|
234
|
1 |
|
def _get_flights(self): |
235
|
1 |
|
keys = self.data.pop('flights', []) |
236
|
1 |
|
flights = [self.data.pop(key) for key in keys if key in self.data] |
237
|
1 |
|
for flight in flights: |
238
|
1 |
|
key = "{}{}".format(FlightRouteSectionParser.output_prefix, flight['id']) |
239
|
1 |
|
flight['route'] = self.data.pop(key, []) |
240
|
1 |
|
return flights |
241
|
|
|
|
242
|
1 |
|
def _get_home_bases(self): |
243
|
1 |
|
home_bases = self.data.pop('home_bases', []) |
244
|
1 |
|
for i, home_base in enumerate(home_bases): |
245
|
1 |
|
key = "{}{}".format(BornPlaceAircraftsSectionParser.output_prefix, i) |
246
|
1 |
|
home_base['spawning']['aircraft_limitations']['allowed_aircrafts'] = self.data.pop(key, []) |
247
|
|
|
|
248
|
1 |
|
key = "{}{}".format(BornPlaceAirForcesSectionParser.output_prefix, i) |
249
|
1 |
|
home_base['spawning']['allowed_air_forces'] = self.data.pop(key, []) |
250
|
|
|
return home_bases |
251
|
|
|
|