11from __future__ import absolute_import
22
3- import os
4- from sc2reader import utils
5- from sc2reader import readers
6- from sc2reader import data
7- from sc2reader import exceptions
8- from sc2reader .replay import Replay
9- from collections import defaultdict
3+ from sc2reader import factories
104
11- class SC2Reader (object ):
12- """
13- The primary interface to the sc2reader library. Acts as a configurable
14- factory for :class:`Replay` objects. Maintains a set of registered readers,
15- datapacks, and listeners with filterfuncs that allow the factory to apply
16- a replay specific context to each replay as it loads.
5+ # For backwards compatibility
6+ SC2Reader = factories .SC2Factory
177
18- #TODO: Include some examples here...
19-
20- See the specific functions below for details.
21-
22- :param True register_defaults: Automatically registers default readers
23- and datapacks. Only disable if you know what you are doing.
24-
25- :param True load_events: Enables parsing of game events. If you are do
26- not need this information setting to false will reduce replay load
27- time.
28-
29- :param True autoplay: Enables auto playing of replays after loading game
30- events. Playing events triggers enables registered listeners to add
31- new data features to replays. Option ignored if load_events is false.
32-
33- :param False load_map: Triggers downloading and parsing of map files
34- associated with replays as they are loaded. When false, only the map
35- url and name are available.
36-
37- :param False verbose: Causes many steps in the replay loading process
38- to produce more verbose output.
39-
40- :param string directory: Specifies a base directory to prepend to all paths
41- before attempting to load the replay.
42-
43- :param -1 depth: Indicates the maximum search depth when loading replays
44- from directories. -1 indicates no limit, 0 turns recursion off.
45-
46- :param list exclude: A list of directory names (not paths) to exclude when
47- performing recursive searches while loading replays from directories.
48-
49- :param False followlinks: Enables symlink following when recursing through
50- directories to load replay files.
51-
52- """
53-
54- default_options = dict (
55- # General use
56- verbose = False ,
57- debug = False ,
58-
59- # Related to the SC2Reader class only
60- register_defaults = True ,
61-
62- # Related to creating new replay objects
63- autoplay = True ,
64- load_events = True ,
65- load_map = False ,
66-
67- # Related to passing paths into load_replay(s)
68- directory = '' ,
69-
70- # Related to passing a directory to load_replays
71- depth = - 1 ,
72- exclude = [],
73- followlinks = False
74- )
75-
76- def __init__ (self , ** options ):
77- self .reset ()
78- self .configure (** options )
79-
80- if self .options .get ('register_defaults' ,None ):
81- self .register_defaults ()
82-
83-
84-
85- def configure (self , ** new_options ):
86- """
87- Update the factory settings with the specified overrides.
88-
89- See :class:`SC2Reader` for a list of available options.
90-
91- :param new_options: Option values to override current factory settings.
92- """
93- self .options .update (new_options )
94-
95- def reset (self ):
96- """
97- Resets the current factory to default settings and removes all
98- registered readers, datapacks, and listeners.
99- """
100- self .options = utils .AttributeDict (self .default_options )
101- self .registered_readers = defaultdict (list )
102- self .registered_datapacks = list ()
103- self .registered_listeners = defaultdict (list )
104-
105-
106- def load_replays (self , collection , options = None , ** new_options ):
107- """
108- Loads the specified collection of replays using the current factory
109- settings with specified overrides.
110-
111- :param collection: Either a directory path or a mixed collection of
112- directories, file paths, and open file objects.
113-
114- :param None options: When options are passed directly into the options
115- parameter the current factory settings are ignored and only the
116- specified options are used during replay load.
117-
118- :param new_options: Options values to override current factory settings
119- for the collection of replays to be loaded.
120-
121- :rtype: generator(:class:`Replay`)
122- """
123- options = options or utils .merged_dict (self .options , new_options )
124-
125- # Get the directory and hide it from nested calls
126- directory = options .get ('directory' ,'' )
127- if 'directory' in options : del options ['directory' ]
128-
129- if isinstance (collection , basestring ):
130- full_path = os .path .join (directory , collection )
131- for replay_path in utils .get_replay_files (full_path , ** options ):
132- with open (replay_path ) as replay_file :
133- try :
134- yield self .load_replay (replay_file , options = options )
135- except exceptions .MPQError as e :
136- print e
137-
138- else :
139- for replay_file in collection :
140- if isinstance (replay_file , basestring ):
141- full_path = os .path .join (directory , replay_file )
142- if os .path .isdir (full_path ):
143- for replay in self .load_replays (full_path , options = options ):
144- yield replay
145- else :
146- yield self .load_replay (full_path , options = options )
147-
148- else :
149- yield self .load_replay (replay_file , options = options )
150-
151- def load_replay (self , replay_file , options = None , ** new_options ):
152- """
153- Loads the specified replay using current factory settings with the
154- specified overrides.
155-
156- :param replay: An open file object or a path to a single file.
157-
158- :param None options: When options are passed directly into the options
159- parameter the current factory settings are ignored and only the
160- specified options are used during replay load.
161-
162- :param new_options: Options values to override current factory settings
163- while loading this replay.
164-
165- :rtype: :class:`Replay`
166- """
167- options = options or utils .merged_dict (self .options , new_options )
168- load_events = options .get ('load_events' ,True )
169- load_map = options .get ('load_map' ,False )
170- autoplay = options .get ('autoplay' ,True )
171-
172- if isinstance (replay_file , basestring ):
173- location = os .path .join (options .get ('directory' ,'' ), replay_file )
174- with open (location , 'rb' ) as replay_file :
175- return self .load_replay (replay_file , options = options )
176-
177- if options ['verbose' ]:
178- print replay_file .name
179-
180- replay = Replay (replay_file , ** options )
181-
182- for data_file in ('replay.initData' ,
183- 'replay.details' ,
184- 'replay.attributes.events' ,
185- 'replay.message.events' ,):
186- reader = self .get_reader (data_file , replay )
187- replay .read_data (data_file , reader )
188-
189- replay .load_details ()
190- replay .load_players ()
191-
192- if load_map :
193- replay .load_map ()
194-
195- if load_events :
196- for data_file in ('replay.game.events' ,):
197- reader = self .get_reader (data_file , replay )
198- replay .read_data (data_file , reader )
199-
200- replay .load_events (self .get_datapack (replay ))
201-
202- if autoplay :
203- replay .listeners = self .get_listeners (replay )
204- replay .start ()
205-
206- return replay
207-
208-
209- def get_reader (self , data_file , replay ):
210- for callback , reader in self .registered_readers [data_file ]:
211- if callback (replay ):
212- return reader
213-
214- def get_datapack (self , replay ):
215- for callback , datapack in self .registered_datapacks :
216- if callback (replay ):
217- return datapack
218- return None
219-
220- def get_listeners (self , replay ):
221- listeners = defaultdict (list )
222- for event_class in self .registered_listeners .keys ():
223- for callback , listener in self .registered_listeners [event_class ]:
224- if callback (replay ):
225- listeners [event_class ].append (listener )
226- return listeners
227-
228-
229- def register_listener (self , events , listener , filterfunc = lambda r : True ):
230- """
231- Allows you to specify event listeners for adding new features to the
232- :class:`Replay` objects on :meth:`~Replay.play`. sc2reader comes with a
233- small collection of :class:`Listener` classes that you can apply to your
234- replays as needed.
235-
236- Events are sent to listeners in registration order as they come up. By
237- specifying a parent class you can register a listener to a set of events
238- at once instead of listing them out individually. See the tutorials for
239- more information.
240-
241- :param events: A list of event classes you want sent to this listener.
242- Registration to a single event can be done by specifying a single
243- event class instead of a list. An isinstance() check is used so
244- you can catch sets of classes at once by supplying a parent class.
245-
246- :param listener: The :class:`Listener` object you want events sent to.
247-
248- :param filterfunc: A function that accepts a partially loaded
249- :class:`Replay` object as an argument and returns true if the
250- reader should be used on this replay.
251- """
252- try :
253- for event in events :
254- self .registered_listeners [event ].append ((filterfunc , listener ))
255- except TypeError :
256- self .registered_listeners [events ].append ((filterfunc , listener ))
257-
258- def register_reader (self , data_file , reader , filterfunc = lambda r : True ):
259- """
260- Allows you to specify your own reader for use when reading the data
261- files packed into the .SC2Replay archives. Datapacks are checked for
262- use with the supplied filterfunc in reverse registration order to give
263- user registered datapacks preference over factory default datapacks.
264-
265- Don't use this unless you know what you are doing.
266-
267- :param data_file: The full file name that you would like this reader to
268- parse.
269-
270- :param reader: The :class:`Reader` object you wish to use to read the
271- data file.
272-
273- :param filterfunc: A function that accepts a partially loaded
274- :class:`Replay` object as an argument and returns true if the
275- reader should be used on this replay.
276- """
277- self .registered_readers [data_file ].insert (0 ,(filterfunc , reader ))
278-
279- def register_datapack (self , datapack , filterfunc = lambda r : True ):
280- """
281- Allows you to specify your own datapacks for use when loading replays.
282- Datapacks are checked for use with the supplied filterfunc in reverse
283- registration order to give user registered datapacks preference over
284- factory default datapacks.
285-
286- This is how you would add mappings for your favorite custom map.
287-
288- :param datapack: A :class:`BaseData` object to use for mapping unit
289- types and ability codes to their corresponding classes.
290-
291- :param filterfunc: A function that accepts a partially loaded
292- :class:`Replay` object as an argument and returns true if the
293- datapack should be used on this replay.
294- """
295- self .registered_datapacks .insert (0 ,(filterfunc , datapack ))
296-
297-
298- def register_defaults (self ):
299- """Registers all factory default objects."""
300- self .register_default_readers ()
301- self .register_default_datapacks ()
302-
303- def register_default_readers (self ):
304- """Registers factory default readers."""
305- self .register_reader ('replay.details' , readers .DetailsReader_Base ())
306- self .register_reader ('replay.initData' , readers .InitDataReader_Base ())
307- self .register_reader ('replay.message.events' , readers .MessageEventsReader_Base ())
308- self .register_reader ('replay.attributes.events' , readers .AttributesEventsReader_Base (), lambda r : r .build < 17326 )
309- self .register_reader ('replay.attributes.events' , readers .AttributesEventsReader_17326 (), lambda r : r .build >= 17326 )
310- self .register_reader ('replay.game.events' , readers .GameEventsReader_Base (), lambda r : r .build < 16561 )
311- self .register_reader ('replay.game.events' , readers .GameEventsReader_16561 (), lambda r : 16561 <= r .build < 18574 )
312- self .register_reader ('replay.game.events' , readers .GameEventsReader_18574 (), lambda r : 18574 <= r .build < 19595 )
313- self .register_reader ('replay.game.events' , readers .GameEventsReader_19595 (), lambda r : 19595 <= r .build )
314-
315- def register_default_datapacks (self ):
316- """Registers factory default datapacks."""
317- self .register_datapack (data .Data_16561 , lambda r : 16561 <= r .build < 17326 )
318- self .register_datapack (data .Data_17326 , lambda r : 17326 <= r .build < 18317 )
319- self .register_datapack (data .Data_18317 , lambda r : 18317 <= r .build < 19595 )
320- self .register_datapack (data .Data_19595 , lambda r : 19595 <= r .build )
321-
322- __defaultSC2Reader = SC2Reader ()
8+ # Expose a nice module level interface
9+ __defaultSC2Reader = factories .SC2Factory ()
32310
32411register_datapack = __defaultSC2Reader .register_datapack
32512register_listener = __defaultSC2Reader .register_listener
@@ -329,8 +16,10 @@ def register_default_datapacks(self):
32916get_datapack = __defaultSC2Reader .get_datapack
33017get_reader = __defaultSC2Reader .get_reader
33118
332- load_replay = __defaultSC2Reader .load_replay
33319load_replays = __defaultSC2Reader .load_replays
20+ load_replay = __defaultSC2Reader .load_replay
21+ load_maps = __defaultSC2Reader .load_maps
22+ load_map = __defaultSC2Reader .load_map
33423
33524configure = __defaultSC2Reader .configure
33625reset = __defaultSC2Reader .reset
0 commit comments