Loading#

Loading a Replay#

When you instantiate a Replay, it doesn’t have replay data, know the username or user id of who played it, know mods it was played with, or have very much information about itself at all.

Some Replays like ReplayMap have a little more information, because you passed it explicitly. For example, a ReplayMap knows the map id and user id of the replay (as well as the mods if passed), and a ReplayID knows the id of its replay.

We can illustrate this by trying to access these attributes of replays directly after instantiation:

r_path = ReplayPath("/path/to/your/osr.osr")
r_map = ReplayMap(221777, 2757689)
r_id = ReplayID(2177560145)

print(r_path.path) # "/path/to/your/osr.osr"
print(r_path.replay_data) # None
print(r_path.map_id) # None
print(r_path.user_id) # None
print(r_path.replay_id) # None
print(r_path.mods) # None

print(r_map.replay_data) # None
print(r_map.map_id) # 221777
print(r_map.user_id) # 2757689
print(r_map.replay_id) # None
print(r_map.mods) # None

print(r_id.replay_data) # None
print(r_id.map_id) # None
print(r_id.user_id) # None
print(r_id.replay_id) # 2177560145
print(r_id.mods) # None

As you can see, not many attributes are available to us. However, we can retrieve more information about a replay by “loading” it. A replay is called “unloaded” if it hasn’t been loaded yet, and “loaded” if it has.

To load a replay, call cg.load() on it. Once the replay is loaded, most of these attributes will be filled with the proper value:

r_map = ReplayMap(221777, 2757689)
cg.load(r_map)

# now we can access the real values of these attributes

print(len(r_map.replay_data)) # 26614
print(r_map.map_id) # 221777
print(r_map.user_id) # 2757689
print(r_map.replay_id) # 2832574010
print(r_map.mods) # Mod.HDHR

Warning

Circleguard usually requests information about the replay from the api in order to fill in the gaps, so a call to cg.load() could take considerable (>100ms) time as it involves network requests.

Depending on the replay class, some attributes may never be available due to api limitations or other issues. For instance, the api doesn’t provide any information for a ReplayID besides its replay data, so almost none of its attributes will be filled, even after loading it:

r_id = ReplayID(2177560145)
cg.load(r_id)

print(len(r_id.replay_data)) # 20611
print(r_id.map_id) # None
print(r_id.user_id) # None
print(r_id.replay_id) # 2177560145
print(r_id.mods) # None

To find exactly what attributes a replay class provides before and after it’s loaded, see its class’ documentation.

Implicit Loading#

Whenever you pass a Replay to a Circleguard method (cg.snaps(), cg.hits(), cg.judgments(), etc), Circleguard implicitly loads that replay before calculating the relevant statistic. This means that you don’t have to worry about loading a replay before passing it to a circleguard method.

Info Loading#

A Replay has two stages, “unloaded” and “loaded”. A ReplayContainer introduces a third stage called “info loaded”.

When a ReplayContainer is first instantiated, it is unloaded, just like a Replay. This means that it only knows the information you’ve given it - its map id if it’s a Map, or its user id if it’s a User, for instance. It has no knowledge of any Replay objects yet.

To ask the ReplayContainer to create its replay objects, call cg.load_info() on the ReplayContainer. This will make the ReplayContainer “info loaded”, and its replays can then be retrieved:

m = Map(221777, span="1-2")

print(list(m)) # [] since it's not info loaded!

cg.load_info(m)
print(list(m)) # [ReplayMap(...), ReplayMap(...)]

The reason this does not happen by default / automatically is that info loading requires making api calls. This is a (relatively) expensive operation and so is deferred until explicitly requested.

An important distinction is that when a ReplayContainer is info loaded, its Replay objects are not loaded. A ReplayContainer only has loaded Replay objects when it is fully loaded:

m = Map(221777, span="1-2")

cg.load_info(m)
for replay in m:
    print(replay.loaded) # False
    # because the replay is unloaded, we can't access
    # very many of its attributes:
    print(replay.replay_id) # None

cg.load(m):
for replay in m:
    print(replay.loaded) # True
    # but we can now
    print(replay.replay_id) # some number

When you call cg.load() on a completely unloaded ReplayContainer (that is, not even info loaded), it info loads that ReplayContainer for you before loading it. So the following are equivalent:

# good
m = Map(221777, span="1-2")
cg.load(m)

# bad
m = Map(221777, span="1-2")
cg.info_load(m)
cg.load(m)

Creating Loaded Replays or ReplayContainers#

Creating a Replay and then loading it immediately afterwards is a common operation. We provide convenience methods cg.ReplayMap(), cg.ReplayID(), cg.ReplayPath(), and cg.ReplayString() to create a loaded Replay:

r = cg.ReplayMap(221777, 4196808)
print(r.loaded) # True
# similarly for other replays
r2 = cg.ReplayID(2177560145)
r3 = cg.ReplayPath("/path/to/your/osr.osr")

Similarly, it is common to info-load a ReplayContainer immediately after creating it. We provide analogous convenience methods cg.Map(), cg.User(), cg.MapUser(), and cg.ReplayDir() to create an info-loaded ReplayContainer:

m = cg.Map(221777, "1-50")
print(len(m)) # 50
# similarly for other replay containers
u = cg.User(124493, "1-2")
mu = cg.MapUser(124493, 129891)

Each of these methods takes the exact same arguments as instantiating the relevant Replay or ReplayContainer normally.

If you would like to create a fully loaded ReplayContainer, not just an info-loaded one, pass load=True:

m = cg.Map(221777, "1-2", load=True)
print(m[0].loaded) # True

Checking State#

You can check whether a Replay or ReplayContainer is unloaded, loaded, or info loaded by checking the loaded and/or info_loaded attributes:

cg = Circleguard("key")

m = Map(221777, span="1")
print(m.info_loaded, m.loaded) # False, False
cg.load_info(m)
print(m.info_loaded, m.loaded) # True, False
cg.load(m)
print(m.info_loaded, m.loaded) # True, True

r = ReplayMap(221777, 2757689)
print(r.loaded) # False
cg.load(r)
print(r.loaded) # True