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