Skip to content

Commit ee53bf9

Browse files
Berserker66FlySniper
authored andcommitted
Factorio: skip a bunch of file IO (ArchipelagoMW#2444)
In a lot of cases, Factorio would write data to file first, then attach that file into zip. It now directly attaches the data to the zip and encapsulation was used to allow earlier GC in places (rendered templates especially).
1 parent a17dee5 commit ee53bf9

File tree

2 files changed

+34
-47
lines changed

2 files changed

+34
-47
lines changed

worlds/factorio/Mod.py

+34-33
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import shutil
66
import threading
77
import zipfile
8-
from typing import Optional, TYPE_CHECKING
8+
from typing import Optional, TYPE_CHECKING, Any, List, Callable, Tuple
99

1010
import jinja2
1111

@@ -24,6 +24,7 @@
2424
data_final_template: Optional[jinja2.Template] = None
2525
locale_template: Optional[jinja2.Template] = None
2626
control_template: Optional[jinja2.Template] = None
27+
settings_template: Optional[jinja2.Template] = None
2728

2829
template_load_lock = threading.Lock()
2930

@@ -62,15 +63,24 @@
6263
class FactorioModFile(worlds.Files.APContainer):
6364
game = "Factorio"
6465
compression_method = zipfile.ZIP_DEFLATED # Factorio can't load LZMA archives
66+
writing_tasks: List[Callable[[], Tuple[str, str]]]
67+
68+
def __init__(self, *args: Any, **kwargs: Any):
69+
super().__init__(*args, **kwargs)
70+
self.writing_tasks = []
6571

6672
def write_contents(self, opened_zipfile: zipfile.ZipFile):
6773
# directory containing Factorio mod has to come first, or Factorio won't recognize this file as a mod.
6874
mod_dir = self.path[:-4] # cut off .zip
6975
for root, dirs, files in os.walk(mod_dir):
7076
for file in files:
71-
opened_zipfile.write(os.path.join(root, file),
72-
os.path.relpath(os.path.join(root, file),
77+
filename = os.path.join(root, file)
78+
opened_zipfile.write(filename,
79+
os.path.relpath(filename,
7380
os.path.join(mod_dir, '..')))
81+
for task in self.writing_tasks:
82+
target, content = task()
83+
opened_zipfile.writestr(target, content)
7484
# now we can add extras.
7585
super(FactorioModFile, self).write_contents(opened_zipfile)
7686

@@ -98,6 +108,7 @@ def load_template(name: str):
98108
locations = [(location, location.item)
99109
for location in world.science_locations]
100110
mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}"
111+
versioned_mod_name = mod_name + "_" + Utils.__version__
101112

102113
random = multiworld.per_slot_randoms[player]
103114

@@ -153,48 +164,38 @@ def flop_random(low, high, base=None):
153164
template_data["free_sample_blacklist"].update({item: 1 for item in multiworld.free_sample_blacklist[player].value})
154165
template_data["free_sample_blacklist"].update({item: 0 for item in multiworld.free_sample_whitelist[player].value})
155166

156-
control_code = control_template.render(**template_data)
157-
data_template_code = data_template.render(**template_data)
158-
data_final_fixes_code = data_final_template.render(**template_data)
159-
settings_code = settings_template.render(**template_data)
167+
mod_dir = os.path.join(output_directory, versioned_mod_name)
160168

161-
mod_dir = os.path.join(output_directory, mod_name + "_" + Utils.__version__)
162-
en_locale_dir = os.path.join(mod_dir, "locale", "en")
163-
os.makedirs(en_locale_dir, exist_ok=True)
169+
zf_path = os.path.join(mod_dir + ".zip")
170+
mod = FactorioModFile(zf_path, player=player, player_name=multiworld.player_name[player])
164171

165172
if world.zip_path:
166-
# Maybe investigate read from zip, write to zip, without temp file?
167173
with zipfile.ZipFile(world.zip_path) as zf:
168174
for file in zf.infolist():
169175
if not file.is_dir() and "/data/mod/" in file.filename:
170176
path_part = Utils.get_text_after(file.filename, "/data/mod/")
171-
target = os.path.join(mod_dir, path_part)
172-
os.makedirs(os.path.split(target)[0], exist_ok=True)
173-
174-
with open(target, "wb") as f:
175-
f.write(zf.read(file))
177+
mod.writing_tasks.append(lambda arcpath=versioned_mod_name+"/"+path_part, content=zf.read(file):
178+
(arcpath, content))
176179
else:
177180
shutil.copytree(os.path.join(os.path.dirname(__file__), "data", "mod"), mod_dir, dirs_exist_ok=True)
178181

179-
with open(os.path.join(mod_dir, "data.lua"), "wt") as f:
180-
f.write(data_template_code)
181-
with open(os.path.join(mod_dir, "data-final-fixes.lua"), "wt") as f:
182-
f.write(data_final_fixes_code)
183-
with open(os.path.join(mod_dir, "control.lua"), "wt") as f:
184-
f.write(control_code)
185-
with open(os.path.join(mod_dir, "settings.lua"), "wt") as f:
186-
f.write(settings_code)
187-
locale_content = locale_template.render(**template_data)
188-
with open(os.path.join(en_locale_dir, "locale.cfg"), "wt") as f:
189-
f.write(locale_content)
182+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/data.lua",
183+
data_template.render(**template_data)))
184+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/data-final-fixes.lua",
185+
data_final_template.render(**template_data)))
186+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/control.lua",
187+
control_template.render(**template_data)))
188+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/settings.lua",
189+
settings_template.render(**template_data)))
190+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/locale/en/locale.cfg",
191+
locale_template.render(**template_data)))
192+
190193
info = base_info.copy()
191194
info["name"] = mod_name
192-
with open(os.path.join(mod_dir, "info.json"), "wt") as f:
193-
json.dump(info, f, indent=4)
195+
mod.writing_tasks.append(lambda: (versioned_mod_name + "/info.json",
196+
json.dumps(info, indent=4)))
194197

195-
# zip the result
196-
zf_path = os.path.join(mod_dir + ".zip")
197-
mod = FactorioModFile(zf_path, player=player, player_name=multiworld.player_name[player])
198+
# write the mod file
198199
mod.write()
199-
200+
# clean up
200201
shutil.rmtree(mod_dir)

worlds/factorio/data/mod/info.json

-14
This file was deleted.

0 commit comments

Comments
 (0)